Following a body with the camera in Box2D

If you want to make a good game, a good camera movement is necessary.

This time we’ll learn to follow a Box2D object with the camera. Box2D does not have a “camera”, so we will have to move the stage according to body position. This is the same script as Real world catapult using Box2D – cleaner code, I just placed a gradient background to help you seeing how the camera is following the objects.

The main idea is following the catapult until it fires the cannonball, then following the cannonball.

This is the result:

Left and right to move, spacebar to fire

And this is the code:

package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.*;
	import Box2D.Dynamics.Joints.*;
	public class main extends Sprite {
		public var world:b2World=new b2World(new b2Vec2(0,10.0),true);
		public var world_scale:int=30;
		var the_cannonball_itself:b2Body;
		var catapult_chassis_body:b2Body;
		var catapult_arm_body:b2Body;
		var rear_wheel_body:b2Body;
		var front_wheel_body:b2Body;
		var arm_revolute_joint:b2RevoluteJoint;
		var front_wheel_revolute_joint:b2RevoluteJoint;
		var rear_wheel_revolute_joint:b2RevoluteJoint;
		var left_key_pressed:Boolean=false;
		var right_key_pressed:Boolean=false;
		var following_catapult:Boolean=true;
		var bg:background_mc = new background_mc();
		public function main():void {
			addChild(bg);
			debug_draw();
			the_ground();
			the_catapult_body();
			the_catapult_arm();
			the_catapult_motor();
			the_wheels();
			the_wheel_motors();
			the_cannonball();
			addEventListener(Event.ENTER_FRAME, update);
			stage.addEventListener(KeyboardEvent.KEY_DOWN, key_down);
			stage.addEventListener(KeyboardEvent.KEY_UP, key_up);
		}
		public function the_cannonball():void {
			var cannonball:b2BodyDef= new b2BodyDef();
			cannonball.position.Set(90/world_scale, 90/world_scale);
			cannonball.type=b2Body.b2_dynamicBody;
			var ball:b2CircleShape=new b2CircleShape(10/world_scale);
			var cannonball_fixture:b2FixtureDef = new b2FixtureDef();
			cannonball_fixture.shape=ball;
			cannonball_fixture.friction=0.9;
			cannonball_fixture.density=20;
			cannonball_fixture.restitution=0.5;
			the_cannonball_itself=world.CreateBody(cannonball);
			the_cannonball_itself.CreateFixture(cannonball_fixture);
		}
		public function the_wheel_motors():void {
			var front_wheel_joint:b2RevoluteJointDef = new b2RevoluteJointDef();
			front_wheel_joint.enableMotor=true;
			front_wheel_joint.Initialize(catapult_chassis_body, front_wheel_body,new b2Vec2(0,0));
			front_wheel_joint.localAnchorA=new b2Vec2(80/world_scale,0);
			front_wheel_joint.localAnchorB=new b2Vec2(0,0);
			front_wheel_revolute_joint=world.CreateJoint(front_wheel_joint) as b2RevoluteJoint;
			front_wheel_revolute_joint.SetMaxMotorTorque(1000000);
			//
			var rear_wheel_joint:b2RevoluteJointDef = new b2RevoluteJointDef();
			rear_wheel_joint.enableMotor=true;
			rear_wheel_joint.Initialize(catapult_chassis_body, rear_wheel_body,new b2Vec2(0,0));
			rear_wheel_joint.localAnchorA=new b2Vec2(-80/world_scale,0);
			rear_wheel_joint.localAnchorB=new b2Vec2(0,0);
			rear_wheel_revolute_joint=world.CreateJoint(rear_wheel_joint) as b2RevoluteJoint;
			rear_wheel_revolute_joint.SetMaxMotorTorque(1000000);
		}
		public function the_wheels():void {
			var rear_wheel:b2BodyDef= new b2BodyDef();
			rear_wheel.position.Set(250/world_scale, 200/world_scale);
			rear_wheel.type=b2Body.b2_dynamicBody;
			var rear_wheel_shape:b2CircleShape=new b2CircleShape(40/world_scale);
			var rear_wheel_fixture:b2FixtureDef = new b2FixtureDef();
			rear_wheel_fixture.shape=rear_wheel_shape;
			rear_wheel_fixture.friction=0.9;
			rear_wheel_fixture.density=30;
			rear_wheel_fixture.restitution=0.1;
			rear_wheel_body=world.CreateBody(rear_wheel);
			rear_wheel_body.CreateFixture(rear_wheel_fixture);
			//
			var front_wheel:b2BodyDef= new b2BodyDef();
			front_wheel.position.Set(450/world_scale, 200/world_scale);
			front_wheel.type=b2Body.b2_dynamicBody;
			var front_wheel_shape:b2CircleShape=new b2CircleShape(40/world_scale);
			var front_wheel_fixture:b2FixtureDef = new b2FixtureDef();
			front_wheel_fixture.shape=front_wheel_shape;
			front_wheel_fixture.friction=0.9;
			front_wheel_fixture.density=30;
			front_wheel_fixture.restitution=0.1;
			front_wheel_body=world.CreateBody(front_wheel);
			front_wheel_body.CreateFixture(front_wheel_fixture);
		}
		public function the_catapult_motor():void {
			var arm_joint:b2RevoluteJointDef = new b2RevoluteJointDef();
			arm_joint.enableMotor=true;
			arm_joint.enableLimit=true;
			arm_joint.Initialize(catapult_chassis_body, catapult_arm_body,new b2Vec2(0,0));
			arm_joint.localAnchorA=new b2Vec2(-80/world_scale,-90/world_scale);
			arm_joint.localAnchorB=new b2Vec2(60/world_scale,0);
			arm_revolute_joint=world.CreateJoint(arm_joint) as b2RevoluteJoint;
			arm_revolute_joint.SetMotorSpeed(1000);
			arm_revolute_joint.SetLimits(-Math.PI,Math.PI/3);
			arm_revolute_joint.SetMaxMotorTorque(1);
		}
		public function the_catapult_arm():void {
			var catapult_arm:b2BodyDef = new b2BodyDef();
			catapult_arm.allowSleep=false;
			catapult_arm.position.Set(210/world_scale,110/world_scale);
			catapult_arm.type=b2Body.b2_dynamicBody;
			var arm_part:b2PolygonShape = new b2PolygonShape();
			arm_part.SetAsOrientedBox(150/world_scale, 10/world_scale, new b2Vec2(0,0),0);
			var arm_part_fixture:b2FixtureDef = new b2FixtureDef();
			arm_part_fixture.shape=arm_part;
			arm_part_fixture.friction=0.9;
			arm_part_fixture.density=5;
			arm_part_fixture.restitution=0.1;
			var stopper:b2PolygonShape = new b2PolygonShape();
			stopper.SetAsOrientedBox(10/world_scale, 20/world_scale, new b2Vec2(-140/world_scale,-30/world_scale),0);
			var stopper_fixture:b2FixtureDef = new b2FixtureDef();
			stopper_fixture.shape=stopper;
			stopper_fixture.friction=0.9;
			stopper_fixture.density=10;
			stopper_fixture.restitution=0.1;
			catapult_arm_body=world.CreateBody(catapult_arm);
			catapult_arm_body.CreateFixture(arm_part_fixture);
			catapult_arm_body.CreateFixture(stopper_fixture);
		}
		public function the_catapult_body():void {
			var catapult_body:b2BodyDef = new b2BodyDef();
			catapult_body.position.Set(350/world_scale,200/world_scale);
			catapult_body.type=b2Body.b2_dynamicBody;
			var main_part:b2PolygonShape = new b2PolygonShape();
			main_part.SetAsOrientedBox(125/world_scale, 20/world_scale, new b2Vec2(0,0),0);
			var chassis_fixture:b2FixtureDef = new b2FixtureDef();
			chassis_fixture.shape=main_part;
			chassis_fixture.friction=0.9;
			chassis_fixture.density=50;
			chassis_fixture.restitution=0.1;
			var fixed_arm:b2PolygonShape = new b2PolygonShape();
			fixed_arm.SetAsOrientedBox(20/world_scale, 60/world_scale, new b2Vec2(-80/world_scale,-40/world_scale),0);
			var fixed_arm_fixture:b2FixtureDef = new b2FixtureDef();
			fixed_arm_fixture.shape=fixed_arm;
			fixed_arm_fixture.friction=0.9;
			fixed_arm_fixture.density=1;
			fixed_arm_fixture.restitution=0.1;
			catapult_chassis_body=world.CreateBody(catapult_body);
			catapult_chassis_body.CreateFixture(chassis_fixture);
			catapult_chassis_body.CreateFixture(fixed_arm_fixture);
		}
		public function the_ground():void {
			var ground:b2BodyDef= new b2BodyDef();
			ground.position.Set(250/world_scale, 400/world_scale);
			var my_box:b2PolygonShape = new b2PolygonShape();
			my_box.SetAsBox(5000/world_scale, 15/world_scale);
			var ground_fixture:b2FixtureDef = new b2FixtureDef();
			ground_fixture.shape=my_box;
			ground_fixture.friction=0.9;
			ground_fixture.restitution=0.1;
			var the_ground_itself:b2Body=world.CreateBody(ground);
			the_ground_itself.CreateFixture(ground_fixture);
		}
		public function key_up(event:KeyboardEvent):void {
			switch (event.keyCode) {
				case 39 :
					right_key_pressed=false;
					break;
				case 37 :
					left_key_pressed=false;
					break;
			}
		}
		public function key_down(event:KeyboardEvent):void {
			switch (event.keyCode) {
				case 39 :
					right_key_pressed=true;
					left_key_pressed=false;
					break;
				case 37 :
					left_key_pressed=true;
					right_key_pressed=false;
					break;
				case 32 :
					arm_revolute_joint.SetMaxMotorTorque(10000);
					following_catapult=false;
					break;
			}
		}
		public function debug_draw():void {
			var debug_draw:b2DebugDraw = new b2DebugDraw();
			var debug_sprite:Sprite = new Sprite();
			addChild(debug_sprite);
			debug_draw.SetSprite(debug_sprite);
			debug_draw.SetDrawScale(world_scale);
			debug_draw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
			debug_draw.SetFillAlpha(0.5);
			world.SetDebugDraw(debug_draw);
		}
		public function set_motor_speed():void {
			var current_speed:Number;
			if (right_key_pressed) {
				current_speed=1;
			}
			if (left_key_pressed) {
				current_speed=-1;
			}
			if (! right_key_pressed&&! left_key_pressed) {
				current_speed=rear_wheel_revolute_joint.GetMotorSpeed()*0.9;
				if (Math.abs(current_speed)<0.1) {
					current_speed=0;
				}
			}
			rear_wheel_revolute_joint.SetMotorSpeed(current_speed);
			front_wheel_revolute_joint.SetMotorSpeed(current_speed);
		}
		public function update(e:Event):void {
			var pos_x:Number;
			var pos_y:Number;
			set_motor_speed();
			world.Step(1/30,10,10);
			if (following_catapult) {
				pos_x=catapult_chassis_body.GetWorldCenter().x*world_scale;
				pos_y=catapult_chassis_body.GetWorldCenter().y*world_scale;
			} else {
				pos_x=the_cannonball_itself.GetWorldCenter().x*world_scale;
				pos_y=the_cannonball_itself.GetWorldCenter().y*world_scale;
			}
			x=stage.stageWidth/2-pos_x;
			y=stage.stageHeight/2-pos_y;
			world.ClearForces();
			world.DrawDebugData();
		}
	}
}

Download the source code. Next time, smarter camera movement and fully commented code.

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