PhysicsEditor template for the Citrus Engine

Some months ago, I bought two softwares from code ‘n’ web : TexturePacker to create SpriteSheet and PhysicsEditor to create easily box2d object from png file.

Today, I’m happy to share my first extension / template for PhysicsEditor’s software : I’ve created a template to export physics object for the Citrus Engine.

This is a quick usage example
.
Click on the physical object and press tab. The console is now open. Then write : set Box2D visible false and you will see its picture. It matches really well to the physical object!

When I worked on Kinessia, I was sad that I couldn’t have physical object that I desired e.g. the catapulte which was a simple rectangle.
To create complex box2d physical object you need to define vertices which are quite complex, and determine convex / concave polygons. Actually PhysicsEditor do that. Thanks to its other templates (there is already a template for AS3 box2d but it is not based on the alchemy version), I have been able to create my template.

It is really easy to use an object created from my PhysicsEditor’s template in your code. In your State class just do :

1
2
var pe:PhysicsEditorObjects = new PhysicsEditorObjects("Pe", {x:200, peObject:"muffin", view:"muffin.png", registration:"topLeft"});
add(pe);

Be careful, the registration point is topLeft instead of center!

To use my template, download the PhysicsEditor and install it. You can use the free version. Then on Mac OS X right click on the application and go “inside”. Finally paste my template in this folder : Contents/Resources/exporters. I think it isn’t too different on Windows.

The software looks like this :

This is my exporter.xml template file, you may want to add some options (take a look on the other templates) :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<exporter>
 
    <!-- identifier of the exporter -->
	<name>box2d-alchemy</name>
 
	<!-- name for the dropbox -->
    <displayName>CitrusEngine ActionScript (FLASH)</displayName>
 
	<!-- description of the exporter -->
    <description>CitrusEngine ActionScript for Flash</description>
 
	<!-- exporter version -->
	<version>1.0</version>
 
	<!-- direction of the y axis: up / down -->
    <yAxisDirection>down</yAxisDirection>
 
	<!-- physics engine to use: box2d, chipmunk -->
	<physicsEngine>box2d</physicsEngine>
 
	<!-- name of the template file -->
    <template>citrusengine.as</template>
 
	<!-- file exension for the data file -->
	<fileExtension>as</fileExtension>
 
	<!-- anchor point settings -->
	<anchorPoint>
	    <!-- are anchor points supported ? yes/no -->
		<enabled>no</enabled>
 
		<!-- relative position of the default anchor point -->
		<relX>0.0</relX>		
		<relY>0.0</relY>
	</anchorPoint>
 
    <origin>
        <!--
        origin (0/0) point of the polygons 
        can be set to "anchorPoint" or "fixed"
        Use 
		<relX>0.5</relX>		
		<relY>0.5</relY>
		to specify a fixed origin.
		Default is 0.5/0.5
        -->
        <type>fixed</type>
		<relX>0.0</relX>		
        <relY>1.0</relY>
    </origin>
 
	<!-- custom global parameters -->
	<global>
	</global>
 
	<!-- custom body parameters -->
	<body>
	</body>
 
	<!-- custom fixture parameters -->
	<fixture>
		<parameter>
			<name>density</name>
			<displayName>Density</displayName>
			<description>Density of the shape. Used for calculating the mass.</description>
			<shortDescription>Density of the shape. Used for calculating the mass.</shortDescription>
			<type>float</type>
			<default>1</default>
		</parameter>
		<parameter>
			<name>friction</name>
			<description>Fricion.</description>
			<shortDescription>Friction.</shortDescription>
			<displayName>Friction</displayName>
			<type>float</type>
			<default>0.6</default>
		</parameter>
		<parameter>
			<name>restitution</name>
			<displayName>Restitution</displayName>
			<description>Restitution defines how much a shape bounces.</description>
			<shortDescription>Density of the shape. Used for calculating the mass.</shortDescription>
			<type>float</type>
			<default>0.3</default>
		</parameter>
    </fixture>
</exporter>

The ActionScript template :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package {
 
    import Box2DAS.Collision.Shapes.b2PolygonShape;
	import Box2DAS.Common.V2;
 
	import com.citrusengine.objects.PhysicsObject;
 
	/**
	 * @author Aymeric
	 * <p>This is a class created by the software http://www.physicseditor.de/</p>
	 * <p>Just select the CitrusEngine template, upload your png picture, set polygons and export.</p>
	 * <p>Be careful, the registration point is topLeft !</p>
	 * @param peObject : the name of the png file
	 */
    public class PhysicsEditorObjects extends PhysicsObject {
 
		[Inspectable(defaultValue="")]
		public var peObject:String = "";
 
		private var _tab:Array;
 
		public function PhysicsEditorObjects(name:String, params:Object = null) {
 
			super(name, params);
		}
 
		override public function destroy():void {
 
			super.destroy();
		}
 
		override public function update(timeDelta:Number):void {
 
			super.update(timeDelta);
		}
 
		override protected function defineFixture():void {
 
			super.defineFixture();
 
			_createVertices();
 
			_fixtureDef.density = _getDensity();
			_fixtureDef.friction = _getFriction();
			_fixtureDef.restitution = _getRestitution();
 
			for (var i:uint = 0; i < _tab.length; ++i) {
				var polygonShape:b2PolygonShape = new b2PolygonShape();
				polygonShape.Set(_tab[i]);
				_fixtureDef.shape = polygonShape;
 
				body.CreateFixture(_fixtureDef);
			}
		}
 
        protected function _createVertices():void {
 
			_tab = [];
			var vertices:Vector.<V2> = new Vector.<V2>();
 
			switch (peObject) {
				{% for body in bodies %}
				case "{{body.name}}":
					{% for fixture in body.fixtures %}{% for polygon in fixture.polygons %}						
			        {% for point in polygon %}vertices.push(new V2({{point.x}}/_box2D.scale, {{point.y}}/_box2D.scale));
					{% endfor %}
					_tab.push(vertices);{% if not forloop.last %}
					vertices = new Vector.<V2>();{% endif %}
					{% endfor %}{% endfor %}
					break;
			{% endfor %}
			}
		}
 
		protected function _getDensity():Number {
 
			switch (peObject) {
				{% for body in bodies %}
				case "{{body.name}}":
					{% for fixture in body.fixtures %}return {{fixture.density}};{% endfor %}
					break;
			{% endfor %}
			}
 
			return 1;
		}
 
		protected function _getFriction():Number {
 
			switch (peObject) {
				{% for body in bodies %}
				case "{{body.name}}":
					{% for fixture in body.fixtures %}return {{fixture.friction}};{% endfor %}
					break;
			{% endfor %}
			}
 
			return 0.6;
		}
 
		protected function _getRestitution():Number {
 
			switch (peObject) {
				{% for body in bodies %}
				case "{{body.name}}":
					{% for fixture in body.fixtures %}return {{fixture.restitution}};{% endfor %}
					break;
			{% endfor %}
			}
 
			return 0.3;
		}
	}
}

And finally the AS3 export file which contains a muffin and a icecream :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
package {
 
    import Box2DAS.Collision.Shapes.b2PolygonShape;
	import Box2DAS.Common.V2;
 
	import com.citrusengine.objects.PhysicsObject;
 
	/**
	 * @author Aymeric
	 * <p>This is a class created by the software http://www.physicseditor.de/</p>
	 * <p>Just select the CitrusEngine template, upload your png picture, set polygons and export.</p>
	 * <p>Be careful, the registration point is topLeft !</p>
	 * @param peObject : the name of the png file
	 */
    public class PhysicsEditorObjects extends PhysicsObject {
 
		[Inspectable(defaultValue="")]
		public var peObject:String = "";
 
		private var _tab:Array;
 
		public function PhysicsEditorObjects(name:String, params:Object = null) {
 
			super(name, params);
		}
 
		override public function destroy():void {
 
			super.destroy();
		}
 
		override public function update(timeDelta:Number):void {
 
			super.update(timeDelta);
		}
 
		override protected function defineFixture():void {
 
			super.defineFixture();
 
			_createVertices();
 
			_fixtureDef.density = _getDensity();
			_fixtureDef.friction = _getFriction();
			_fixtureDef.restitution = _getRestitution();
 
			for (var i:uint = 0; i < _tab.length; ++i) {
				var polygonShape:b2PolygonShape = new b2PolygonShape();
				polygonShape.Set(_tab[i]);
				_fixtureDef.shape = polygonShape;
 
				body.CreateFixture(_fixtureDef);
			}
		}
 
        protected function _createVertices():void {
 
			_tab = [];
			var vertices:Vector.<V2> = new Vector.<V2>();
 
			switch (peObject) {
 
				case "icecream":
 
			        vertices.push(new V2(30.5/_box2D.scale, 45.5/_box2D.scale));
					vertices.push(new V2(97.5/_box2D.scale, 246.5/_box2D.scale));
					vertices.push(new V2(93.5/_box2D.scale, 270.5/_box2D.scale));
					vertices.push(new V2(62.5/_box2D.scale, 302.5/_box2D.scale));
					vertices.push(new V2(21.5/_box2D.scale, 126.5/_box2D.scale));
					vertices.push(new V2(16.5/_box2D.scale, 62.5/_box2D.scale));
					vertices.push(new V2(18.5/_box2D.scale, 50.5/_box2D.scale));
 
					_tab.push(vertices);
					vertices = new Vector.<V2>();
 
			        vertices.push(new V2(62.5/_box2D.scale, 302.5/_box2D.scale));
					vertices.push(new V2(93.5/_box2D.scale, 270.5/_box2D.scale));
					vertices.push(new V2(81.5/_box2D.scale, 306.5/_box2D.scale));
					vertices.push(new V2(73.5/_box2D.scale, 312.5/_box2D.scale));
					vertices.push(new V2(65.5/_box2D.scale, 310.5/_box2D.scale));
 
					_tab.push(vertices);
					vertices = new Vector.<V2>();
 
			        vertices.push(new V2(9.5/_box2D.scale, 111.5/_box2D.scale));
					vertices.push(new V2(16.5/_box2D.scale, 62.5/_box2D.scale));
					vertices.push(new V2(21.5/_box2D.scale, 126.5/_box2D.scale));
					vertices.push(new V2(10.5/_box2D.scale, 121.5/_box2D.scale));
 
					_tab.push(vertices);
					vertices = new Vector.<V2>();
 
			        vertices.push(new V2(44.5/_box2D.scale, 32.5/_box2D.scale));
					vertices.push(new V2(135.5/_box2D.scale, 65.5/_box2D.scale));
					vertices.push(new V2(144.5/_box2D.scale, 115.5/_box2D.scale));
					vertices.push(new V2(135.5/_box2D.scale, 120.5/_box2D.scale));
					vertices.push(new V2(30.5/_box2D.scale, 45.5/_box2D.scale));
					vertices.push(new V2(28.5/_box2D.scale, 37.5/_box2D.scale));
 
					_tab.push(vertices);
					vertices = new Vector.<V2>();
 
			        vertices.push(new V2(0.5/_box2D.scale, 81.5/_box2D.scale));
					vertices.push(new V2(6.5/_box2D.scale, 69.5/_box2D.scale));
					vertices.push(new V2(16.5/_box2D.scale, 62.5/_box2D.scale));
					vertices.push(new V2(9.5/_box2D.scale, 111.5/_box2D.scale));
					vertices.push(new V2(-0.5/_box2D.scale, 97.5/_box2D.scale));
 
					_tab.push(vertices);
					vertices = new Vector.<V2>();
 
			        vertices.push(new V2(82.5/_box2D.scale, 3.5/_box2D.scale));
					vertices.push(new V2(127.5/_box2D.scale, 42.5/_box2D.scale));
					vertices.push(new V2(135.5/_box2D.scale, 65.5/_box2D.scale));
					vertices.push(new V2(44.5/_box2D.scale, 32.5/_box2D.scale));
					vertices.push(new V2(51.5/_box2D.scale, 14.5/_box2D.scale));
					vertices.push(new V2(70.5/_box2D.scale, -0.5/_box2D.scale));
 
					_tab.push(vertices);
					vertices = new Vector.<V2>();
 
			        vertices.push(new V2(151.5/_box2D.scale, 86.5/_box2D.scale));
					vertices.push(new V2(144.5/_box2D.scale, 115.5/_box2D.scale));
					vertices.push(new V2(135.5/_box2D.scale, 65.5/_box2D.scale));
					vertices.push(new V2(146.5/_box2D.scale, 73.5/_box2D.scale));
 
					_tab.push(vertices);
					vertices = new Vector.<V2>();
 
			        vertices.push(new V2(129.5/_box2D.scale, 129.5/_box2D.scale));
					vertices.push(new V2(30.5/_box2D.scale, 45.5/_box2D.scale));
					vertices.push(new V2(135.5/_box2D.scale, 120.5/_box2D.scale));
 
					_tab.push(vertices);
					vertices = new Vector.<V2>();
 
			        vertices.push(new V2(101.5/_box2D.scale, 241.5/_box2D.scale));
					vertices.push(new V2(97.5/_box2D.scale, 246.5/_box2D.scale));
					vertices.push(new V2(30.5/_box2D.scale, 45.5/_box2D.scale));
					vertices.push(new V2(129.5/_box2D.scale, 129.5/_box2D.scale));
 
					_tab.push(vertices);
 
					break;
 
				case "muffin":
 
			        vertices.push(new V2(195.5/_box2D.scale, 166.5/_box2D.scale));
					vertices.push(new V2(188.5/_box2D.scale, 128.5/_box2D.scale));
					vertices.push(new V2(199.5/_box2D.scale, 158.5/_box2D.scale));
					vertices.push(new V2(199.5/_box2D.scale, 164.5/_box2D.scale));
 
					_tab.push(vertices);
					vertices = new Vector.<V2>();
 
			        vertices.push(new V2(89.5/_box2D.scale, -0.5/_box2D.scale));
					vertices.push(new V2(95.5/_box2D.scale, 8.5/_box2D.scale));
					vertices.push(new V2(92.5/_box2D.scale, 14.5/_box2D.scale));
					vertices.push(new V2(82.5/_box2D.scale, 3.5/_box2D.scale));
					vertices.push(new V2(85.5/_box2D.scale, -0.5/_box2D.scale));
 
					_tab.push(vertices);
					vertices = new Vector.<V2>();
 
			        vertices.push(new V2(2.5/_box2D.scale, 149.5/_box2D.scale));
					vertices.push(new V2(23.5/_box2D.scale, 117.5/_box2D.scale));
					vertices.push(new V2(9.5/_box2D.scale, 175.5/_box2D.scale));
					vertices.push(new V2(-0.5/_box2D.scale, 161.5/_box2D.scale));
 
					_tab.push(vertices);
					vertices = new Vector.<V2>();
 
			        vertices.push(new V2(120.5/_box2D.scale, 71.5/_box2D.scale));
					vertices.push(new V2(120.5/_box2D.scale, 91.5/_box2D.scale));
					vertices.push(new V2(24.5/_box2D.scale, 223.5/_box2D.scale));
					vertices.push(new V2(18.5/_box2D.scale, 199.5/_box2D.scale));
					vertices.push(new V2(74.5/_box2D.scale, 92.5/_box2D.scale));
					vertices.push(new V2(96.5/_box2D.scale, 56.5/_box2D.scale));
					vertices.push(new V2(101.5/_box2D.scale, 56.5/_box2D.scale));
					vertices.push(new V2(113.5/_box2D.scale, 61.5/_box2D.scale));
 
					_tab.push(vertices);
					vertices = new Vector.<V2>();
 
			        vertices.push(new V2(26.5/_box2D.scale, 251.5/_box2D.scale));
					vertices.push(new V2(24.5/_box2D.scale, 223.5/_box2D.scale));
					vertices.push(new V2(180.5/_box2D.scale, 202.5/_box2D.scale));
					vertices.push(new V2(174.5/_box2D.scale, 226.5/_box2D.scale));
					vertices.push(new V2(122.5/_box2D.scale, 283.5/_box2D.scale));
					vertices.push(new V2(72.5/_box2D.scale, 280.5/_box2D.scale));
					vertices.push(new V2(48.5/_box2D.scale, 273.5/_box2D.scale));
					vertices.push(new V2(32.5/_box2D.scale, 264.5/_box2D.scale));
 
					_tab.push(vertices);
					vertices = new Vector.<V2>();
 
			        vertices.push(new V2(156.5/_box2D.scale, 273.5/_box2D.scale));
					vertices.push(new V2(122.5/_box2D.scale, 283.5/_box2D.scale));
					vertices.push(new V2(174.5/_box2D.scale, 226.5/_box2D.scale));
					vertices.push(new V2(172.5/_box2D.scale, 254.5/_box2D.scale));
					vertices.push(new V2(168.5/_box2D.scale, 264.5/_box2D.scale));
 
					_tab.push(vertices);
					vertices = new Vector.<V2>();
 
			        vertices.push(new V2(76.5/_box2D.scale, 66.5/_box2D.scale));
					vertices.push(new V2(82.5/_box2D.scale, 60.5/_box2D.scale));
					vertices.push(new V2(96.5/_box2D.scale, 56.5/_box2D.scale));
					vertices.push(new V2(74.5/_box2D.scale, 92.5/_box2D.scale));
					vertices.push(new V2(72.5/_box2D.scale, 76.5/_box2D.scale));
 
					_tab.push(vertices);
					vertices = new Vector.<V2>();
 
			        vertices.push(new V2(101.5/_box2D.scale, 34.5/_box2D.scale));
					vertices.push(new V2(97.5/_box2D.scale, 32.5/_box2D.scale));
					vertices.push(new V2(92.5/_box2D.scale, 14.5/_box2D.scale));
					vertices.push(new V2(95.5/_box2D.scale, 8.5/_box2D.scale));
 
					_tab.push(vertices);
					vertices = new Vector.<V2>();
 
			        vertices.push(new V2(23.5/_box2D.scale, 117.5/_box2D.scale));
					vertices.push(new V2(34.5/_box2D.scale, 107.5/_box2D.scale));
					vertices.push(new V2(54.5/_box2D.scale, 97.5/_box2D.scale));
					vertices.push(new V2(74.5/_box2D.scale, 92.5/_box2D.scale));
					vertices.push(new V2(18.5/_box2D.scale, 199.5/_box2D.scale));
					vertices.push(new V2(9.5/_box2D.scale, 175.5/_box2D.scale));
 
					_tab.push(vertices);
					vertices = new Vector.<V2>();
 
			        vertices.push(new V2(180.5/_box2D.scale, 202.5/_box2D.scale));
					vertices.push(new V2(24.5/_box2D.scale, 223.5/_box2D.scale));
					vertices.push(new V2(120.5/_box2D.scale, 91.5/_box2D.scale));
					vertices.push(new V2(154.5/_box2D.scale, 100.5/_box2D.scale));
					vertices.push(new V2(175.5/_box2D.scale, 114.5/_box2D.scale));
					vertices.push(new V2(188.5/_box2D.scale, 128.5/_box2D.scale));
					vertices.push(new V2(195.5/_box2D.scale, 166.5/_box2D.scale));
 
					_tab.push(vertices);
					vertices = new Vector.<V2>();
 
			        vertices.push(new V2(101.5/_box2D.scale, 34.5/_box2D.scale));
					vertices.push(new V2(101.5/_box2D.scale, 56.5/_box2D.scale));
					vertices.push(new V2(96.5/_box2D.scale, 56.5/_box2D.scale));
					vertices.push(new V2(97.5/_box2D.scale, 32.5/_box2D.scale));
 
					_tab.push(vertices);
 
					break;
 
			}
		}
 
		protected function _getDensity():Number {
 
			switch (peObject) {
 
				case "icecream":
					return 1;
					break;
 
				case "muffin":
					return 1;
					break;
 
			}
 
			return 1;
		}
 
		protected function _getFriction():Number {
 
			switch (peObject) {
 
				case "icecream":
					return 0.6;
					break;
 
				case "muffin":
					return 0.6;
					break;
 
			}
 
			return 0.6;
		}
 
		protected function _getRestitution():Number {
 
			switch (peObject) {
 
				case "icecream":
					return 0.3;
					break;
 
				case "muffin":
					return 0.3;
					break;
 
			}
 
			return 0.3;
		}
	}
}

There are lots of vertices stuff! Don’t forget, don’t use many vertices if it isn’t really necessary. In my case, I’ve put lots of vertices for the example, but it may be quite good with 11 vertices instead of 40. You have to think about optimization, complex shape are heavy to manage for box2d.

Finally, I would like to thanks Andreas Löw for helping me to start this template and his support on TexturePacker mac version which works really great now with SWF file!
Oh, and I forgot to mention that the CitrusEngine is now totaly free!

PhysicsEditor template zip file.
PhysicsEditor example zip file.

One thing on my todo list is achieved now 🙂
However you may have noticed the Inspectable metadata tag, I haven’t finish yet with this cool engine 😉

4 thoughts on “PhysicsEditor template for the Citrus Engine

  1. Hello,

    I was wondering if you could tell me more about the syntax you used for your for…in loops. I’ve never seen it that way before. How does it work? What does it do? This is the code I mean:

    switch (peObject) {
    {% for body in bodies %}
    case “{{body.name}}”:
    {% for fixture in body.fixtures %}{% for polygon in fixture.polygons %}
    {% for point in polygon %}vertices.push(new V2({{point.x}}/_box2D.scale, {{point.y}}/_box2D.scale));
    {% endfor %}
    _tab.push(vertices);{% if not forloop.last %}
    vertices = new Vector.();{% endif %}
    {% endfor %}{% endfor %}
    break;
    {% endfor %}
    }

  2. Hi Owen,

    The syntax come from the software. I don’t know if it is related to a specific programming language. Before I started my template, I had looked the original exporter and saw this syntax ; so I tried and it works!

    {% for fixture in body.fixtures %}{% endfor %} this for…in loops means that for each box2d fixtures in the body its generate the code between the braces. Same with {% if %}{% endif %}.
    {% if not forloop.last %} means if it not the last element of your loop. It was luck for this one, I have seen {% if not forloop.first %} in the exporter’s example, so I tried its opposite.

    {{body.name}} comes from the software too, it picks up the png file name, {{point.x}} the vertice x point, etc…

    It was a bit hard at first, but finally it works like a charm 😉

Leave a Reply

Your email address will not be published. Required fields are marked *