Customize Box2D debug draw and make it run even on Starling mobile apps

One Box2D feature which is really underestimated is the debug draw. When I started blogging about Box2D I wrote a post about debug draw, but recently when I tried to render debug draw in Starling – which shouldn’t be possible since Starling does not use flash.display.Sprite class – I googled a bit for it and found some workarounds that really scared me.

So, at first, here is how you can override the classic debug draw routine.

The example is taken from the post “creation of a Box2D hook like the one seen on iOS Mikey Hooks game” with highlighted lines to add.

Basically we create a custom debug class and override the orignal one:

package {
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.events.Event;
	import Box2D.Dynamics.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.*;
	import Box2D.Dynamics.Joints.*;
	public class Main extends Sprite {
		private var world:b2World=new b2World(new b2Vec2(0,10),true);
		private var worldScale:Number=30;
		private var hero:b2Body;
		private var distanceJoint:b2DistanceJoint;
		private var isHooked:Boolean=false;
		private var customDebug=new CustomDebug();
		public function Main() {
			addBox(320,480,640,20,b2Body.b2_staticBody);
			for (var i:Number=1; i<=12; i++) {
				addBox(Math.random()*600+20,Math.random()*300,Math.random()*30+15,Math.random()*30+15,b2Body.b2_staticBody);
			}
			hero=addBox(320,460,20,20,b2Body.b2_dynamicBody);
			addEventListener(Event.ENTER_FRAME,update);
			stage.addEventListener(MouseEvent.MOUSE_DOWN,fireHook);
			stage.addEventListener(MouseEvent.MOUSE_UP,releaseHook);
			debugDraw();
		}
		private function fireHook(e:MouseEvent):void {
			if (distanceJoint) {
				world.DestroyJoint(distanceJoint);
			}
			world.QueryPoint(queryCallback,new b2Vec2(mouseX/worldScale,mouseY/worldScale));
		}
		private function queryCallback(fixture:b2Fixture):Boolean {
			var touchedBody:b2Body=fixture.GetBody();
			if (touchedBody.GetType()==b2Body.b2_staticBody) {
				var distanceJointDef:b2DistanceJointDef=new b2DistanceJointDef();
				distanceJointDef.Initialize(hero,touchedBody,hero.GetWorldCenter(),new b2Vec2(mouseX/worldScale,mouseY/worldScale));
				distanceJointDef.collideConnected=true;
				distanceJoint=world.CreateJoint(distanceJointDef) as b2DistanceJoint;
				isHooked=true;
			}
			return false;
		}
		private function releaseHook(e:MouseEvent):void {
			if (distanceJoint) {
				world.DestroyJoint(distanceJoint);
			}
		}
		private function manageHook():void{
			if (isHooked) {
				hero.SetAwake(true)
				distanceJoint.SetLength(distanceJoint.GetLength()*0.99);
			}
		}		
		private function addBox(pX:Number,pY:Number,w:Number,h:Number,bodyType:Number):b2Body {
			var bodyDef:b2BodyDef=new b2BodyDef();
			bodyDef.position.Set(pX/worldScale,pY/worldScale);
			bodyDef.type=bodyType;
			var polygonShape:b2PolygonShape=new b2PolygonShape();
			polygonShape.SetAsBox(w/2/worldScale,h/2/worldScale);
			var fixtureDef:b2FixtureDef=new b2FixtureDef();
			fixtureDef.shape=polygonShape;
			fixtureDef.density=1;
			fixtureDef.restitution=0.4;
			fixtureDef.friction=0.5;
			var body:b2Body=world.CreateBody(bodyDef);
			body.CreateFixture(fixtureDef);
			return body;
		}
		private function debugDraw():void {
			var debugSprite:Sprite=new Sprite();
			addChild(debugSprite);
			customDebug.SetSprite(debugSprite);
			customDebug.SetDrawScale(worldScale);
			customDebug.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
			customDebug.SetFillAlpha(0.5);
			world.SetDebugDraw(customDebug);
		}
		private function update(e:Event):void {
			world.Step(1/30, 10, 10);
			manageHook();
			world.ClearForces();
			world.DrawDebugData();
		}
	}
}

And this is CustomDebug class:

package {

	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Dynamics.Contacts.*;
	import Box2D.Dynamics.*;
	import Box2D.Common.Math.*;
	import Box2D.Common.*;
	import flash.display.Sprite;

	public class CustomDebug extends b2DebugDraw {

		override public function DrawSolidPolygon(vertices:Vector.<b2Vec2>, vertexCount:int, color:b2Color):void {
			color = new b2Color(Math.random(),Math.random(),Math.random())
			var m_sprite:Sprite=this.GetSprite();
			var m_lineThickness:Number=this.GetLineThickness();
			var m_drawScale:Number=this.GetDrawScale();
			var m_alpha:Number=this.GetAlpha();
			var m_fillAlpha:Number=this.GetFillAlpha();
			m_sprite.graphics.lineStyle(m_lineThickness, color.color, m_alpha);
			m_sprite.graphics.moveTo(vertices[0].x * m_drawScale, vertices[0].y * m_drawScale);
			m_sprite.graphics.beginFill(color.color, m_fillAlpha);
			for (var i:int = 1; i < vertexCount; i++) {
				m_sprite.graphics.lineTo(vertices[i].x * m_drawScale, vertices[i].y * m_drawScale);
			}
			m_sprite.graphics.lineTo(vertices[0].x * m_drawScale, vertices[0].y * m_drawScale);
			m_sprite.graphics.endFill();

		}

		override public function DrawSegment(p1:b2Vec2, p2:b2Vec2, color:b2Color):void {
			color = new b2Color(Math.random(),Math.random(),Math.random())
			var m_sprite:Sprite=this.GetSprite();
			var m_lineThickness:Number=this.GetLineThickness();
			var m_drawScale:Number=this.GetDrawScale();
			var m_alpha:Number=this.GetAlpha();;
			m_sprite.graphics.lineStyle(m_lineThickness, color.color, m_alpha);
			m_sprite.graphics.moveTo(p1.x * m_drawScale, p1.y * m_drawScale);
			m_sprite.graphics.lineTo(p2.x * m_drawScale, p2.y * m_drawScale);

		}
		
	}
	
}

The result I wanted to give is some kind of psychedelic effect just to show you something, anyway you got the point:

You should know how to play: click and hold your mouse over a static body to create a hook and rewind it, release mouse button to destroy the hook. Try to swing like a tarzan geek.

And this is a video taken directly from my iPhone, how you can see it works, although it’s dropping a bit my frame rate, but since it’s just a debug, it does not matter:

And… yes, I am making a game out of it. Hoping not to get sued by Spiderman…

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

215 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
// Stairs
// 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