The engine behind Splitter Flash game – first AS3 prototype

Some time ago I posted about the engine behind Splitter Flash game, the C code used to cut off and split objects with Box2D

Now I received an AS3 prototype from Guillaume Pommey.

Hi ! I try to port the splitter engine to AS3, I haven’t any error in the function but I have an incorrect structure…

Can you help me, the port may be correct…

PS : The Box2D files are changed, because I use a funtion (raycast) which isn’t in the original source. There isn’t the key controls, there are useless if the programm doesn’t work. The problem should be come the structure and the end of the programme with the function Step or Test.

As you can see, Guillaume used raycast function you can find at this link (also take a look at the demo, it’s very interesting).

This is the 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
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
package{
 
import Box2D.Dynamics.*
import Box2D.Collision.*
import Box2D.Collision.Shapes.*
import Box2D.Dynamics.Joints.*
import Box2D.Dynamics.Contacts.*
import Box2D.Common.Math.*
import Box2D.Common.*
import flash.events.Event;
import flash.display.*;
import flash.text.*;
import General.*
import flash.display.MovieClip;
 
	public class splitter extends MovieClip{ //Public Class
		public function splitter(){ //Main function
 
			var m_sprite:Sprite = new Sprite();
			addChild(m_sprite);
 
			addEventListener(Event.ENTER_FRAME, update, false, 0, true);
 
			var worldAABB:b2AABB = new b2AABB();
			worldAABB.lowerBound.Set(-1000.0, -1000.0);
			worldAABB.upperBound.Set(1000.0, 1000.0);
 
			// Define the gravity vector
			var gravity:b2Vec2 = new b2Vec2(0.0, 0.9);
 
			// Allow bodies to sleep
			var doSleep:Boolean = true;
 
			// Construct a world object
			m_world = new b2World(worldAABB, gravity, doSleep);
			dbgDraw = new b2DebugDraw();
			dbgDraw.m_sprite = m_sprite;
			dbgDraw.m_drawScale = 15.0;
			dbgDraw.m_fillAlpha = 0.3;
			dbgDraw.m_lineThickness = 1.0;
			dbgDraw.m_drawFlags = b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit;
			m_world.SetDebugDraw(dbgDraw);
 
			CutterTest();
 
		}
 
 
		public function CutterTest(){
 
			//m_world->SetGravity(b2Vec2(0,0));
 
					var ground:b2Body = null;
 
					var bd:b2BodyDef = new b2BodyDef();
					bd.position.Set(0.0, -10.0);
					ground = m_world.CreateBody(bd);
 
					var sd:b2PolygonDef = new b2PolygonDef();
					sd.SetAsBox(50.0, 10.0);
					ground.CreateShape(sd);
 
					bd.position.Set(0.0, 30);
					ground = m_world.CreateBody(bd);
 
					sd.SetAsBox(50.0, 10.0);
					ground.CreateShape(sd);
 
					bd.position.Set(0.0, 1.0);
					laserBody = m_world.CreateBody(bd);
 
					sd.SetAsBox(5.0, 1.0);
					sd.density = 4.0;
					laserBody.CreateShape(sd);
					laserBody.SetMassFromShapes();
 
					sd.SetAsBox(3.0, 3.0);
					sd.density = 5.0;
 
					bd.userData = 1;
					bd.position.Set(0.0, 8.0);
					var body1:b2Body = m_world.CreateBody(bd);
					body1.CreateShape(sd);
					body1.SetMassFromShapes();
 
					sd.SetAsBox(3.0, 3.0);
					sd.density = 5.0;
 
					bd.userData = 1;
					bd.position.Set(0.0, 8.0);
					body1 = m_world.CreateBody(bd);
					body1.CreateShape(sd);
					body1.SetMassFromShapes();
		}
 
		/***************************************************/
		/*********************ETAPE 1***********************/
		/***************************************************/
 
		public function CheckPolyShape(poly)
		{
			if (!(3 <= poly.vertexCount && poly.vertexCount <= b2_maxPolygonVertices)){
					return -1;
			}
 
			var m_normals:b2Vec2 = new b2Vec2(poly.vertexCount);
 
		 	// Compute normals. Ensure the edges have non-zero length.
				for (var i=0; i < poly.vertexCount; i++)
				{
					var i1 = i;
					var i2 = 0;
					if((i + 1) < poly.vertexCount){//Possible erreur
						i2 = i + 1;
					} else {
						i2 = 0;
					}
					//var i2 = i + 1 <poly->vertexCount ? i + 1 : 0;
					var edge:b2Vec2 = new b2Vec2(poly.vertices[i2] - poly.vertices[i1]);
					if (!(edge.LengthSquared()> Number.MIN_VALUE * Number.MIN_VALUE))//Peut être une erreur
						return -1;
					m_normals[i] = b2Math.b2CrossVF(edge, 1.0);//Problème ???
					m_normals[i].Normalize();
				}
 
				// Ensure the polygon is convex.
				for (i=0; i <poly.vertexCount; i++)
				{
					for (var j=0; j <poly.vertexCount; j++)
					{
						// Don't check vertices on the current edge.
						if (j == i || j == (i + 1) % poly.vertexCount)
						{
							continue;
						}
 
						// Your polygon is non-convex (it has an indentation).
						// Or your polygon is too skinny.
						var vecPlus = poly.vertices[j] - poly.vertices[i];
						var s = b2Math.b2Dot(m_normals[i], vecPlus);//Problème ??
						if (!(s < -b2Settings.b2_linearSlop)){//Idem
							return -1;
						}
					}
				}
 
				// Ensure the polygon is counter-clockwise.
				for (i=1; i <poly.vertexCount; i++)
				{
					var cross = b2Math.b2CrossVV(m_normals[i-1], m_normals[i]);
 
					// Keep asinf happy.
					cross = b2Math.b2Clamp(cross, -1.0, 1.0);
 
					// You have consecutive edges that are almost parallel on your polygon.
					var angle = Math.asin(cross);
					if (!(angle> b2Settings.b2_angularSlop))
						return -1;
				}
 
				// Compute the polygon centroid.
				var m_centroid:b2Vec2; 
				m_centroid.Set(0.0, 0.0);
				var area = 0.0;
 
				// pRef is the reference point for forming triangles.
				// It's location doesn't change the result (except for rounding error).
				var pRef:b2Vec2 = new b2Vec2(0.0, 0.0);
 
				const inv3 = 1.0 / 3.0;
 
				for (i=0; i <poly.vertexCount; i++)
				{
					// Triangle vertices.
					var p1:b2Vec2 =  pRef;
					var p2:b2Vec2 = new b2Vec2(poly.vertices[i]);
					var p3 = 0;
					if((i + 1) < poly.vertexCount){//Possible erreur
						p3 = poly.vertices[i+1];
					} else {
						p3 = poly.vertices[0];
					}
					//var p3:b2Vec2 = i + 1 <poly->vertexCount ? poly->vertices[i+1] : poly->vertices[0];
 
					//var e1Vec:b2Vec2 = (p2 - p1)
					var e1:b2Vec2 = b2Math.SubtractVV(p2, p1);// ?????
					var e2:b2Vec2 = b2Math.SubtractVV(p3, p1);// ?????
 
					var D = b2Math.b2CrossVV(e1, e2);
 
					var triangleArea = 0.5 * D;
					area += triangleArea;
 
					// Area weighted centroid
					m_centroid += triangleArea * inv3 * (p1 + p2 + p3);
				}
 
				// Centroid
				if (!(area> Number.MIN_VALUE))
					return -1;
				//m_centroid *= 1.0 / area;
				b2Math.MulFV((1.0 / area), m_centroid); // ??????
 
				// Compute the oriented bounding box.
				//ComputeOBB(&m_obb, m_vertices, m_vertexCount);
 
				// Create core polygon shape by shifting edges inward.
				// Also compute the min/max radius for CCD.
				for (i=0; i <poly.vertexCount; i++)
				{
					//Possible erreur
					if((i - 1) >= 0){
						i1 = i - 1;
					}else {
						i1 = (poly.vertexCount - 1)
					}
					//int32 i1 = i - 1>= 0 ? i - 1 : poly->vertexCount - 1;
					i2 = i;
 
					var n1:b2Vec2 = new b2Vec2(m_normals[i1]);
					var n2:b2Vec2 = new b2Vec2(m_normals[i2]);
					var v:b2Vec2 = b2Math.SubtractVV(poly.vertices[i], m_centroid); // ?????
 
					var d:b2Vec2;
					d.x = b2Math.b2Dot(n1, v) - b2Settings.b2_toiSlop;
					d.y = b2Math.b2Dot(n2, v) - b2Settings.b2_toiSlop;
 
					// Shifting the edge inward by b2_toiSlop should
					// not cause the plane to pass the centroid.
 
					// Your shape has a radius/extent less than b2_toiSlop.
					if (!(d.x>= 0.0))
						return -1;
					if (!(d.y>= 0.0))
						return -1;
				}
 
 
			return 0;
 
		}
 
	/***************************************************/
	/*********************ETAPE 2***********************/
	/***************************************************/
 
	 /// Split a shape trough a segment
    /// @return
    /// -1 - Error on split
    ///  0 - Normal result is two new shape definitions.
    public function SplitShape(shape, segment, splitSize, newPolygon)// ????
    {
        /*assert(shape != NULL);
        assert(newPolygon != NULL);
        assert(splitSize>= 0);*/ // Eventuellement utile...mais bon
 
     	var lambda:Number = 1;
        var normal:b2Vec2;
 
		const b:b2Body = shape.GetBody();
      	//const b2Body* b = shape->GetBody();
        const xf:b2XForm = b.GetXForm();
        if (shape.TestSegment(xf, lambda, normal, segment, 1.0) != e_hitCollide)
            return -1;
		var nextVec = (1-lambda)*segment.p1+lambda*segment.p2;
        var entryPoint:b2Vec2 = nextVec;
 
        var reverseSegment:b2Segment;
        reverseSegment.p1 = segment.p2;
        reverseSegment.p2 = segment.p1;
 
        if (shape.TestSegment(xf, lambda, normal, reverseSegment, 1.0) != e_hitCollide)
            return -1;
		var nextVec2 = (1-lambda)*segment.p2+lambda*segment.p1;//Clash ??
        var exitPoint:b2Vec2 = nextVec2;
 
        var localEntryPoint:b2Vec2 = b.GetLocalPoint(entryPoint);
        var localExitPoint:b2Vec2  = b.GetLocalPoint(exitPoint);
        const vertices:b2Vec2 = shape.GetVertices();
        var cutAdded:Array = [-1,-1];
        var lastA = -1;
        for(var i = 0; i<shape.GetVertexCount(); i++)
        {
            var n;
            //Find out if this vertex is on the old or new shape.
            if (b2Math.b2Dot(b2Math.b2CrossVF(b2Math.SubtractVV(localExitPoint,localEntryPoint), 1), b2Math.SubtractVV(vertices[i], localEntryPoint))> 0) //Clash ??
                n = 0;
            else
                n = 1;
            if (lastA != n)
            {
                //If we switch from one shape to the other add the cut vertices.
                if (lastA == 0)
                {
                    //assert(cutAdded[0] == -1); Couiiic
                    cutAdded[0] = newPolygon[lastA].vertexCount;
                    newPolygon[lastA].vertices[newPolygon[lastA].vertexCount] = localExitPoint;
                    newPolygon[lastA].vertexCount++;
                    newPolygon[lastA].vertices[newPolygon[lastA].vertexCount] = localEntryPoint;
                    newPolygon[lastA].vertexCount++;
                }
                if (lastA == 1)
                {
                    //assert(cutAdded[lastA] == -1); Recouiiic
                    cutAdded[lastA] = newPolygon[lastA].vertexCount;
                    newPolygon[lastA].vertices[newPolygon[lastA].vertexCount] = localEntryPoint;
                    newPolygon[lastA].vertexCount++;
                    newPolygon[lastA].vertices[newPolygon[lastA].vertexCount] = localExitPoint;
                    newPolygon[lastA].vertexCount++;
                }
            }
            newPolygon[n].vertices[newPolygon[n].vertexCount] = vertices[i];
            newPolygon[n].vertexCount++;
            lastA = n;
        }
 
        //Add the cut in case it has not been added yet.
        if (cutAdded[0] == -1)
        {
            cutAdded[0] = newPolygon[0].vertexCount;
            newPolygon[0].vertices[newPolygon[0].vertexCount] = localExitPoint;
            newPolygon[0].vertexCount++;
            newPolygon[0].vertices[newPolygon[0].vertexCount] = localEntryPoint;
            newPolygon[0].vertexCount++;
        }
        if (cutAdded[1] == -1)
        {
            cutAdded[1] = newPolygon[1].vertexCount;
            newPolygon[1].vertices[newPolygon[1].vertexCount] = localEntryPoint;
            newPolygon[1].vertexCount++;
            newPolygon[1].vertices[newPolygon[1].vertexCount] = localExitPoint;
            newPolygon[1].vertexCount++;
        }
 
        for(n = 0; n<2 ; n++)
        {
            var offset:b2Vec2;
            if (cutAdded[n]> 0)
            {
				var offsetValue = (newPolygon[n].vertices[cutAdded[n]-1] - newPolygon[n].vertices[cutAdded[n]])//Substitution
                offset = offsetValue;
            }else{
				offsetValue = (newPolygon[n].vertices[newPolygon[n].vertexCount-1] - newPolygon[n].vertices[0]);//Substitution
                offset = offsetValue;
            }
            offset.Normalize();
 
            newPolygon[n].vertices[cutAdded[n]] += b2Math.MulFV(splitSize, offset);
 
 
            if (cutAdded[n] <newPolygon[n].vertexCount-2)
            {
				offsetValue = (newPolygon[n].vertices[cutAdded[n]+2] - newPolygon[n].vertices[cutAdded[n]+1]);
                offset = offsetValue;
            }else{
				offsetValue = (newPolygon[n].vertices[0] - newPolygon[n].vertices[newPolygon[n].vertexCount-1]);
                offset = offsetValue;
            }
            offset.Normalize();
 
            newPolygon[n].vertices[cutAdded[n]+1] += b2Math.MulFV(splitSize, offset);
        }
 
        /*
        //Check if the new shapes are not too tiny. (TODO: still generates shapes which fail assert checks)
        for(int n=0;n<2;n++)
            for(int i=0;i<newPolygon[n].vertexCount;i++)
                for(int j=0;j<newPolygon[n].vertexCount;j++)
                    if (i != j && (newPolygon[n].vertices[i] - newPolygon[n].vertices[j]).Length() <0.1)
                        return -1;
        */
        for(n=0;n<2;n++)
            if (CheckPolyShape(newPolygon[n]))
                return -1;
        return 0;
    }
 
	/***************************************************/
	/*********************ETAPE 3***********************/
	/***************************************************/
 
	public function Cut():void
    {
        var segmentLength = 30.0;
 
        var segment:b2Segment;
 
        var laserStart:b2Vec2 = new b2Vec2(5.0-0.1, 0.0);
        var laserDir:b2Vec2 = new b2Vec2(segmentLength, 0.0);
 
        segment.p1 = laserBody.GetWorldPoint(laserStart);
        segment.p2 = segment.p1 + laserBody.GetWorldVector(laserDir);
 
        var max_shapes = 64;
     	var shapes:b2Shape; 
		var count = 1;
        count = m_world.Raycast(segment, shapes[0], max_shapes, false, null);//Gros doute !!
        for(var i=0;i<count;i++)
        {
            //Make sure it's a polygon, we cannot cut circles.
            if(shapes[i].GetType() != b2Shape.e_polygonShape)
                continue;
 
            var b:b2Body = shapes[i].GetBody();
            //Custom check to make sure we don't cut stuff we don't want to cut.
            if (b.GetUserData() != 1) //Ouille !
                continue;//return if we cannot pass trough uncutable shapes.
 
            var polyShape:b2PolygonShape = new b2PolygonShape(shapes[i]);
            var pd:b2PolygonDef;
            pd[0].density = 5.0;
            pd[1].density = 5.0;
 
            if (SplitShape(polyShape, segment, 0.1, pd) == 0)
            {
                b.DestroyShape(shapes[i]);
                b.CreateShape(pd[0]);
                b.SetMassFromShapes();
                b.WakeUp();
 
                var bd:b2BodyDef;
                bd.userData = 1;
                bd.position = b.GetPosition();
                bd.angle = b.GetAngle();
                var newBody:b2Body = m_world.CreateBody(bd);
                newBody.CreateShape(pd[1]);
                newBody.SetMassFromShapes();
                newBody.SetAngularVelocity(b.GetAngularVelocity());
                newBody.SetLinearVelocity(b.GetLinearVelocity());
            }
        }
    }
 
	public function Step(Settings:*, settings)
    {
      /*  m_debugDraw.DrawString(5, m_textLine, "Keys: cut = c");
        m_textLine += 15;*/
 
        //Test::Step(settings);
 
       	var segmentLength = 30.0;
 
        var segment:b2Segment;
       	var laserStart:b2Vec2 = new b2Vec2(5.0-0.1, 0.0);
       	var laserDir:b2Vec2 = new b2Vec2(segmentLength,0.0);
        segment.p1 = laserBody.GetWorldPoint(laserStart);
        segment.p2 = laserBody.GetWorldVector(laserDir);
        segment.p2+=segment.p1;
 
        var laserColor:b2Color = new b2Color(1,0,0);
        dbgDraw.DrawSegment(segment.p1, segment.p2, laserColor);
    }
 
	//I have a problem with this
	/*public static function Create() 
    {
        CutterTest();
    }*/
 
 
	/***************************************************/
	/*********************ETAPE 3***********************/
	/***************************************************/
 
		public var m_world:b2World;
		public var m_physScale:Number = 30;
		public var m_iterations:int = 10;
		public var m_timeStep:Number = 1/30;
 
		//Problème à régler
		public const b2_maxPolygonVertices:int = 8;		
 
		//Programme
		public var poly:b2PolygonDef = new b2PolygonDef();
		public var newPolygon:b2PolygonDef = new b2PolygonDef();
		public const segment:b2Segment = new b2Segment();//???
		public var shape:b2PolygonShape;
		public var laserBody:b2Body;
 
		public var dbgDraw:b2DebugDraw;
 
		//Basics
		public var e_hitCollide = 1;
 
		public function update(e:Event):void{
			m_world.Step(m_timeStep, m_iterations);
		}
	}
}

And this is the source with raycast for Box2D included.

The scripts gave no errors so I think we are close to the solution, I will take a look at it during the next days, meanwhile if you find out what’s wrong, you know where to comment :)

Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars (8 votes, average: 4.75 out of 5)
Loading ... Loading ...
Flash Templates provided by Template Monster are pre-made web design products developed using Flash technology.
They can be easily customized to meet the unique requirements of your project.
Be my fan on Facebook and follow me on Twitter! Exclusive content for my Facebook fans and Twitter followers

This post has 10 comments

  1. Shival

    on February 16, 2009 at 6:33 am

    Looks great but still unable to understand. Emanuele please continue with the absolute beginner tutorials. And what happened to the colours in the script? All plain black?

  2. Simon

    on February 16, 2009 at 7:30 am

    Shival there’s only so far you can go with absolute beginner tutorials.

  3. ThomasTan

    on February 16, 2009 at 11:43 am

    *drags himself to computer*
    *drags fingers across the keyboard*
    check your e-mail…
    (this message took 12 min 34 sec 385 milliseconds to write)
    *drag pinky finger to mouse*
    *clicks on “Submit Comment”*

  4. Xodus

    on February 16, 2009 at 1:25 pm

    I think its great that your doing this, along with the beginner tuts. who knows how many game concepts are in those 489 lines? Please continue!

  5. Jonh

    on February 16, 2009 at 1:53 pm

    Yeah, please do simpler tutorials with line by line descriptions. This doesn’t make any sense for me.

  6. Niall Lavigne

    on February 17, 2009 at 2:28 pm

    You guys are completely missing the point. He’s doing the simple Box2D line by line tutorials as well, just look back a few posts. Things like these are experiments, and invitations to the more experienced programmers to give an interesting concept a try. He can’t only do the beginner tutorials, that’s be very boring for others. You need to remember you aren’t all newcomers.

  7. Shival

    on February 17, 2009 at 5:24 pm

    @Niall and others: It takes time for the newcomers like us to reach your level. It would be better if you use this platform to add more people to the flash reign rather than just discussing codes amongst yourself.

  8. Crunch

    on February 17, 2009 at 10:59 pm

    That ragdoll part on the demos page could be used for a spiderman flash game!

  9. The engine behind Splitter Flash game - new AS3 prototypes : Emanuele Feronato

    on February 19, 2009 at 2:26 pm

    [...] Box2D objects seem to be the ultimate challenge around here… after The engine behind Splitter Flash game – first AS3 prototype, now we have two updates… the first by Guillaume Pommey, author of the previous prototype and [...]

  10. brart

    on February 21, 2009 at 1:53 am

    *cough*
    I don’t get where everyone is talking about! maybe this is to hard for the beginners… But there are loads of tutorials from emanuel written for you…

    And he didn’t wasted his time on advanced stuff because this has been written by someone else. So I miss your point.

    And Shival: “It would be better if you use this platform to add more people to the flash reign rather than just discussing codes amongst yourself.”
    As far as I know it isn’t emanuels duty to bring as much people as possible to the world of flash!

    Plz respect the time someone has spend on this and just say topic realated things.

    Can’t wait till someone post a working version, this looks promising!