Create a Flash prototype of The Moops – Combos of Joy – Step 2

Read all posts about "" game

Here we are with the 2nd part of the tutorial to create a game like The Moops. The guys at Heavy Boat, the studio behind the game, are following the tutorial too, so I hope I’ll try to make something really similar to their game.

In the first part I showed you how to fire the ball and hit squares, now it’s time to remove hit squares after a certain amount of time. There’s nothing new as we only have to work on contact listeners as explained in this post.

Let’s see the main class:

package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.utils.Timer;
	import flash.events.TimerEvent;
	import Box2D.Dynamics.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.*;
	public class moops extends Sprite {
		private var world:b2World=new b2World(new b2Vec2(0,0),true);
		private var worldScale:int=30;
		private var timeCount:Timer=new Timer(500);
		private var contactListener=new customContactListener();
		public function moops():void {
			world.SetContactListener(contactListener);
			timeCount.start();
			debugDraw();
			addEventListener(Event.ENTER_FRAME, update);
			stage.addEventListener(MouseEvent.CLICK,onClick);
			timeCount.addEventListener(TimerEvent.TIMER, onTime);
		}
		private function onClick(e:MouseEvent):void {
			var angle:Number=Math.atan2(mouseX,mouseY-480)-Math.PI/2;
			addball(20*Math.cos(angle),-20*Math.sin(angle));
		}
		private function onTime(event:TimerEvent):void {
			addBox(300+Math.random()*200,-100,40);
		}
		private function addball(xVel:Number,yVel:Number):void {
			var ball:b2BodyDef= new b2BodyDef();
			ball.userData=new Object();
			ball.userData.name="ball";
			ball.type=b2Body.b2_dynamicBody;
			ball.position.Set(20/worldScale, 460/worldScale);
			var circle:b2CircleShape=new b2CircleShape(15/worldScale);
			var ballFixture:b2FixtureDef = new b2FixtureDef();
			ballFixture.shape=circle;
			ballFixture.friction=0;
			ballFixture.density=1;
			ballFixture.restitution=1;
			var ballBody:b2Body=world.CreateBody(ball);
			ballBody.CreateFixture(ballFixture);
			ballBody.SetLinearVelocity(new b2Vec2(xVel,yVel));
		}
		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 addBox(xOrigin:Number,yOrigin:Number,size:Number):void {
			var box:b2BodyDef= new b2BodyDef();
			box.userData=new Object();
			box.userData.name="box";
			box.userData.stamina=50;
			box.userData.hit=false;
			box.position.Set(xOrigin/worldScale,yOrigin/worldScale);
			box.type=b2Body.b2_dynamicBody;
			var square:b2PolygonShape = new b2PolygonShape();
			square.SetAsBox(size/2/worldScale, size/2/worldScale);
			var boxFixture:b2FixtureDef = new b2FixtureDef();
			boxFixture.shape=square;
			boxFixture.friction=0;
			boxFixture.density=4;
			boxFixture.restitution=1;
			var boxBody:b2Body=world.CreateBody(box);
			boxBody.CreateFixture(boxFixture);
			boxBody.SetLinearVelocity(new b2Vec2(0,5));
		}
		private function update(e:Event):void {
			world.Step(1/30,10,10);
			world.ClearForces();
			world.DrawDebugData();
			for (var currentBody:b2Body = world.GetBodyList(); currentBody; currentBody=currentBody.GetNext()) {
				if (currentBody.GetPosition().y*worldScale>600 || currentBody.GetPosition().y*worldScale<(100*-1)) {
					world.DestroyBody(currentBody);
				}
				if (currentBody.GetUserData()!=null&&currentBody.GetUserData().name=="ball") {
					var velocity:b2Vec2=currentBody.GetLinearVelocity();
					if (velocity.Length()!=20) {
						var speedOffset:Number=20/velocity.Length();
						currentBody.SetLinearVelocity(new b2Vec2(velocity.x*speedOffset,velocity.y*speedOffset));
					}
				}
				if (currentBody.GetUserData()!=null&&currentBody.GetUserData().hit) {
					currentBody.GetUserData().stamina--;
					if (currentBody.GetUserData().stamina<=0) {
						world.DestroyBody(currentBody);
					}
				}
			}
		}
	}
}

Let's examine lines 14 and 16:

private var contactListener=new customContactListener();

First I need to create a new custom contact listener, then I have to bind it to the world to use it in the simulation.

world.SetContactListener(contactListener);

At this time we need to add some custom properties to boxes we are creating:

private function addBox(xOrigin:Number,yOrigin:Number,size:Number):void {
	var box:b2BodyDef= new b2BodyDef();
	box.userData=new Object();
	box.userData.name="box";
	box.userData.stamina=50;
	box.userData.hit=false;
	box.position.Set(xOrigin/worldScale,yOrigin/worldScale);
	box.type=b2Body.b2_dynamicBody;
	var square:b2PolygonShape = new b2PolygonShape();
	square.SetAsBox(size/2/worldScale, size/2/worldScale);
	var boxFixture:b2FixtureDef = new b2FixtureDef();
	boxFixture.shape=square;
	boxFixture.friction=0;
	boxFixture.density=4;
	boxFixture.restitution=1;
	var boxBody:b2Body=world.CreateBody(box);
	boxBody.CreateFixture(boxFixture);
	boxBody.SetLinearVelocity(new b2Vec2(0,5));
}

From lines 58 to 61 I'm attaching an Object to the box, whose properties are the name, the stamina and a Boolean variable called hit.

Now things will work this way: if a box is hit by the ball or by another box, that is if the box collides with any other body in the stage, hit is set to true and if hit is true its stamina will be decreased at every frame. Once the stamina reaches zero, the box is removed.

This routine will be placed in update function:

private function update(e:Event):void {
	world.Step(1/30,10,10);
	world.ClearForces();
	world.DrawDebugData();
	for (var currentBody:b2Body = world.GetBodyList(); currentBody; currentBody=currentBody.GetNext()) {
		if (currentBody.GetPosition().y*worldScale>600 || currentBody.GetPosition().y*worldScale<(100*-1)) {
			world.DestroyBody(currentBody);
		}
		if (currentBody.GetUserData()!=null&&currentBody.GetUserData().name=="ball") {
			var velocity:b2Vec2=currentBody.GetLinearVelocity();
			if (velocity.Length()!=20) {
				var speedOffset:Number=20/velocity.Length();
				currentBody.SetLinearVelocity(new b2Vec2(velocity.x*speedOffset,velocity.y*speedOffset));
			}
		}
		if (currentBody.GetUserData()!=null&&currentBody.GetUserData().hit) {
			currentBody.GetUserData().stamina--;
			if (currentBody.GetUserData().stamina<=0) {
				world.DestroyBody(currentBody);
			}
		}
	}
}

Lines 90-95 deal with stamina.

And that's enough for main class, now we just have to extend the built in b2ContactListener
in a file called customContactListener.as.

package {
	import Box2D.Dynamics.*;
	import Box2D.Dynamics.Contacts.*;
	class customContactListener extends b2ContactListener {
		override public function BeginContact(contact:b2Contact):void {
			var fixtureA:b2Fixture=contact.GetFixtureA();
			var fixtureB:b2Fixture=contact.GetFixtureB();
			if(fixtureA.GetBody().GetUserData().name=="box"){
				fixtureA.GetBody().GetUserData().hit=true;
			}
			if(fixtureB.GetBody().GetUserData().name=="box"){
				fixtureB.GetBody().GetUserData().hit=true;
			}
		}
	}
}

The new class just set hit property to true if a box gets hit.

This is the result:

Mouse click to shoot the ball, try to hit a box and make the box hit more boxes, and see what happens.

Download the source code. Next time, we'll add some graphics.

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