Talking about Slingy game, Actionscript 3, Box2D, Flash, Game development and Monetize.
More than three years ago I released Slingy game, sponsored by Hairy Games.
The game did not have the success I expected, but today I played it again and I thought it would be nice to have a similar game on a mobile device.
So I am giving you the source code of the game, with a lot of outdated libraries like Mochi Media and Playtomic APIs, but you will find the game engine, powered by Box2D, quite interesting, have a look at the main class:
package triqui{ import flash.display.Sprite; import flash.events.MouseEvent; import flash.events.Event; import flash.events.KeyboardEvent; import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Dynamics.Contacts.*; import Box2D.Common.Math.*; public class Game extends Sprite { private var level:Number; private var newMouse:NewMouse; private var ball:Ball; private var world:b2World=new b2World(new b2Vec2(0,10.0),true); private var worldScale:Number=30; private var levels:Levels=new Levels(); private var ballBody:b2Body; private var totalCoins:Number=0; private var launchedBalls:Number=0; private var totalBalls:Number=5; private var collectedCoins:Number=0; private var comboCoins:Number; private var removeTexts:Boolean=false; private var levelCompleted:Boolean=false; private var starsToGive:Number=0; private var levelScore:Number=0; private var neededToComplete:Array=new Array(50,/*2*/30,/*3*/10,/*4*/30,/*5*/40,/*6*/10,/*7*/25,/*8*/20,/*9*/30,/*10*/30,/*11*/55,/*12*/45,/*13*/10,/*14*/25,/*15*/15,/*16*/35,/*17*/20,/*18*/15,/*19*/10,/*20*/20,/*21*/50,/*22*/5,/*23*/25,/*24*/40,/*25*/20,/*26*/15,/*27*/40,/*28*/25,/*29*/40,/*30*/25,/*31*/40,/*32*/25,/*33*/30,/*34*/25,/*35*/35,/*36*/20,/*37*/35,/*38*/30,/*39*/25,/*40*/10); private var starScores:Array=new Array(5000,/*2*/5400,/*3*/2100,/*4*/2500,/*5*/3200,/*6*/400,/*7*/1200,/*8*/4400,/*9*/3100,/*10*/2700,/*11*/6500,/*12*/10800,/*13*/900,/*14*/2000,/*15*/4500,/*16*/3700,/*17*/700,/*18*/300,/*19*/1000,/*20*/1900,/*21*/9900,/*22*/200,/*23*/2300,/*24*/8500,/*25*/1100,/*26*/2000,/*27*/3500,/*28*/5300,/*29*/3400,/*30*/2400,/*31*/8400,/*32*/2600,/*33*/3000,/*34*/2700,/*35*/4600,/*36*/1100,/*37*/3800,/*38*/2200,/*39*/2380,/*40*/1400); public var circle:Circle; public var crosshair:Crosshair; public var ballTrail:BallTrail; public var launchedBall:LaunchedBall; public function Game(n:Number) { world.SetContactListener(new CustomContact()); level=n; addEventListener(Event.ADDED_TO_STAGE,init); } private function init(e:Event):void { addChild(levels); levels.gotoAndStop(level+1); for (var i:int=0; i<levels.numChildren; i++) { with (levels.getChildAt(i)) { switch (name) { case "theCoin" : addCoin(x,y); levels.removeChildAt(i); i--; totalCoins++; break; case "theWall" : addWall(x,y); levels.removeChildAt(i); i--; break; case "thePushable" : addPushable(x,y); levels.removeChildAt(i); i--; break; case "theOneWayU" : addOneWayU(x,y); levels.removeChildAt(i); i--; break; case "theCircle" : addStuff(x,y); levels.removeChildAt(i); i--; break; case "theBreakable" : addBreakable(x,y); levels.removeChildAt(i); i--; break; case "theAbsorbing" : addAbsorbing(x,y); levels.removeChildAt(i); i--; break; case "theKiller" : addKiller(x,y); levels.removeChildAt(i); i--; break; } } } stage.addEventListener(MouseEvent.MOUSE_DOWN,fire); stage.addEventListener(KeyboardEvent.KEY_DOWN,destroyBall); addEventListener(Event.ENTER_FRAME,update); } private function addStuff(pX:Number,pY:Number):void { circle=new Circle(totalBalls-launchedBalls); addChild(circle); circle.x=pX; circle.y=pY; circle.goalText.text=collectedCoins+"/"+neededToComplete[level]; circle.scoreText.text=levelScore.toString(); circle.star1.alpha=0.3; circle.star2.alpha=0.3; circle.star3.alpha=0.3; if (collectedCoins>=neededToComplete[level]) { circle.star1.alpha=1; if (levelScore>=starScores[level]*0.8) { circle.star2.alpha=1; if (levelScore>=starScores[level]) { circle.star3.alpha=1; } } } newMouse=new NewMouse(); addChild(newMouse); crosshair=new Crosshair(); addChild(crosshair); ball=new Ball(); addChild(ball); } private function addWall(pX:Number,pY:Number):void { var wallShape:b2PolygonShape = new b2PolygonShape(); wallShape.SetAsBox(20/worldScale,20/worldScale); var wallFixture:b2FixtureDef = new b2FixtureDef(); wallFixture.density=0; wallFixture.friction=10; wallFixture.restitution=0.6; wallFixture.shape=wallShape; var wallBodyDef:b2BodyDef = new b2BodyDef(); wallBodyDef.position.Set(pX/worldScale,pY/worldScale); wallBodyDef.userData=new Wall(); wallBodyDef.userData.name="wall"; addChild(wallBodyDef.userData); var wall:b2Body=world.CreateBody(wallBodyDef); wall.CreateFixture(wallFixture); } private function addBreakable(pX:Number,pY:Number):void { var breakableShape:b2PolygonShape = new b2PolygonShape(); breakableShape.SetAsBox(20/worldScale,20/worldScale); var breakableFixture:b2FixtureDef = new b2FixtureDef(); breakableFixture.density=0; breakableFixture.friction=10; breakableFixture.restitution=0.6; breakableFixture.shape=breakableShape; var breakableBodyDef:b2BodyDef = new b2BodyDef(); breakableBodyDef.position.Set(pX/worldScale,pY/worldScale); breakableBodyDef.userData=new Breakable(); breakableBodyDef.userData.name="breakable"; addChild(breakableBodyDef.userData); var breakable:b2Body=world.CreateBody(breakableBodyDef); breakable.CreateFixture(breakableFixture); } private function addKiller(pX:Number,pY:Number):void { var killerShape:b2PolygonShape = new b2PolygonShape(); killerShape.SetAsBox(20/worldScale,20/worldScale); var killerFixture:b2FixtureDef = new b2FixtureDef(); killerFixture.density=0; killerFixture.friction=10; killerFixture.restitution=0.6; killerFixture.shape=killerShape; var killerBodyDef:b2BodyDef = new b2BodyDef(); killerBodyDef.position.Set(pX/worldScale,pY/worldScale); killerBodyDef.userData=new Killer(); killerBodyDef.userData.name="killer"; addChild(killerBodyDef.userData); var killer:b2Body=world.CreateBody(killerBodyDef); killer.CreateFixture(killerFixture); } private function addAbsorbing(pX:Number,pY:Number):void { var absorbingShape:b2PolygonShape = new b2PolygonShape(); absorbingShape.SetAsBox(20/worldScale,20/worldScale); var absorbingFixture:b2FixtureDef = new b2FixtureDef(); absorbingFixture.density=10; absorbingFixture.friction=1; absorbingFixture.restitution=0.1; absorbingFixture.shape=absorbingShape; var absorbingBodyDef:b2BodyDef = new b2BodyDef(); absorbingBodyDef.position.Set(pX/worldScale,pY/worldScale); absorbingBodyDef.userData=new Absorbing(); absorbingBodyDef.userData.name="absorbing"; addChild(absorbingBodyDef.userData); var absorbing:b2Body=world.CreateBody(absorbingBodyDef); absorbing.CreateFixture(absorbingFixture); } private function addOneWayU(pX:Number,pY:Number):void { var oneWayUShape:b2PolygonShape = new b2PolygonShape(); oneWayUShape.SetAsBox(20/worldScale,20/worldScale); var oneWayUFixture:b2FixtureDef = new b2FixtureDef(); oneWayUFixture.density=0; oneWayUFixture.friction=10; oneWayUFixture.restitution=0.6; oneWayUFixture.shape=oneWayUShape; var oneWayUBodyDef:b2BodyDef = new b2BodyDef(); oneWayUBodyDef.position.Set(pX/worldScale,pY/worldScale); oneWayUBodyDef.userData=new OneWayU(); oneWayUBodyDef.userData.name="oneWayU"; addChild(oneWayUBodyDef.userData); var oneWayU:b2Body=world.CreateBody(oneWayUBodyDef); oneWayU.CreateFixture(oneWayUFixture); } private function addPushable(pX:Number,pY:Number):void { var pushableShape:b2PolygonShape = new b2PolygonShape(); pushableShape.SetAsBox(20/worldScale,20/worldScale); var pushableFixture:b2FixtureDef = new b2FixtureDef(); pushableFixture.density=2; pushableFixture.friction=10; pushableFixture.restitution=0.5; pushableFixture.shape=pushableShape; var pushableBodyDef:b2BodyDef = new b2BodyDef(); pushableBodyDef.type=b2Body.b2_dynamicBody; pushableBodyDef.position.Set(pX/worldScale,pY/worldScale); pushableBodyDef.userData=new Pushable(); pushableBodyDef.userData.name="pushable"; addChild(pushableBodyDef.userData); var pushable:b2Body=world.CreateBody(pushableBodyDef); pushable.CreateFixture(pushableFixture); } private function addCoin(pX:Number,pY:Number):void { var coinBodyDef:b2BodyDef= new b2BodyDef(); coinBodyDef.userData=new Coin(); coinBodyDef.userData.name="coin"; addChild(coinBodyDef.userData); coinBodyDef.position.Set(pX/worldScale, pY/worldScale); var coinShape:b2CircleShape=new b2CircleShape(10/worldScale); var coinFixture:b2FixtureDef = new b2FixtureDef(); coinFixture.isSensor=true; coinFixture.shape=coinShape; var coinBody:b2Body=world.CreateBody(coinBodyDef); coinBody.CreateFixture(coinFixture); } private function fire(e:MouseEvent):void { removeTexts=true; if (ball!=null) { if (ball.x>20&&ball.y>20&&ball.x<620&&ball.y<460) { launchBall(ball.x,ball.y,8,ball.xSpeed,ball.ySpeed); } } } private function destroyBall(e:KeyboardEvent):void { if (ball==null && e.keyCode==32 && launchedBalls<=totalBalls) { launchedBall.removeTimer(); destroyLaunchedBall(); } if (ball==null && e.keyCode==81 && launchedBalls<=totalBalls) { launchedBall.removeTimer(); launchedBalls=totalBalls; destroyLaunchedBall(); } } private function launchBall(pX:Number,pY:Number,r:Number,sX:Number,sY:Number):void { comboCoins=0; launchedBalls++; removeChild(ball); ball=null; var ballBodyDef:b2BodyDef= new b2BodyDef(); launchedBall =new LaunchedBall(); ballBodyDef.userData=launchedBall; ballBodyDef.userData.name="ball"; var force:b2Vec2=new b2Vec2(sX,sY); ballBodyDef.position.Set(pX/worldScale, pY/worldScale); ballBodyDef.type=b2Body.b2_dynamicBody; var ballShape:b2CircleShape=new b2CircleShape(r/worldScale); var ballFixture:b2FixtureDef = new b2FixtureDef(); ballFixture.restitution=0.5; ballFixture.friction=0; ballFixture.density=4; ballFixture.shape=ballShape; ballBody=world.CreateBody(ballBodyDef); ballBody.SetBullet(true); ballBody.SetLinearVelocity(force); ballBody.CreateFixture(ballFixture); ballTrail=new BallTrail(); addChild(ballTrail); addChild(ballBodyDef.userData); } public function destroyLaunchedBall():void { world.DestroyBody(ballBody); removeChild(launchedBall); ballTrail.destroy(); var explosion:Explosion = new Explosion(); addChild(explosion); explosion.x=launchedBall.x; explosion.y=launchedBall.y; if (launchedBalls<totalBalls) { var saveX:Number=circle.x; var saveY:Number=circle.y; removeChild(circle); removeChild(newMouse); removeChild(crosshair); addStuff(saveX,saveY); } else { stage.removeEventListener(MouseEvent.MOUSE_DOWN,fire); stage.removeEventListener(KeyboardEvent.KEY_DOWN,destroyBall); levelCompleted=true; } } private function update(e:Event):void { if (removeTexts && levels.alpha>0) { levels.alpha-=0.05; } world.Step(1/30,10,10); for (var bb:b2Body = world.GetBodyList(); bb; bb = bb.GetNext()) { if (bb.GetUserData() is Sprite) { bb.GetUserData().x=bb.GetPosition().x*worldScale; bb.GetUserData().y=bb.GetPosition().y*worldScale; bb.GetUserData().rotation=bb.GetAngle()*(180/Math.PI); if (bb.GetUserData().name=="removeBall") { launchedBall.removeTimer(); world.DestroyBody(bb); destroyLaunchedBall(); } if (bb.GetUserData().name=="removeCoin") { comboCoins++; levelScore+=(comboCoins*10); collectedCoins++; if (collectedCoins>=neededToComplete[level]) { circle.star1.alpha=1; if (levelScore>=starScores[level]*0.8) { circle.star2.alpha=1; if (levelScore>=starScores[level]) { circle.star3.alpha=1; } } } circle.goalText.text=collectedCoins+"/"+neededToComplete[level]; circle.scoreText.text=levelScore.toString(); var explodingCoin:ExplodingCoin=new ExplodingCoin(); addChild(explodingCoin); explodingCoin.x=bb.GetUserData().x; explodingCoin.y=bb.GetUserData().y; var points:Points=new Points(); addChild(points); points.x=bb.GetUserData().x; points.y=bb.GetUserData().y; points.PointsText.text=(comboCoins*10).toString(); removeChild(bb.GetUserData()); world.DestroyBody(bb); if (collectedCoins==totalCoins && launchedBalls!=totalBalls) { stage.removeEventListener(MouseEvent.MOUSE_DOWN,fire); stage.removeEventListener(KeyboardEvent.KEY_DOWN,destroyBall); launchedBall.removeTimer(); levelCompleted=true; } } if (bb.GetUserData().name=="broken") { removeChild(bb.GetUserData()); world.DestroyBody(bb); var explodingWall:ExplodingWall=new ExplodingWall(); addChild(explodingWall); explodingWall.x=bb.GetUserData().x; explodingWall.y=bb.GetUserData().y; } } } world.ClearForces(); if (levelCompleted) { alpha-=0.04; if (alpha<=0) { removeEventListener(Event.ENTER_FRAME,update); var theParent:Main=this.parent as Main; if (collectedCoins>=neededToComplete[level]) { starsToGive=1; if (levelScore>=starScores[level]*0.8) { starsToGive=2; if (levelScore>=starScores[level]) { starsToGive=3; } } } theParent.endLevel(level,collectedCoins>=neededToComplete[level],starsToGive,levelScore); } } } } }
Moreover, I will make a Box2D Phaser prototype soon, to see if it can be nicely played on mobile like I think.
So, download the source code and enjoy.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.