How to create destructible terrain using Box2D – step 1

After I showed you various examples of destructible terrain created with Box2D (look here, here and here), it’s time to show you the full tutorial to create destructible terrain.

In this first part I won’t talk about Box2D, I am just showing you how to create a destructible terrain made of polygons.

The principle of the process is divide et impera. Since it would be complicated to manage a polygon with an hole in it, I am going to split a big polygon (the terrain) into a set of small polygons. How small? Small enough the hole made by an explosion can NEVER be completely inside them. This way I will never have real holes in my terrain.

Then it’s easy to use PolygonClipper class to manage polygon breaking.

Here is the script:

package {
	import flash.display.Sprite;
	import flash.geom.Point;
	import flash.events.MouseEvent;
	import com.logicom.geom.Clipper;
	import com.logicom.geom.ClipType;
	public class Main extends Sprite {
		// the canvas where we'll draw the terrain
		private var terrainCanvas:Sprite=new Sprite();
		// the array of polygons forming the terrain
		private var terrainPolygons:Array=new Array();
		public function Main():void {
			addChild(terrainCanvas);
			// creation of a 13x8 grid of squares, this will be our terrain
			for (var i:Number=0; i<13; i++) {
				for (var j:Number=0; j<8; j++) {
					var thePoly:Array = new Array(new Point(-5+i*50,80+j*50),new Point(45+i*50,80+j*50),new Point(45+i*50,130+j*50),new Point(-5+i*50,130+j*50));
					terrainPolygons.push(thePoly);
				}
			}
			// drawing the terrain
			drawTerrain();
			// listeners: basically we destroy the terrain with a mouse click or a mouse drag
			stage.addEventListener(MouseEvent.MOUSE_DOWN,function(){stage.addEventListener(MouseEvent.MOUSE_MOVE,doExplosion)});
			stage.addEventListener(MouseEvent.MOUSE_UP,function(){stage.removeEventListener(MouseEvent.MOUSE_MOVE,doExplosion)});
			stage.addEventListener(MouseEvent.CLICK,doExplosion);
		}
		// the core of the script, doExplosion function
		private function doExplosion(e:MouseEvent):void {
			// creation of an explosion polygon, looking like a circle, obviously it can be any shape you want
			var explosionPolygon:Array=createCircle(20,new Point(mouseX,mouseY),30);
			// for each existing terrain polygon, check the difference between the polygon itself and the 
			// explosion polygon. This should be optimized in some way, checking only for terrain polygons
			// which are actually affected by the explosion.
			// Then we remove the terrain polygon from the array, and we add the resulting polygon(s) after
			// difference is calculated.
			for (var i:Number=terrainPolygons.length-1; i>=0; i--) {
				var resultPolygons:Array=Clipper.clipPolygon(terrainPolygons[i],explosionPolygon,ClipType.DIFFERENCE);
				var totalArea:Number=0;
				terrainPolygons.splice(i,1);
				for (var j:Number=0; j<resultPolygons.length; j++) {
					terrainPolygons.push(resultPolygons[j]);
				}

			}
			// now it's time to redraw the terrain
			drawTerrain();
		}
		// function to create a "circular" polygon
		private function createCircle(precision:Number,origin:Point,radius:Number):Array {
			var angle:Number=2*Math.PI/precision;
			var circleArray:Array=new Array();
			for (var i:Number=0; i<precision; i++) {
				circleArray.push(new Point(origin.x+radius*Math.cos(angle*i),origin.y+radius*Math.sin(angle*i)));
			}
			return circleArray;
		}
		// these two remaining functions are not interesting, they just display the terrain
		private function drawTerrain() {
			terrainCanvas.graphics.clear();
			for (var i:Number=0; i<terrainPolygons.length; i++) {
				drawPolygon(terrainPolygons[i],terrainCanvas,0x0000FF);
			}
		}
		private function drawPolygon(polygon:Array,canvas:Sprite,color:Number):void {
			canvas.graphics.lineStyle(0.1,0xffffff);
			canvas.graphics.beginFill(color);
			var n:uint=polygon.length;
			if (n<3) {
				return;
			}
			var p:Point=polygon[0];
			canvas.graphics.moveTo(p.x, p.y);
			for (var i:Number = 1; i <= n; ++i) {
				p=polygon[i%n];
				canvas.graphics.lineTo(p.x, p.y);
			}
		}
	}
}

And this is the result:

Click to create a hole or drag to dig into the terrain.

Download the source code. Next time, we are going to add Box2D physics.

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