Create a terrain like the one in Tiny Wings with Flash and Box2D

Read all posts about "" game

Do you know Tiny Wings?

It’s a cute and addictive game for iPhone featuring a bird with tiny wings – too tiny to fly – which tries to fly anyway using a procedural generated terrain as a ramp.

What makes the game interesting is the physics response of the bird running along slopes.

At a first glance it seems the author is using some kind of physics engine which allows the creation of curves. But at a closer look (or playing on an iPad with zoom mode) we can see hills are made by a series of segments.

So we are going to reproduce such hills with Box2D. But before we dive into physics, some theory.

Tiny Wings hills are generated by trigonometric functions. The more complex the functions, the more interesting the result. In my example I am using a simple cosine, but feel free to try your own formulas and send me your results. I will be happy to publish them.

Once you defined your function, you’ll obviously end with a curve.

The trick is to divide such curve into an amount of slices, having the curve made of segments rather than points.

The higher the number of segments, the better will look the curve, the more CPU consuming will be the game.

In this example, I am assuming I want two hills in a 640×480 stage, with a slice width of 10 pixels.

I won’t use any physics engine, I will just draw two hills:

package {
	import flash.display.Sprite;
	import flash.geom.Point;
	import flash.events.MouseEvent;
	public class Main extends Sprite {
		public function Main() {
			drawHills(2,10);
			stage.addEventListener(MouseEvent.CLICK,mouseClicked);
		}
		private function mouseClicked(e:MouseEvent):void{
			graphics.clear();
			drawHills(2,10);
		}
		// this is the core function: drawHills
		// arguments: the number of hills to generate, and the horizontal step, in pixels, between two hill points
		private function drawHills(numberOfHills:int,pixelStep:int):void{
			// setting a starting y coordinate, around the vertical center of the stage
			var hillStartY:Number=140+Math.random()*200;
			// defining hill width, in pixels, that is the stage width divided by the number of hills
			var hillWidth:Number=640/numberOfHills;
			// defining the number of slices of the hill. This number is determined by the width of the hill in pixels divided by the amount of pixels between two points
			var hillSlices=hillWidth/pixelStep;
			// drawing stuff
			graphics.lineStyle(0,0xAAAAAA);
			graphics.moveTo(0,480);
			// looping through the hills
			for (var i:int=0; i

The script is quite simple and it's fully commented, and produces this result:

Click on the movie to generate new hills.

Now it's time to achieve the same results using Box2D. We can say slices are polygons made by four vertices, and we know the coordinates of all vertices, so we just have to create a series of static bodies built starting from such vertices.

To create the polygons, we'll use the same concept seen in the second part of the slicing tutorial, storing vertices in a Vector, finding polygon centroid starting from such Vector and finally drawing it and placing in the right place.

And this is the script:

package {
	import flash.display.Sprite;
	import flash.geom.Point;
	import flash.events.MouseEvent;
	import flash.events.Event;
	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.*;
	public class Main extends Sprite {
		private var world:b2World=new b2World(new b2Vec2(0,10),true);
		private var worldScale:int=30;
		var worldDebugDraw:b2DebugDraw;
		public function Main() {
			debugDraw();
			drawHills(2,10);
			stage.addEventListener(MouseEvent.CLICK,mouseClicked);
			addEventListener(Event.ENTER_FRAME,updateWorld);
		}
		private function mouseClicked(e:MouseEvent):void{
			drawHills(2,10);
		}
		private function debugDraw():void {
			worldDebugDraw=new b2DebugDraw();
			var debugSprite:Sprite = new Sprite();
			addChild(debugSprite);
			worldDebugDraw.SetSprite(debugSprite);
			worldDebugDraw.SetDrawScale(worldScale);
			worldDebugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
			worldDebugDraw.SetFillAlpha(0.5);
		}
		private function drawHills(numberOfHills:int,pixelStep:int):void{
			world=new b2World(new b2Vec2(0,10),true);
			world.SetDebugDraw(worldDebugDraw);
			var hillStartY:Number=140+Math.random()*200;
			var hillWidth:Number=640/numberOfHills;
			var hillSliceWidth=hillWidth/pixelStep;
			var hillVector:Vector.;
			for (var i:int=0; i();
						hillVector.push(new b2Vec2((j*pixelStep+hillWidth*i)/worldScale,480/worldScale));
						hillVector.push(new b2Vec2((j*pixelStep+hillWidth*i)/worldScale,(hillStartY+randomHeight*Math.cos(2*Math.PI/hillSliceWidth*j))/worldScale));
						hillVector.push(new b2Vec2(((j+1)*pixelStep+hillWidth*i)/worldScale,(hillStartY+randomHeight*Math.cos(2*Math.PI/hillSliceWidth*(j+1)))/worldScale));
						hillVector.push(new b2Vec2(((j+1)*pixelStep+hillWidth*i)/worldScale,480/worldScale));
						var sliceBody:b2BodyDef=new b2BodyDef;
						var centre:b2Vec2=findCentroid(hillVector,hillVector.length);
						sliceBody.position.Set(centre.x,centre.y);
						for(var z:int=0;z, count:uint):b2Vec2 {
			var c:b2Vec2 = new b2Vec2();
			var area:Number=0.0;
			var p1X:Number=0.0;
			var p1Y:Number=0.0;
			var inv3:Number=1.0/3.0;
			for (var i:int = 0; i < count; ++i) {
				var p2:b2Vec2=vs[i];
				var p3:b2Vec2=i+1

The concept is the same of the previous script, we are just building Box2D polygons rather than drawing them on the stage.

And this is the result:

Click on the movie to generate new hills.

Now I have two questions:

1) How would you produce more various shaped hills?

2) How would you scroll/zoom the hills?

Think, meanwhile download the full source 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