Get the full commented source code of

HTML5 Suika Watermelon Game

Talking about Concentration game, Actionscript 3, Box2D and Flash.

There are a lot of Concentration games around the web, but… what happens when Box2D meets concentration? Tiles stack and fall down, moving from their starting position and making it harder to remember tile position.

Take this prototype:

It’s concentration powered by Box2D!!

How would you improve the game?

Look at the source code (uncommented – I’ll tell you later why…)

package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.*;
	public class Main extends Sprite {
		private var radToDeg:Number=57.2957795;
		private var world:b2World=new b2World(new b2Vec2(0,10),true);
		private var worldScale:Number=30;
		private var pickedTiles:int=0;
		private var tiles:Array=[2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18,19,19];
		private var pickedBodies:Array=new Array();
		private var idle:Timer;
		public function Main() {
			for (var i:int=1; i<=tiles.length*10; i++) {
				var from:int=Math.floor(Math.random()*tiles.length);
				var to:int=Math.floor(Math.random()*tiles.length);
				var temp:int=tiles[from];
				tiles[from]=tiles[to];
				tiles[to]=temp;
			}
			debugDraw();
			wall(320,470,600,20);
			wall(10,280,20,400);
			wall(630,280,20,400);
			for (i=0; i<6; i++) {
				for (var j:int=0; j<6; j++) {
					brick(170+60*i,120+60*j,60,60,j*6+i);
				}
			}
			addEventListener(Event.ENTER_FRAME,update);
			stage.addEventListener(MouseEvent.CLICK,destroyBrick);
		}
		private function wall(pX:Number,pY:Number,w:Number,h:Number):void {
			var bodyDef:b2BodyDef=new b2BodyDef();
			bodyDef.position.Set(pX/worldScale,pY/worldScale);
			var polygonShape:b2PolygonShape=new b2PolygonShape();
			polygonShape.SetAsBox(w/2/worldScale,h/2/worldScale);
			var fixtureDef:b2FixtureDef=new b2FixtureDef();
			fixtureDef.shape=polygonShape;
			fixtureDef.density=2;
			fixtureDef.restitution=0.4;
			fixtureDef.friction=0.5;
			var theWall:b2Body=world.CreateBody(bodyDef);
			theWall.CreateFixture(fixtureDef);
		}
		private function brick(pX:Number,pY:Number,w:Number,h:Number,val:int):void {
			var bodyDef:b2BodyDef=new b2BodyDef();
			bodyDef.position.Set(pX/worldScale,pY/worldScale);
			bodyDef.type=b2Body.b2_dynamicBody;
			bodyDef.userData=new Object();
			bodyDef.userData.tile=new Tile();
			bodyDef.userData.tile.buttonMode=true;
			bodyDef.userData.tile.gotoAndStop(1);
			addChild(bodyDef.userData.tile);
			bodyDef.userData.picked=false;
			bodyDef.userData.tileValue=val;
			var polygonShape:b2PolygonShape=new b2PolygonShape();
			polygonShape.SetAsBox(w/2/worldScale,h/2/worldScale);
			var fixtureDef:b2FixtureDef=new b2FixtureDef();
			fixtureDef.shape=polygonShape;
			fixtureDef.density=2;
			fixtureDef.restitution=0.4;
			fixtureDef.friction=0.5;
			var theWall:b2Body=world.CreateBody(bodyDef);
			theWall.CreateFixture(fixtureDef);
		}
		private function destroyBrick(e:MouseEvent):void {
			if (pickedTiles<2) {
				var pX:Number=mouseX/worldScale;
				var pY:Number=mouseY/worldScale;
				world.QueryPoint(queryCallback,new b2Vec2(pX,pY));
			}
		}
		private function queryCallback(fixture:b2Fixture):Boolean {
			var touchedBody:b2Body=fixture.GetBody();
			if (touchedBody.GetUserData()!=null && ! touchedBody.GetUserData().picked) {
				pickedBodies.push(touchedBody);
				pickedTiles++;
				touchedBody.GetUserData().picked=true;
				touchedBody.GetUserData().tile.gotoAndStop(tiles[touchedBody.GetUserData().tileValue]);
			}
			if (pickedTiles==2) {
				stage.removeEventListener(MouseEvent.CLICK,destroyBrick);
				idle=new Timer(1000);
				idle.start();
				idle.addEventListener(TimerEvent.TIMER, process);
			}
			return false;
		}
		private function process(e:TimerEvent):void {
			idle.removeEventListener(TimerEvent.TIMER, process);
			stage.addEventListener(MouseEvent.CLICK,destroyBrick);
			if (tiles[pickedBodies[0].GetUserData().tileValue]==tiles[pickedBodies[1].GetUserData().tileValue]) {
				removeChild(pickedBodies[0].GetUserData().tile);
				removeChild(pickedBodies[1].GetUserData().tile);
				world.DestroyBody(pickedBodies[0]);
				world.DestroyBody(pickedBodies[1]);
			}
			else {
				pickedBodies[0].GetUserData().tile.gotoAndStop(1);
				pickedBodies[1].GetUserData().tile.gotoAndStop(1);
				pickedBodies[0].GetUserData().picked=false;
				pickedBodies[1].GetUserData().picked=false;
			}
			pickedBodies=new Array();
			pickedTiles=0;
		}
		private function debugDraw():void {
			var debugDraw:b2DebugDraw = new b2DebugDraw();
			var debugSprite:Sprite = new Sprite();
			addChild(debugSprite);
			debugDraw.SetSprite(debugSprite);
			debugDraw.SetDrawScale(worldScale);
			debugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
			debugDraw.SetFillAlpha(0.5);
			world.SetDebugDraw(debugDraw);
		}
		private function update(e:Event):void {
			world.Step(1/30,10,10);
			world.ClearForces();
			for (var b:b2Body=world.GetBodyList(); b; b=b.GetNext()) {
				if (b.GetUserData()) {
					b.GetUserData().tile.x=b.GetPosition().x*worldScale;
					b.GetUserData().tile.y=b.GetPosition().y*worldScale;
					b.GetUserData().tile.rotation=b.GetAngle()*radToDeg;
				}
			}
			world.DrawDebugData();
		}
	}
}

A complete prototype in a few more than 100 lines, hope to see some good games out of it.

Download the source code.

Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.