Get the full commented source code of

HTML5 Suika Watermelon Game

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.