The engine behind Splitter Flash game : working code

Read all posts about "" game

I think we finally have a working slicing engine. It started with The engine behind Splitter Flash game, then come The engine behind Splitter Flash game – first AS3 prototype and The engine behind Splitter Flash game – new AS3 prototypes.

Now Guillaume Pommey aka pompom sent us a working version.

I revised my prototype of slicing engine two days ago and I corrected a lot of errors. After an afternoon on my code, I resolved almost all my problems.

I am proud ^^, now the slicing engine works.

I joint the Main.as and the .fla cause I use radio button to change the cutter style, I let you discover it :) .

Ps : Don’t forget that I use the Raycast function so speak about this on your blog.

For more information about Raycast function check Box2D Raycasts post.

The engine works perfectly, and has two ways of cutting objects: the laser mode cuts objects when you press C while the cutter one lets you cut by drawing a line.

Here it is the source code:

/*
* Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
*
* This software is provided 'as-is', without any express or implied
* warranty.  In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/

/*
*This a port in AS3 by Guillaume Pommey of a slicing engine in C
*Thanks to Emanuele Feronato for his help : http://www.emanueleferonato.com/category/box2d/
*Thanks to Boris The Brave for his Raycast function : http://personal.boristhebrave.com/
*I hope you will enjoy this, have fun !
*/

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 General.*
import flash.display.*;
import flash.events.*;
import flash.utils.getTimer
import flash.display.MovieClip;
	
	public class Main extends MovieClip{
		public function Main(){
		
			m_fpsCounter.x = 7;
			m_fpsCounter.y = 5;
			addChildAt(m_fpsCounter, 0);
			
			m_sprite = new Sprite();
			addChild(m_sprite);
			
			m_input = new Input(m_sprite);

			addEventListener(Event.ENTER_FRAME, update, false, 0, true);
			stage.addEventListener(KeyboardEvent.KEY_DOWN, key_pressed);
			stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpEvent);
			
			//Create a b2World
			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, 10.0);
			
			// 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;
			m_world.SetDebugDraw(dbgDraw);

			//Create some nice shapes
			CutterTest();
		}
	
		public function CutterTest(){

				var ground:b2Body = null;
				
				//Top
				var bd:b2BodyDef = new b2BodyDef();
				bd.position.Set(0.0, -6.0);
				ground = m_world.CreateBody(bd);
		
				var sd:b2PolygonDef = new b2PolygonDef();
				sd.SetAsBox(50.0, 10.0);
				ground.CreateShape(sd);
				
				//Bottom
				bd.position.Set(0.0, 45);
				ground = m_world.CreateBody(bd);
		 
				sd.SetAsBox(50.0, 10.0);
				ground.CreateShape(sd);
		 		
				//Laser body
				bd.position.Set(6.0, 15.0);
				laserBody = m_world.CreateBody(bd);
		 
				sd.SetAsBox(5.0, 1.0);
				sd.density = 4.0;
				laserBody.CreateShape(sd);
				laserBody.SetMassFromShapes();
				
				//Boxes
				sd.SetAsBox(3.0, 3.0);
				sd.density = 5.0;
		 
				bd.userData = 1;
				bd.position.Set(25.0, 15.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(25.0, 15.0);
				body1 = m_world.CreateBody(bd);
				body1.CreateShape(sd);
				body1.SetMassFromShapes();
		}
		
		/***************************************************/
		/*********************ETAPE 1***********************/
		/***************************************************/
		
		public function CheckPolyShape(poly:b2PolygonDef)
		{
			if (!(3 <= poly.vertexCount && poly.vertexCount <= b2_maxPolygonVertices)){
					return -1;
			}
			var m_normals:Array = new Array(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){
						i2 = i + 1;
					} else {
						i2 = 0;
					}
					
					var edge:b2Vec2 = poly.vertices[i2].Copy();
					edge.Subtract(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);
					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 = new b2Vec2(); 
				var area:Number = 0;
				var pRef:b2Vec2 = new b2Vec2(0.0, 0.0);
				var inv3:Number = 1 / 3;
		 		
				for (i=0; i  Number.MIN_VALUE)){
					return -1;
				}
				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)
					}
					i2 = i;
		
					var n1:b2Vec2 = m_normals[i1];
					var n2:b2Vec2 = m_normals[i2];
					var v:b2Vec2 = b2Math.SubtractVV(poly.vertices[i], m_centroid); // ?????
		 
					var d:b2Vec2 = new 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.
		 			
					//Those conditions are very annoying !!
				
					// Your shape has a radius/extent less than b2_toiSlop.
					/*if (!(d.x>= 0)){
						return -1;
					}
					if (!(d.y>= 0)){
						return -1;
					}*/
				}
			return 0;
				
		}
	
	 /// Split a shape trough a segment
    /// @return
    /// -1 - Error on split
    ///  0 - Normal result is two new shape definitions.
    public function SplitShape(shape:b2PolygonShape, segment, splitSize, newPolygon:Array)// ????
    {
        /*assert(shape != NULL);
        assert(newPolygon != NULL);
        assert(splitSize>= 0);*/
		
		if(shape == null)
			return -1;
			
		if(newPolygon == null)
			return -1;
			
		if(splitSize <= 0)
			return -1;
		
     	var lambda:Array = [1];
        var normal:b2Vec2 = new b2Vec2();
        
		var b:b2Body = shape.GetBody();
      	var xf:b2XForm = b.GetXForm();
        if (shape.TestSegment(xf, lambda, normal, segment, 1) != b2Shape.e_hitCollide){
            return -1;
		}
			
		var entryPoint:b2Vec2 = segment.p1.Copy();
		entryPoint.Multiply(1 - lambda[0]);
		var tmp:b2Vec2 = segment.p2.Copy();
		tmp.Multiply(lambda[0]);
		entryPoint.Add(tmp);			
			 
        var reverseSegment:b2Segment= new b2Segment;
        reverseSegment.p1 = segment.p2;
        reverseSegment.p2 = segment.p1;

        if (shape.TestSegment(xf, lambda, normal, reverseSegment, 1.0) != e_hitCollide){
            return -1;
		}
		
		var exitPoint:b2Vec2 = reverseSegment.p1.Copy();
		exitPoint.Multiply(1 - lambda[0]);
		tmp = reverseSegment.p2.Copy();
		tmp.Multiply(lambda[0]);
		exitPoint.Add(tmp);		
        
        var localEntryPoint:b2Vec2 = b.GetLocalPoint(entryPoint);
        var localExitPoint:b2Vec2  = b.GetLocalPoint(exitPoint);
		
        var vertices:Array = 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
					if(cutAdded[0] != -1){
						return -1;
					}
                    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
					if(cutAdded[lastA] != -1){
						return -1;
					}
                    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)
            {
				offset =  b2Math.SubtractVV(newPolygon[n].vertices[cutAdded[n]-1],  newPolygon[n].vertices[cutAdded[n]])//Substitution
            }else{
				//Substitution
				offset = b2Math.SubtractVV(newPolygon[n].vertices[newPolygon[n].vertexCount-1], newPolygon[n].vertices[0]);
            }
            offset.Normalize();
			
			var aNewVec:b2Vec2 = b2Math.MulFV(splitSize, offset);
            newPolygon[n].vertices[cutAdded[n]] = b2Math.AddVV(newPolygon[n].vertices[cutAdded[n]], aNewVec);

            if (cutAdded[n] 

And this is the result:

Download the full source code with all needed libraries.

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