The engine behind Splitter Flash game – first AS3 prototype

Read all posts about "" game

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:

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 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  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 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 = 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 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] 

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 :)

Get the most popular Phaser 3 book

Through 202 pages, 32 source code examples and an Android Studio project you will learn how to build cross platform HTML5 games and create a complete game along the way.

Get the book

214 GAME PROTOTYPES EXPLAINED WITH SOURCE CODE
// 1+2=3
// 100 rounds
// 10000000
// 2 Cars
// 2048
// A Blocky Christmas
// A Jumping Block
// A Life of Logic
// Angry Birds
// Angry Birds Space
// Artillery
// Astro-PANIC!
// Avoider
// Back to Square One
// Ball Game
// Ball vs Ball
// Ball: Revamped
// Balloon Invasion
// BallPusher
// Ballz
// Bar Balance
// Bejeweled
// Biggification
// Block it
// Blockage
// Bloons
// Boids
// Bombuzal
// Boom Dots
// Bouncing Ball
// Bouncing Ball 2
// Bouncy Light
// BoxHead
// Breakout
// Bricks
// Bubble Chaos
// Bubbles 2
// Card Game
// Castle Ramble
// Chronotron
// Circle Chain
// Circle Path
// Circle Race
// Circular endless runner
// Cirplosion
// CLOCKS - The Game
// Color Hit
// Color Jump
// ColorFill
// Columns
// Concentration
// Crossy Road
// Crush the Castle
// Cube Jump
// CubesOut
// Dash N Blast
// Dashy Panda
// Deflection
// Diamond Digger Saga
// Don't touch the spikes
// Dots
// Down The Mountain
// Drag and Match
// Draw Game
// Drop Wizard
// DROP'd
// Dudeski
// Dungeon Raid
// Educational Game
// Elasticity
// Endless Runner
// Erase Box
// Eskiv
// Farm Heroes Saga
// Filler
// Flappy Bird
// Fling
// Flipping Legend
// Floaty Light
// Fuse Ballz
// GearTaker
// Gem Sweeper
// Globe
// Goat Rider
// Gold Miner
// Grindstone
// GuessNext
// Helicopter
// Hero Emblems
// Hero Slide
// Hexagonal Tiles
// HookPod
// Hop Hop Hop Underwater
// Horizontal Endless Runner
// Hundreds
// Hungry Hero
// Hurry it's Christmas
// InkTd
// Iromeku
// Jet Set Willy
// Jigsaw Game
// Knife Hit
// Knightfall
// Legends of Runeterra
// Lep's World
// Line Rider
// Lumines
// Magick
// MagOrMin
// Mass Attack
// Math Game
// Maze
// Meeblings
// Memdot
// Metro Siberia Underground
// Mike Dangers
// Mikey Hooks
// Nano War
// Nodes
// o:anquan
// One Button Game
// One Tap RPG
// Ononmin
// Pacco
// Perfect Square!
// Perfectionism
// Phyballs
// Pixel Purge
// PixelField
// Planet Revenge
// Plants Vs Zombies
// Platform
// Platform game
// Plus+Plus
// Pocket Snap
// Poker
// Pool
// Pop the Lock
// Pop to Save
// Poux
// Pudi
// Pumpkin Story
// Puppet Bird
// Pyramids of Ra
// qomp
// Quick Switch
// Racing
// Radical
// Rebuild Chile
// Renju
// Rise Above
// Risky Road
// Roguelike
// Roly Poly
// Run Around
// Rush Hour
// SameGame
// SamePhysics
// Save the Totem
// Security
// Serious Scramblers
// Shrink it
// Sling
// Slingy
// Snowflakes
// Sokoban
// Space Checkers
// Space is Key
// Spellfall
// Spinny Gun
// Splitter
// Spring Ninja
// Sproing
// Stabilize!
// Stack
// Stick Hero
// String Avoider
// Stringy
// Sudoku
// Super Mario Bros
// Surfingers
// Survival Horror
// Talesworth Adventure
// Tetris
// The Impossible Line
// The Moops - Combos of Joy
// The Next Arrow
// Threes
// Tic Tac Toe
// Timberman
// Tiny Wings
// Tipsy Tower
// Toony
// Totem Destroyer
// Tower Defense
// Trick Shot
// Tunnelball
// Turn
// Turnellio
// TwinSpin
// vvvvvv
// Warp Shift
// Way of an Idea
// Whack a Creep
// Wheel of Fortune
// Where's my Water
// Wish Upon a Star
// Word Game
// Wordle
// Worms
// Yanga
// Yeah Bunny
// Zhed
// zNumbers