Talking about Splitter game, Actionscript 3, Box2D, Flash and Users contributions.
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://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.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.