Emanuele Feronato italian geek and PROgrammer 2012-02-06T14:28:18Z http://www.emanueleferonato.com/feed/atom/ WordPress Emanuele Feronato <![CDATA[SamePhysics Flash game source code released]]> http://www.emanueleferonato.com/?p=5138 2012-02-06T14:28:18Z 2012-02-06T14:28:18Z More than three years ago I released my first Box2D game called SamePhysics.

It was an experiment to port the famous tile based game SameGame into a real-world physics environment, and despite it was nothing that a prototype with some bugs – especially with collision management – the game got some success, with about 2.5 million views and two sponsorships by Rotten Tomato Games (now offline) and GirlGames.

Now I am making a sequel of the game, more polished, fun and playable, and it’s time to release the source code of the good old SamePhysics.

You are warned: the code is “as is” and it’s not even the latest Box2D distribution, but I think it could be interesting to learn the basics of Box2D programming from a real world successful example.

Download the source code.

]]>
1
Emanuele Feronato <![CDATA[Making an in-game tutorial with StencylWorks]]> http://www.emanueleferonato.com/?p=5124 2012-01-31T16:44:10Z 2012-01-31T16:44:10Z If you are playing the latest Flash games, you will see the old “how to play” button no longer exists.

People don’t want to read detailed instructions before playing a Flash game, they want to dig into action.

That’s why we have the need to create in-game tutorials, which are easy levels to make players get used to the game design while keeping them in action.

In my upcoming Flash game Kira the Witch, the tutorial is made of signs you can find around the levels. Once you touch a sign, you will be able to “read” it this way:

I am going to show you how to do it in the painless way.

We have two actors: the small sign and the big sign. Small signs are placed in the scene during level design, while the big sign (one for all) will be created on the fly.

Also, the small sign is a sensor so it won’t react to physics and has its own collision group, called “sign”.

The big sign is a doodad, which means it’s a special actor never allowed to collide with anything else.

There is a big difference between a sensor and a doodad. The sensor does not react to physics forces but registers collisions, the doodad does not register collisions.

Now let’s create some behaviors: the first, and the easiest, is called “Big sign information” and does not contain any action, as you can see:

So it may seems useless, but I added to this behavior three not hidden attributes called “Text to display”, “Big sign x position” and “Big sign y position”.

When I customize the behavior of each small sign and add “Big sign information” behavior…

I can set the each sign content, and the x and y coordinates to be shown once Kira “reads” a sign. This way, “Big sign information” behavior acts like a new data type:

Also please notice I am using an asterisk as line break character since using “\n” won’t work at this stage.

Then, I need another behavior called “Write sign text” which will write text inside the big sign, so obvioulsy will be tied to it:

This is also very easy as I am only setting a font and writing some text in the sign. Notice how at this time I need to replace “*” with “\n” to do line breaks.

The difficult task to create the big sign, place it, and write text in it by changing “Text to write” attribute is (and then remove it) made by main player’s behavior called “Kira touches a sign”

I commented it to let you understand how it works.

Basically the key is the communication among different behaviors to get and set attributes on the fly.

And we can say “goodbye” to that old lame “How to play” buttons in a matter of minutes.

]]>
3
Emanuele Feronato <![CDATA[Play Slingy, my latest Flash game, on Hairy Games]]> http://www.emanueleferonato.com/?p=5113 2012-01-27T15:48:30Z 2012-01-27T15:48:07Z Slingy, my latest physics Flash Game sponsored by Hairy Games]]> I want you all to play Slingy, my latest Flash game, sponsored by Hairy Games.

In this physics game you have to beat all 40 levels collecting as much orbs as you can, throwing the ball with a sling. To make your life harder, several kind of blocks to avoid or interact with.

You can find some information about the prototype in these posts:

* Controlling a ball like in Flash Elasticity game tutorial
* Flash Elasticity prototype – AS3 version
* When Elasticity meets Bloons

The physics is powered by Box2D, while I used Flint particle system for the particles.

Talking about advertising, I used MochiMedia for the in-game ads and FlashGameLicense to find a sponsorship.

I recommend Rick from Hairy Games: he provides constructive feedback, helps you to make the game better and the branding process is easy.

I also used both Playtomic and MochiBot to track game statistics.

Play ita, rate it and give me feedback.

I will update you with some interesting new about the game once it’s in the wild for some time.

]]>
7
Emanuele Feronato <![CDATA[The concept behind Stringy Flash game: determine if a Sprite is inside a lasso]]> http://www.emanueleferonato.com/?p=5105 2012-01-25T16:34:42Z 2012-01-25T16:34:42Z Stringy Flash game]]> Do you remember my Flash game called Stringy?

I blogged about it about a month ago and some people asked me some hints about the way used to catch the stars drawing a lasso around it.

There are a lot of mathematical formulas to solve this task, and range from “more than complicate” to “you must be joking”.

I decided to post a prototype of the code I used starting from the post Develop a Flash game like String Avoider – AS3 version – and more!, without any comment, and it will be up to you to guess how I solved the problem in a ridiculously easy way.

This is what you will get:

Catch the circle with a lasso to make it move in a random spot.

And this is the code:

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
package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;
	public class Main extends Sprite {
		private var tailLenght:Number=1;
		private var tailNodes:Number=300;
		private var tailCanvas:Sprite=new Sprite();
		private var areaCanvas:Sprite=new Sprite();
		private var targetCanvas:Sprite=new Sprite();
		private var nodes:Vector.<Point>=new Vector.<Point>();
		private var target:Point;
		public function Main() {
			for (var i:int=0; i<tailNodes; i++) {
				nodes[i]=new Point(mouseX,mouseY);
			}
			addChild(areaCanvas);
			addChild(tailCanvas);
			addChild(targetCanvas);
			placeTarget();
			addEventListener(Event.ENTER_FRAME,update);
		}
		private function placeTarget():void {
			target=new Point(Math.floor(Math.random()*600)+20,Math.floor(Math.random()*440)+20);
			targetCanvas.graphics.clear();
			targetCanvas.graphics.lineStyle(1,0xffffff);
			targetCanvas.graphics.beginFill(0xffffff);
			targetCanvas.graphics.drawCircle(target.x,target.y,5);
			targetCanvas.graphics.endFill();
		}
		private function update(e:Event):void {
			var sampleNodes:Vector.<Point>=new Vector.<Point>();
			tailCanvas.graphics.clear();
			tailCanvas.graphics.lineStyle(3,0x00ff00);
			tailCanvas.graphics.moveTo(mouseX,mouseY);
			areaCanvas.graphics.clear();
			nodes[0]=new Point(mouseX,mouseY);
			sampleNodes[0]=new Point(mouseX,mouseY);
			for (var i:int=1; i<tailNodes-1; i++) {
				var nodeAngle:Number=Math.atan2(nodes[i].y-nodes[i-1].y,nodes[i].x-nodes[i-1].x);
				nodes[i]=new Point(nodes[i-1].x+tailLenght*Math.cos(nodeAngle),nodes[i-1].y+tailLenght*Math.sin(nodeAngle));
				tailCanvas.graphics.lineTo(nodes[i].x,nodes[i].y);
				if (i%10==0 || i==tailNodes-2) {
					sampleNodes.push(nodes[i]);
				}
			}
			for (i=0; i<sampleNodes.length-1; i++) {
				for (var j:int=i+2; j<sampleNodes.length-1; j++) {
					var p:Point=lineIntersection(sampleNodes[j+1],sampleNodes[j],sampleNodes[i+1],sampleNodes[i]);
					if ((p!=null && !isNaN(p.x))) {
						areaCanvas.graphics.beginFill(0xff0000,1);
						areaCanvas.graphics.moveTo(p.x,p.y);
						for (var k:int=i+1; k<=j; k++) {
							areaCanvas.graphics.lineTo(sampleNodes[k].x,sampleNodes[k].y);
						}
						areaCanvas.graphics.endFill();
					}
				}
			}
			if (areaCanvas.hitTestPoint(target.x,target.y,true)) {
				placeTarget();
			}
		}
		private function lineIntersection(p1:Point,p2:Point,p3:Point,p4:Point):Point {
			var x1:Number=p1.x;
			var x2:Number=p2.x;
			var x3:Number=p3.x;
			var x4:Number=p4.x;
			var y1:Number=p1.y;
			var y2:Number=p2.y;
			var y3:Number=p3.y;
			var y4:Number=p4.y;
			var px:Number=(((((x1*y2)-y1*x2)*(x3-x4))-(x1-x2)*((x3*y4)-y3*x4))/(((x1-x2)*(y3-y4))-(y1-y2)*(x3-x4)));
			var py:Number=(((((x1*y2)-y1*x2)*(y3-y4))-(y1-y2)*((x3*y4)-y3*x4))/(((x1-x2)*(y3-y4))-(y1-y2)*(x3-x4)));
			var segment1Len:Number=Math.pow(p1.x-p2.x,2)+Math.pow(p1.y-p2.y,2);
			var segment2Len:Number=Math.pow(p3.x-p4.x,2)+Math.pow(p3.y-p4.y,2);
			if (Math.pow(p1.x-px,2)+Math.pow(p1.y-py,2)>segment1Len) {
				return null;
			}
			if (Math.pow(p2.x-px,2)+Math.pow(p2.y-py,2)>segment1Len) {
				return null;
			}
			if (Math.pow(p3.x-px,2)+Math.pow(p3.y-py,2)>segment2Len) {
				return null;
			}
			if (Math.pow(p4.x-px,2)+Math.pow(p4.y-py,2)>segment2Len) {
				return null;
			}
			return new Point(px,py);
		}
	}
}

No need to download anything, you can replace the code contained in the original prototype with this one.

Did you figure out how I made it possible with no formulas?

]]>
6
Emanuele Feronato <![CDATA[Make the player die after a long fall with StencylWorks]]> http://www.emanueleferonato.com/?p=5089 2012-01-23T10:01:25Z 2012-01-23T10:01:25Z I already showed you how easy is to create a platform game with StencylWorks and even how to include a double jump feature.

Anyway, in some platform games you need to make the player die if falling from high places.

Try to play this prototype:

You can play with ARROW keys, and perform double jumps.

If you fall down the left cliff, you will die (actually, you turn gray for couple of seconds – that’s how I show you would have died).

If you fall down the right cliff, you won’t die.

If you jump from the right cliff and fall down, you will die.

This happens because there’s a maximum falling height. If you’re falling for more than such height, you will die.

To make this kind of behavior, first we need some attributes:

is falling is a Boolean variable to determine whether the player is falling or not

falling start is the y coordinate in pixels of the player when it begins to fall

deadly height is the height, in pixels, of the deadly height. Also notice this attribute is not hidden so it can be set when the behavior is added to the player:

Then, this is the behavior:

As you can see it’s just a matter of checking if player’s y speed is greater than zero (the player is moving down, so it’s falling), and once it hits the ground again, calculating for how long it kept on falling.

]]>
5
Emanuele Feronato <![CDATA[Create REAL explosions with Box2D – adding textures]]> http://www.emanueleferonato.com/?p=5079 2012-01-17T14:46:07Z 2012-01-17T14:45:46Z Ok, you asked it, now you have it. Now the Box2D explosion prototype features bitmap textures and a commented source code.

I have to say a big thank you to Antoan Angelov for his version of the slicing engine as he allowed me to manage textures with no hassle.

So now basically an explosion is a number of cuts at a random angle, all passing for the same point, which is the origin of the explosion. Then, every resulting slice will have a linear velocity according to the distance between the center of the slice mass and the origin of the explosion.

So this is what you’ll get:

Click on a crate to make it explode.

And this is the full, commented source code:

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
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Matrix;
	import flash.display.BitmapData;
	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.*;
	public class Main extends Sprite {
		private var world:b2World=new b2World(new b2Vec2(0,10),true);
		private var enterPointsVec:Vector.<b2Vec2> = new Vector.<b2Vec2>();
		private var numEnterPoints:int=0;
		private var worldScale:Number=30;
		// variables used in the explosion process:
		// the vector of exploding bodies
		private var explodingBodies:Vector.<b2Body>;
		// the number of cuts for every explosion
		private var explosionCuts:Number=5;
		// explosion x and y center
		private var explosionX:Number;
		private var explosionY:Number;
		// explosion radius, useful to determine the velocity of debris
		private var explosionRadius:Number=50;
		public function Main() {
			// calling the debug draw. This is used to show you the bitmaps are correctly applied,
			// and because I did not want to draw the walls :)
			debugDraw();
			// this is the BitmapData representation of my 100x100 pixels crate image
			// check the library to see both the raw image and CrateImage Sprite
			var crateBitmap:BitmapData=new BitmapData(100,100);
			crateBitmap.draw(new CrateImage());
			// adding the four static, undestroyable walls
			addWall(320,480,640,20);
			addWall(320,0,640,20);
			addWall(0,240,20,480);
			addWall(640,240,20,480);
			// this vector stores the clockwise local coordinates of the 100x100 pixels crate
			var crateCoordVector:Vector.<b2Vec2>=new <b2Vec2>[new b2Vec2(-50,-50),new b2Vec2(50,-50),new b2Vec2(50,50),new b2Vec2(-50,50)];
			// then createBody builds the final body and applies the bitmap.
			// the first two arguments are the X and Y position of the center of the crate, in pixels
			createBody(95,420,crateCoordVector,crateBitmap);
			createBody(245,420,crateCoordVector,crateBitmap);
			createBody(395,420,crateCoordVector,crateBitmap);
			createBody(545,420,crateCoordVector,crateBitmap);
			createBody(170,320,crateCoordVector,crateBitmap);
			createBody(320,320,crateCoordVector,crateBitmap);
			createBody(470,320,crateCoordVector,crateBitmap);
			createBody(245,220,crateCoordVector,crateBitmap);
			createBody(395,220,crateCoordVector,crateBitmap);
			createBody(320,120,crateCoordVector,crateBitmap);
			// You can see the reason for creating the enterPointsVec in the coments in the intersection() method.
			enterPointsVec=new Vector.<b2Vec2>(numEnterPoints);
			// listeners
			stage.addEventListener(MouseEvent.MOUSE_DOWN, boom);
			addEventListener(Event.ENTER_FRAME, update);
		}
		// my old friend debugDraw function
		private function debugDraw():void {
			var debugDraw:b2DebugDraw = new b2DebugDraw();
			var debugSprite:Sprite = new Sprite();
			addChild(debugSprite);
			debugDraw.SetSprite(debugSprite);
			debugDraw.SetDrawScale(worldScale);
			debugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
			debugDraw.SetFillAlpha(0.5);
			world.SetDebugDraw(debugDraw);
		}
		// this function returns the body at a X,Y coordinate without using a temp body like the one in
		// the original Box2D distribution. It uses QueryPoint method.
		// returns the body ad X,Y coordinate or null
		private function GetBodyAtXY(coordinate:b2Vec2):b2Body {
			var touchedBody:b2Body=null;
			world.QueryPoint(GetBodyCallback,coordinate);
			function GetBodyCallback(fixture:b2Fixture):Boolean {
				var shape:b2Shape=fixture.GetShape();
				var inside:Boolean=shape.TestPoint(fixture.GetBody().GetTransform(),coordinate);
				if (inside) {
					touchedBody=fixture.GetBody();
					return false;
				}
				return true;
			}
			return touchedBody;
		}
		// simple function to add a static wall
		private function addWall(pX:Number,pY:Number,w:Number,h:Number):void {
			var wallShape:b2PolygonShape = new b2PolygonShape();
			wallShape.SetAsBox(w/worldScale/2,h/worldScale/2);
			var wallFixture:b2FixtureDef = new b2FixtureDef();
			wallFixture.density=0;
			wallFixture.friction=1;
			wallFixture.restitution=0.5;
			wallFixture.shape=wallShape;
			var wallBodyDef:b2BodyDef = new b2BodyDef();
			wallBodyDef.position.Set(pX/worldScale,pY/worldScale);
			var wall:b2Body=world.CreateBody(wallBodyDef);
			wall.CreateFixture(wallFixture);
			numEnterPoints++;
		}
		// function to create and texture a dynamic body
		private function createBody(xPos:Number, yPos:Number, verticesArr:Vector.<b2Vec2>, texture:BitmapData) {
			// I need this temp vector to convert pixels coordinates to Box2D meters coordinates
			var vec:Vector.<b2Vec2>=new Vector.<b2Vec2>();
			for (var i:Number=0; i<verticesArr.length; i++) {
				vec.push(new b2Vec2(verticesArr[i].x/worldScale,verticesArr[i].y/worldScale));
			}
			var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.type=b2Body.b2_dynamicBody;
			var boxDef:b2PolygonShape = new b2PolygonShape();
			boxDef.SetAsVector(vec);
			bodyDef.position.Set(xPos/worldScale, yPos/worldScale);
			// custom userData used to map the texture
			bodyDef.userData=new userData(numEnterPoints,vec,texture);
			addChild(bodyDef.userData);
			var fixtureDef:b2FixtureDef = new b2FixtureDef();
			fixtureDef.density=1;
			fixtureDef.friction=0.2;
			fixtureDef.restitution=0.5;
			fixtureDef.shape=boxDef;
			var tempBox:b2Body=world.CreateBody(bodyDef);
			tempBox.CreateFixture(fixtureDef);
			numEnterPoints++;
		}
		// function to create an explosion
		private function boom(e:MouseEvent) {
			var cutAngle:Number;
			explosionX=mouseX;
			explosionY=mouseY;
			// I am looking for a body under my mouse
			var clickedBody:b2Body=GetBodyAtXY(new b2Vec2(explosionX/worldScale,explosionY/worldScale));
			if (clickedBody!=null) {
				// storing the exploding bodies in a vector. I need to do it since I do not want other bodies
				// to be affected by the raycast and explode
				explodingBodies=new Vector.<b2Body>();
				explodingBodies.push(clickedBody);
				// the explosion begins!
				for (var i:Number=1; i<=explosionCuts; i++) {
					// choosing a random angle
					cutAngle=Math.random()*Math.PI*2;
					// creating the two points to be used for the raycast, according to the random angle and mouse position
					// also notice how I need to add a little offset (i/10) or Box2D will crash. Probably it's not able to 
					// determine raycast on objects whose area is very very close to zero (or zero)
					var p1:b2Vec2=new b2Vec2((explosionX+i/10-2000*Math.cos(cutAngle))/worldScale,(explosionY-2000*Math.sin(cutAngle))/worldScale);
					var p2:b2Vec2=new b2Vec2((explosionX+2000*Math.cos(cutAngle))/worldScale,(explosionY+2000*Math.sin(cutAngle))/worldScale);
					world.RayCast(intersection, p1, p2);
					world.RayCast(intersection, p2, p1);
					enterPointsVec=new Vector.<b2Vec2>(numEnterPoints);
				}
			}
		}
		// update function to simulate and render the world
		public function update(e:Event):void {
			world.Step(1/30, 10, 10);
			world.ClearForces();
			var spr:Sprite;
			for (var b:b2Body = world.GetBodyList(); b; b = b.GetNext()) {
				spr=b.GetUserData();
				if (spr) {
					spr.x=b.GetPosition().x*worldScale;
					spr.y=b.GetPosition().y*worldScale;
					spr.rotation=b.GetAngle()*180/Math.PI;
				}
			}
			world.DrawDebugData();
		}
		private function intersection(fixture:b2Fixture, point:b2Vec2, normal:b2Vec2, fraction:Number):Number {
			if (explodingBodies.indexOf(fixture.GetBody())!=-1) {
				var spr:Sprite=fixture.GetBody().GetUserData();
				// Throughout this whole code I use only one global vector, and that is enterPointsVec. Why do I need it you ask? 
				// Well, the problem is that the world.RayCast() method calls this function only when it sees that a given line gets into the body - it doesnt see when the line gets out of it.
				// I must have 2 intersection points with a body so that it can be sliced, thats why I use world.RayCast() again, but this time from B to A - that way the point, at which BA enters the body is the point at which AB leaves it!
				// For that reason, I use a vector enterPointsVec, where I store the points, at which AB enters the body. And later on, if I see that BA enters a body, which has been entered already by AB, I fire the splitObj() function!
				// I need a unique ID for each body, in order to know where its corresponding enter point is - I store that id in the userData of each body.
				if (spr is userData) {
					var userD:userData=spr as userData;
					if (enterPointsVec[userD.id]) {
						// If this body has already had an intersection point, then it now has two intersection points, thus it must be split in two - thats where the splitObj() method comes in.
						splitObj(fixture.GetBody(), enterPointsVec[userD.id], point.Copy());
					}
					else {
						enterPointsVec[userD.id]=point;
					}
				}
			}
			return 1;
		}
		// function to get the area of a shape. I will remove tiny shape to increase performance
		private function getArea(vs:Vector.<b2Vec2>, count:uint):Number {
			var area:Number=0.0;
			var p1X:Number=0.0;
			var p1Y:Number=0.0;
			var inv3:Number=1.0/3.0;
			for (var i:int = 0; i < count; ++i) {
				var p2:b2Vec2=vs[i];
				var p3:b2Vec2=i+1<count?vs[int(i+1)]:vs[0];
				var e1X:Number=p2.x-p1X;
				var e1Y:Number=p2.y-p1Y;
				var e2X:Number=p3.x-p1X;
				var e2Y:Number=p3.y-p1Y;
				var D:Number = (e1X * e2Y - e1Y * e2X);
				var triangleArea:Number=0.5*D;
				area+=triangleArea;
			}
			return area;
		}
 
		private function splitObj(sliceBody:b2Body, A:b2Vec2, B:b2Vec2):void {
			var origFixture:b2Fixture=sliceBody.GetFixtureList();
			var poly:b2PolygonShape=origFixture.GetShape() as b2PolygonShape;
			var verticesVec:Vector.<b2Vec2>=poly.GetVertices(),numVertices:int=poly.GetVertexCount();
			var shape1Vertices:Vector.<b2Vec2> = new Vector.<b2Vec2>(), shape2Vertices:Vector.<b2Vec2> = new Vector.<b2Vec2>();
			var origUserData:userData=sliceBody.GetUserData(),origUserDataId:int=origUserData.id,d:Number;
			var polyShape:b2PolygonShape=new b2PolygonShape();
			var body:b2Body;
			// First, I destroy the original body and remove its Sprite representation from the childlist.
			world.DestroyBody(sliceBody);
			removeChild(origUserData);
			// The world.RayCast() method returns points in world coordinates, so I use the b2Body.GetLocalPoint() to convert them to local coordinates.;
			A=sliceBody.GetLocalPoint(A);
			B=sliceBody.GetLocalPoint(B);
			// I use shape1Vertices and shape2Vertices to store the vertices of the two new shapes that are about to be created. 
			// Since both point A and B are vertices of the two new shapes, I add them to both vectors.
			shape1Vertices.push(A, B);
			shape2Vertices.push(A, B);
			// I iterate over all vertices of the original body. ;
			// I use the function det() ("det" stands for "determinant") to see on which side of AB each point is standing on. The parameters it needs are the coordinates of 3 points:
			// - if it returns a value >0, then the three points are in clockwise order (the point is under AB)
			// - if it returns a value =0, then the three points lie on the same line (the point is on AB)
			// - if it returns a value <0, then the three points are in counter-clockwise order (the point is above AB). 
			for (var i:Number=0; i<numVertices; i++) {
				d=det(A.x,A.y,B.x,B.y,verticesVec[i].x,verticesVec[i].y);
				if (d>0) {
					shape1Vertices.push(verticesVec[i]);
				}
				else {
					shape2Vertices.push(verticesVec[i]);
				}
			}
			// In order to be able to create the two new shapes, I need to have the vertices arranged in clockwise order.
			// I call my custom method, arrangeClockwise(), which takes as a parameter a vector, representing the coordinates of the shape's vertices and returns a new vector, with the same points arranged clockwise.
			shape1Vertices=arrangeClockwise(shape1Vertices);
			shape2Vertices=arrangeClockwise(shape2Vertices);
			// setting the properties of the two newly created shapes
			var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.type=b2Body.b2_dynamicBody;
			bodyDef.position=sliceBody.GetPosition();
			var fixtureDef:b2FixtureDef = new b2FixtureDef();
			fixtureDef.density=origFixture.GetDensity();
			fixtureDef.friction=origFixture.GetFriction();
			fixtureDef.restitution=origFixture.GetRestitution();
			// creating the first shape, if big enough
			if (getArea(shape1Vertices,shape1Vertices.length)>=0.05) {
				polyShape.SetAsVector(shape1Vertices);
				fixtureDef.shape=polyShape;
				bodyDef.userData=new userData(origUserDataId,shape1Vertices,origUserData.texture);
				addChild(bodyDef.userData);
				enterPointsVec[origUserDataId]=null;
				body=world.CreateBody(bodyDef);
				body.SetAngle(sliceBody.GetAngle());
				body.CreateFixture(fixtureDef);
				// setting a velocity for the debris
				body.SetLinearVelocity(setExplosionVelocity(body));
				// the shape will be also part of the explosion and can explode too
				explodingBodies.push(body);
			}
			// creating the second shape, if big enough
			if (getArea(shape2Vertices,shape2Vertices.length)>=0.05) {
				polyShape.SetAsVector(shape2Vertices);
				fixtureDef.shape=polyShape;
				bodyDef.userData=new userData(numEnterPoints,shape2Vertices,origUserData.texture);
				addChild(bodyDef.userData);
				enterPointsVec.push(null);
				numEnterPoints++;
				body=world.CreateBody(bodyDef);
				body.SetAngle(sliceBody.GetAngle());
				body.CreateFixture(fixtureDef);
				// setting a velocity for the debris
				body.SetLinearVelocity(setExplosionVelocity(body));
				// the shape will be also part of the explosion and can explode too
				explodingBodies.push(body);
			}
		}
		// this function will determine the velocity of the debris according
		// to the center of mass of the body and the distance from the explosion point
		private function setExplosionVelocity(b:b2Body):b2Vec2 {
			var distX:Number=b.GetWorldCenter().x*worldScale-explosionX;
			if (distX<0) {
				if (distX<-explosionRadius) {
					distX=0;
				}
				else {
					distX=- explosionRadius-distX;
				}
			}
			else {
				if (distX>explosionRadius) {
					distX=0;
				}
				else {
					distX=explosionRadius-distX;
				}
			}
			var distY:Number=b.GetWorldCenter().y*worldScale-explosionY;
			if (distY<0) {
				if (distY<-explosionRadius) {
					distY=0;
				}
				else {
					distY=- explosionRadius-distY;
				}
			}
			else {
				if (distY>explosionRadius) {
					distY=0;
				}
				else {
					distY=explosionRadius-distY;
				}
			}
			distX*=0.25;
			distY*=0.25;
			return new b2Vec2(distX,distY);
		}
		private function arrangeClockwise(vec:Vector.<b2Vec2>):Vector.<b2Vec2> {
			// The algorithm is simple: 
			// First, it arranges all given points in ascending order, according to their x-coordinate.
			// Secondly, it takes the leftmost and rightmost points (lets call them C and D), and creates tempVec, where the points arranged in clockwise order will be stored.
			// Then, it iterates over the vertices vector, and uses the det() method I talked about earlier. It starts putting the points above CD from the beginning of the vector, and the points below CD from the end of the vector. 
			// That was it!
			var n:int=vec.length,d:Number,i1:int=1,i2:int=n-1;
			var tempVec:Vector.<b2Vec2>=new Vector.<b2Vec2>(n),C:b2Vec2,D:b2Vec2;
			vec.sort(comp1);
			tempVec[0]=vec[0];
			C=vec[0];
			D=vec[n-1];
			for (var i:Number=1; i<n-1; i++) {
				d=det(C.x,C.y,D.x,D.y,vec[i].x,vec[i].y);
				if (d<0) {
					tempVec[i1++]=vec[i];
				}
				else {
					tempVec[i2--]=vec[i];
				}
			}
			tempVec[i1]=vec[n-1];
			return tempVec;
		}
		private function comp1(a:b2Vec2, b:b2Vec2):Number {
			// This is a compare function, used in the arrangeClockwise() method - a fast way to arrange the points in ascending order, according to their x-coordinate.
			if (a.x>b.x) {
				return 1;
			}
			else if (a.x<b.x) {
				return -1;
			}
			return 0;
		}
		private function det(x1:Number, y1:Number, x2:Number, y2:Number, x3:Number, y3:Number):Number {
			// This is a function which finds the determinant of a 3x3 matrix.
			// If you studied matrices, you'd know that it returns a positive number if three given points are in clockwise order, negative if they are in anti-clockwise order and zero if they lie on the same line.
			// Another useful thing about determinants is that their absolute value is two times the face of the triangle, formed by the three given points.
			return x1*y2+x2*y3+x3*y1-y1*x2-y2*x3-y3*x1;
		}
	}
}

and this is userData class:

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
package {
	import Box2D.Common.Math.b2Vec2;
	import flash.display.Sprite;
	import flash.display.BitmapData;
	import flash.geom.Matrix;
	public class userData extends Sprite {
		var id:int,texture:BitmapData;
		public function userData(id:int, verticesVec:Vector.<b2Vec2>, texture:BitmapData) {
			this.id=id;
			this.texture=texture;
			// I use the matrix so that I can have the center of the shape I'm drawing match the center of the BitmapData image - I "move" the BitmapData projection left by half its width and up by half its height.
			var m:Matrix = new Matrix();
			m.tx=- texture.width*0.5;
			m.ty=- texture.height*0.5;
			// I then draw lines from each vertex to the next, in clockwise order and use the beginBitmapFill() method to add the texture.
			this.graphics.lineStyle(1);
			this.graphics.beginBitmapFill(texture, m, true, true);
			this.graphics.moveTo(verticesVec[0].x*30, verticesVec[0].y*30);
			for (var i:int=1; i<verticesVec.length; i++) {
				this.graphics.lineTo(verticesVec[i].x*30, verticesVec[i].y*30);
			}
			this.graphics.lineTo(verticesVec[0].x*30, verticesVec[0].y*30);
			this.graphics.endFill();
		}
	}
}

And now let me see what kind of games can you make using this concept.

Download the source code.

]]>
9
Emanuele Feronato <![CDATA[Attend the Flash Gaming Summit 2012 with a 15% OFF!]]> http://www.emanueleferonato.com/?p=5068 2012-01-11T20:43:55Z 2012-01-11T20:41:15Z Flash Gaming Summit is the leading annual one-day conference dedicated to fostering the growth and success of the Flash games community. Now you can attend it with a 15% OFF!]]> Do you love to work in Flash games industry? The Flash Gaming Summit is the leading annual one-day conference dedicated to fostering the growth and success of the Flash games community. In this 4th edition, on March 4th, 2012 San Francisco will host the biggest names in the Flash games space to share industry insights and strategies on successful game design, development, and monetization.

The official theme for FGS 2012 is Maximize Your Game. Why? Game developers (and the industry in general) are doing amazing things with Flash games. We are seeing games being designed and created (maximized) for multiple devices and platforms. Developers are increasing (maximizing) the value of their IP. Game designers and artists are improving (maximizing) the overall quality of their games. New tools and resources are available to help improve (maximize) development time. Marketing and monetization strategies are emerging to enhance (maximize) overall game distribution, discovery, and revenue streams. Maximize Your Game reflects all of the exciting innovation we are seeing in the Flash games industry today.

Moreover, there is space for speakers in the following topics:

* Mobile-specific
* Design
* Development
* Marketing strategy
* Designing for multiple devices
* Game post-mortems
* Game Development
* Multi-player
* Developing for multiple platforms
* Development tools
* Performance optimization
* Game Design
* Design tips & tricks
* Game post-mortems
* 3D-specific
* Pros and cons
* Optimizing 2D performance
* Using Stage 3D
* Game post-mortems
* Business
* Building a business (going from one person to a small team)
* Artwork
* Graphics
* Animation

And it’s not over yet! Sign up for the conference with the discount code blog_Emanuele (hey! It’s me!!) and attend the summit with a 15% OFF!!

]]>
2
Emanuele Feronato <![CDATA[Develop double jump behavior in StencylWorks]]> http://www.emanueleferonato.com/?p=5056 2012-01-12T10:10:09Z 2012-01-10T16:31:56Z One of the most interesting features to add in a platform game is the double jump.

This means the player can jump, and while in the air, can jump once more to get higher platforms.

Something like this demo made with StencylWorks, taken from my upcoming game Kira the Witch:

Move with LEFT and RIGHT arrow keys, jump and double jump with UP arrow key.

I already showed you how to make an actor jump in the post Create awesome Flash games in no time with StencylWorks – adding intro, jumping and more, but when we want a double jump, we need two more attributes: one to see if we are jumping, that is if we are in the air because we made a jump (and not because we are falling down a platform) and one to determine if we can perform a double jump.

In the end, the double jump can be made only when you jump, and before you touch the ground you release the jump key and then press it again.

That is, the behavior you can see here:

Useless to say, really easy, like all StencylWorks features.

]]>
9
Emanuele Feronato <![CDATA[Create REAL explosions with Box2D – exploding objects and setting the center of explosion with mouse click]]> http://www.emanueleferonato.com/?p=5047 2012-01-05T11:55:13Z 2012-01-05T11:55:13Z I got a lot of requests once I published how to create REAL explosions with Box2D and most of them asked for exploding objects clicking on them, setting the center of explosion with mouse pointer.

So I changed a bit the script and now you have such features, and to preserve speed I also removed all chunks whose area is too small. They just slow down the prototype with no advantage.

So that’s what you have now:

Click on any object (static or dynamic) to explode it.

And this is the uncommented, yet to be optimized script:

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
package {
	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.*;
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.events.Event;
	public class Main extends Sprite {
		private var world:b2World=new b2World(new b2Vec2(0,10),true);
		private var worldScale:int=30;
		private var laserSegment:b2Segment;
		private var drawing:Boolean=false;
		private var affectedByLaser:Vector.<b2Body>;
		private var entryPoint:Vector.<b2Vec2>;
		private var explodingBodies:Vector.<b2Body>;
		private var explosionCenterX:Number;
		private var explosionCenterY:Number;
		private var chunks:Number=5;
		private var explosionRadius:Number=50;
		public function Main() {
			debugDraw();
			addWall(320,480,640,20);
			addWall(320,0,640,20);
			addWall(0,240,20,480);
			addWall(640,240,20,480);
			addWall(320,240,200,200);
			addWall(250,110,60,60);
			addWall(390,110,60,60);
			addEventListener(Event.ENTER_FRAME, updateWorld);
			stage.addEventListener(MouseEvent.MOUSE_DOWN,mousePressed);
		}
		private function mousePressed(e:MouseEvent):void {
			var cutAngle:Number;
			explosionCenterX=mouseX;
			explosionCenterY=mouseY;
			var clickedBody:b2Body=GetBodyAtXY(new b2Vec2(explosionCenterX/worldScale,explosionCenterY/worldScale));
			explodingBodies=new Vector.<b2Body>();
			if (clickedBody!=null) {
				explodingBodies.push(clickedBody);
				for (var i:Number=0; i<chunks; i++) {
					cutAngle=Math.random()*Math.PI*2;
					laserSegment=new b2Segment();
					laserSegment.p1=new b2Vec2((explosionCenterX+i/10-200*Math.cos(cutAngle))/worldScale,(explosionCenterY-200*Math.sin(cutAngle))/worldScale);
					laserSegment.p2=new b2Vec2((explosionCenterX+200*Math.cos(cutAngle))/worldScale,(explosionCenterY+200*Math.sin(cutAngle))/worldScale);
					affectedByLaser=new Vector.<b2Body>();
					entryPoint=new Vector.<b2Vec2>();
					world.RayCast(laserFired,laserSegment.p1,laserSegment.p2);
					world.RayCast(laserFired,laserSegment.p2,laserSegment.p1);
				}
			}
		}
		private function debugDraw():void {
			var debugDraw:b2DebugDraw = new b2DebugDraw();
			var debugSprite:Sprite = new Sprite();
			addChild(debugSprite);
			debugDraw.SetSprite(debugSprite);
			debugDraw.SetDrawScale(worldScale);
			debugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
			debugDraw.SetFillAlpha(0.5);
			world.SetDebugDraw(debugDraw);
		}
		private function addWall(pX:Number,pY:Number,w:Number,h:Number):void {
			var wallShape:b2PolygonShape = new b2PolygonShape();
			wallShape.SetAsBox(w/worldScale/2,h/worldScale/2);
			var wallFixture:b2FixtureDef = new b2FixtureDef();
			wallFixture.density=0;
			wallFixture.friction=1;
			wallFixture.restitution=0.5;
			wallFixture.shape=wallShape;
			var wallBodyDef:b2BodyDef = new b2BodyDef();
			wallBodyDef.position.Set(pX/worldScale,pY/worldScale);
			var wall:b2Body=world.CreateBody(wallBodyDef);
			wall.CreateFixture(wallFixture);
		}
		private function updateWorld(e:Event):void {
			world.Step(1/30,10,10);
			world.ClearForces();
			world.DrawDebugData();
		}
		private function laserFired(fixture:b2Fixture,point:b2Vec2,normal:b2Vec2,fraction:Number):Number {
			var affectedBody:b2Body=fixture.GetBody();
			if (explodingBodies.indexOf(affectedBody)!=-1) {
				var affectedPolygon:b2PolygonShape=fixture.GetShape() as b2PolygonShape;
				var fixtureIndex:int=affectedByLaser.indexOf(affectedBody);
				if (fixtureIndex==-1) {
					affectedByLaser.push(affectedBody);
					entryPoint.push(point);
				}
				else {
					var rayCenter:b2Vec2=new b2Vec2((point.x+entryPoint[fixtureIndex].x)/2,(point.y+entryPoint[fixtureIndex].y)/2);
					var rayAngle:Number=Math.atan2(entryPoint[fixtureIndex].y-point.y,entryPoint[fixtureIndex].x-point.x);
					var polyVertices:Vector.<b2Vec2>=affectedPolygon.GetVertices();
					var newPolyVertices1:Vector.<b2Vec2>=new Vector.<b2Vec2>();
					var newPolyVertices2:Vector.<b2Vec2>=new Vector.<b2Vec2>();
					var currentPoly:int=0;
					var cutPlaced1:Boolean=false;
					var cutPlaced2:Boolean=false;
					for (var i:int=0; i<polyVertices.length; i++) {
						var worldPoint:b2Vec2=affectedBody.GetWorldPoint(polyVertices[i]);
						var cutAngle:Number=Math.atan2(worldPoint.y-rayCenter.y,worldPoint.x-rayCenter.x)-rayAngle;
						if (cutAngle<Math.PI*-1) {
							cutAngle+=2*Math.PI;
						}
						if (cutAngle>0&&cutAngle<=Math.PI) {
							if (currentPoly==2) {
								cutPlaced1=true;
								newPolyVertices1.push(point);
								newPolyVertices1.push(entryPoint[fixtureIndex]);
							}
							newPolyVertices1.push(worldPoint);
							currentPoly=1;
						}
						else {
							if (currentPoly==1) {
								cutPlaced2=true;
								newPolyVertices2.push(entryPoint[fixtureIndex]);
								newPolyVertices2.push(point);
							}
							newPolyVertices2.push(worldPoint);
							currentPoly=2;
 
						}
					}
					if (! cutPlaced1) {
						newPolyVertices1.push(point);
						newPolyVertices1.push(entryPoint[fixtureIndex]);
					}
					if (! cutPlaced2) {
						newPolyVertices2.push(entryPoint[fixtureIndex]);
						newPolyVertices2.push(point);
					}
					createSlice(newPolyVertices1,newPolyVertices1.length);
					createSlice(newPolyVertices2,newPolyVertices2.length);
					world.DestroyBody(affectedBody);
				}
			}
			return 1;
		}
		private function findCentroid(vs:Vector.<b2Vec2>, count:uint):b2Vec2 {
			var c:b2Vec2 = new b2Vec2();
			var area:Number=0.0;
			var p1X:Number=0.0;
			var p1Y:Number=0.0;
			var inv3:Number=1.0/3.0;
			for (var i:int = 0; i < count; ++i) {
				var p2:b2Vec2=vs[i];
				var p3:b2Vec2=i+1<count?vs[int(i+1)]:vs[0];
				var e1X:Number=p2.x-p1X;
				var e1Y:Number=p2.y-p1Y;
				var e2X:Number=p3.x-p1X;
				var e2Y:Number=p3.y-p1Y;
				var D:Number = (e1X * e2Y - e1Y * e2X);
				var triangleArea:Number=0.5*D;
				area+=triangleArea;
				c.x += triangleArea * inv3 * (p1X + p2.x + p3.x);
				c.y += triangleArea * inv3 * (p1Y + p2.y + p3.y);
			}
			c.x*=1.0/area;
			c.y*=1.0/area;
			return c;
		}
		private function getArea(vs:Vector.<b2Vec2>, count:uint):Number {
			var area:Number=0.0;
			var p1X:Number=0.0;
			var p1Y:Number=0.0;
			var inv3:Number=1.0/3.0;
			for (var i:int = 0; i < count; ++i) {
				var p2:b2Vec2=vs[i];
				var p3:b2Vec2=i+1<count?vs[int(i+1)]:vs[0];
				var e1X:Number=p2.x-p1X;
				var e1Y:Number=p2.y-p1Y;
				var e2X:Number=p3.x-p1X;
				var e2Y:Number=p3.y-p1Y;
				var D:Number = (e1X * e2Y - e1Y * e2X);
				var triangleArea:Number=0.5*D;
				area+=triangleArea;
			}
			return area;
		}
		private function createSlice(vertices:Vector.<b2Vec2>,numVertices:int):void {
			if (getArea(vertices,vertices.length)>=0.05) {
				var centre:b2Vec2=findCentroid(vertices,vertices.length);
				for (var i:int=0; i<numVertices; i++) {
					vertices[i].Subtract(centre);
				}
				var sliceBody:b2BodyDef= new b2BodyDef();
				sliceBody.position.Set(centre.x, centre.y);
				sliceBody.type=b2Body.b2_dynamicBody;
				var slicePoly:b2PolygonShape = new b2PolygonShape();
				slicePoly.SetAsVector(vertices,numVertices);
				var sliceFixture:b2FixtureDef = new b2FixtureDef();
				sliceFixture.shape=slicePoly;
				sliceFixture.density=1;
				var worldSlice:b2Body=world.CreateBody(sliceBody);
				worldSlice.CreateFixture(sliceFixture);
				for (i=0; i<numVertices; i++) {
					vertices[i].Add(centre);
				}
				var distX:Number=(centre.x*worldScale-explosionCenterX);
				if (distX<0) {
					if (distX<-explosionRadius) {
						distX=0;
					}
					else {
						distX=-50-distX;
					}
				}
				else {
					if (distX>explosionRadius) {
						distX=0;
					}
					else {
						distX=50-distX;
					}
				}
				var distY:Number=(centre.y*worldScale-explosionCenterY);
				if (distY<0) {
					if (distY<-explosionRadius) {
						distY=0;
					}
					else {
						distY=-50-distY;
					}
				}
				else {
					if (distY>explosionRadius) {
						distY=0;
					}
					else {
						distY=50-distY;
					}
				}
				distX*=0.25;
				distY*=0.25;
				worldSlice.SetLinearVelocity(new b2Vec2(distX,distY));
				explodingBodies.push(worldSlice);
			}
		}
		private function GetBodyAtXY(coordinate:b2Vec2):b2Body {
			var touchedBody:b2Body=null;
			world.QueryPoint(GetBodyCallback,coordinate);
			function GetBodyCallback(fixture:b2Fixture):Boolean {
				var shape:b2Shape=fixture.GetShape();
				var inside:Boolean=shape.TestPoint(fixture.GetBody().GetTransform(),coordinate);
				if (inside) {
					touchedBody=fixture.GetBody();
					return false;
				}
				return true;
			}
			return touchedBody;
		}
	}
}

I will post the complete tutorial once I’ll complete the final step: adding textures to explosions. If you want to try the class, you can copy/paste it in the example you can download at slicing, splitting and cutting objects with Box2D.

]]>
20
Emanuele Feronato <![CDATA[One game, many frameworks]]> http://www.emanueleferonato.com/?p=5037 2012-01-03T16:23:07Z 2012-01-03T16:23:07Z Roger Engelbert built a guide to do it with four different frameworks!]]> There’s no doubt 2012 will be the year of the frameworks. Roger Engelbert in his blog rengelbert.com show us how to do a mobile Frogger game using four different frameworks: Starling, Sparrow, Cocos2D and LibGDX.

This is the game involved in the experiment:

Use arrow keys to move the frog, or click on the frog controller in the bottom right corner of the game.

Here is a brief intro by Roger:

I will first explain something of the logic controlling the game. It is very important to point out that the logic itself has nothing to do with any of the frameworks I’ll use. The frameworks for the most part only influence the VIEW part of the logic: the actual rendering of textures on screen in place of all the rectangle objects the logic handles.

So these tutorials are not meant to be on how to build a game like Frogger, but how to blitt stuff to the screen.

Having said that, I thought I might as well explain enough of the game logic so that you can separate that logic from the actual framework code.

The Game Engine

The engine manages a series of Screen objects, but displays only one screen at a time. So you can build an Intro Screen, a How to Play Screen, a High Scores Screen, a Game Screen… The Engine then updates the currently displayed Screen object within a loop.

For this example I created two screens, one called a MenuScreen that shows the instructions on how to play the game, and one screen called GameScreen where the actual game runs.

The main Game object contains exposed references to GameData, the TextureAtlas and Sounds. Every object in the game receives a reference to the Game object. I’ve decide to use composition and drop the Singletons.

Controls

For the Flash version of the game I will use two sets of controls: Key presses and Mouse clicks. Later when porting to mobile devices I will drop the Keyboard input logic and just keep the touch events.

The Game Logic

The game logic is very simple. The screen is divided into 13 rows, I called them Tiers. Each Tier has a bunch of sprites inside it, they can be cars, or turtles or tree logs.

The frog sprite, controlled by the player, can have only 13 possible values for its Y position, one value per Tier. So with every UP or DOWN movement the player Y value changes to either the next Tier or the previous one.

Collision is run only with the objects belonging to the Tier the frog is currently at.

Each Tier has the responsibility to update the positions of the sprites it contains.

The Skin Property

As I talked about earlier, the different frameworks used will be in charge of the SKIN property of the Sprites used in the game as well as the various Texture objects a skin may use (if it’s animated for instance.) It is here that the Framework logic and syntax can be found.

However when I update the position of sprites in the game I do this through the wrappers and not directly to the Image objects. This might seem like extra work for you, and you’re free to change this. I simply find this way to be the easiest when taking the same logic to different platforms as I only need to change the logic controlling rendering of the skins.

Now, you are ready to ready one of the most interesting tutorials I’ve ever seen.

http://www.rengelbert.com/tutorial.php?id=163 (using Starling)
http://www.rengelbert.com/tutorial.php?id=167 (using Sparrow)
http://www.rengelbert.com/tutorial.php?id=171 (using Cocos2D)
http://www.rengelbert.com/tutorial.php?id=175 (using LibGDX)

I am looking forward for playing and messing around with frameworks and publish my thoughts.

]]>
1