Alternativa3d 7 Flying carpet game Part 2 the terrain

David Jones
@david3jones
avatar-davidejones

Now if you followed the first part of the flying carpet tutorial you should have the first stage of the waving carpet. We now need to create the scene for the carpet to fly through and to do this i have decided to use the jiglib flash libraries to create terrain from height maps. A heightmap is an image usually made up of whites, greys and black and these represent different heights in 3d space. I created a basic tutorial on using the heightmaps jiglibflash and alternativa7 here. For this example i’m going to do something a little different and have actionscript create the heightmap for me. To do this i’m going to make use of the perlin noise function for bitmapdata.

var pmap:BitmapData = new BitmapData(500, 500);
var _seed:uint = Math.round(Math.random()*100);
pmap.perlinNoise(500,500, 6, _seed, true, false, 1, true);

As you can see in the code above its pretty easy to do and should create a bitmap looking like this. noise Now i actually want to have a flat piece of land at the beginning and end of this terrain so i’m going to create a new bitmapdata from this and add in the flat land which will just be a solid black. This way when i create multiple terrains i can join them seamlessly.

var pmap2:BitmapData = new BitmapData(500, 600,false,0x000000);
var rect:Rectangle = new Rectangle(0, 0, 500, 500);
var pt:Point = new Point(0, 50);
pmap2.copyPixels(pmap,rect,pt);

Here you can now see the 2 black areas which will be our flat land that we can connect together. noise Once we have our heightmap generated we can use the jiglibflash to create the terrain by using the following

terrain = physics.createTerrain(pmap2,MaterialTerrain, 500,600,40,11,11);

There are a few more steps to getting up and running with jiglibflash , if you are a little confused i suggest reading up on my previous post here which is a little more detailed or download the example files at the bottom of this post. Anyway now we have our terrain what i want to do is put this together with my carpet and have it move and update the terrain as we move. To do this i have setup 2 functions one called setupTerrain() and one called updateTerrain(). As you might expect the setupTerrain creates the initial section of terrain positions it and scales it to what is needed. Along with this is a variable called changeoverdistance which is used to increment every time a new piece of terrain is used. carpet

package
{
    	import alternativa.engine3d.core.Camera3D;
    	import alternativa.engine3d.core.Debug;
    	import alternativa.engine3d.core.Object3D;
    	import alternativa.engine3d.core.Object3DContainer;
    	import alternativa.engine3d.core.View;
    	import alternativa.engine3d.materials.FillMaterial;
    	import alternativa.engine3d.materials.TextureMaterial;
    	import alternativa.engine3d.containers.*;
    	import alternativa.engine3d.core.MipMapping;
    	import alternativa.engine3d.primitives.Sphere;
    	import alternativa.engine3d.primitives.Box;
    	import alternativa.engine3d.core.RayIntersectionData;
    	import alternativa.engine3d.core.MouseEvent3D;
    	import flash.display.Sprite;
    	import flash.display.StageAlign;
    	import flash.display.StageScaleMode;
    	import flash.events.Event;
    	import flash.display.BitmapData;
    	import flash.display.Bitmap;
    	import flash.geom.Rectangle;
    	import flash.geom.Point;
    	import flash.geom.Vector3D;
    	import jiglib.geometry.*;
    	import jiglib.physics.*;
    	import jiglib.math.*;
    	import net.glassesfactory.alt3dkit.jiglib.*;
    	import WavePlane;
    	import DesertSkyBox;
    	[SWF(backgroundColor="#000000", frameRate="100", width="800", height="600")]
    	public class test1 extends Sprite
    	{
    		[Embed(source="carpet.jpg")] static private const Carpet:Class;
    		[Embed(source="terraintex.jpg")] static private const Terraintex:Class;
    		private var rootContainer:Object3DContainer = new Object3DContainer();
    		private var terrainContainer:ConflictContainer = new ConflictContainer();
    		private var carpetContainer:Object3DContainer = new Object3DContainer();
    		private var camera:Camera3D;
    		private var terrain:JTerrain;
    		private var physics:GFAlternativa3DPhysics;
    		private var carpet:WavePlane;
    		private var MaterialCarpet:TextureMaterial;
    		private var MaterialTerrain:TextureMaterial;
    		private var sbox:DesertSkyBox;
    		private var changeoverdistance:int = 0;
    		private var pmap2:BitmapData;
    		public function test1():void
    		{
    			//setup stage
    			stage.align = StageAlign.TOP_LEFT;
    			stage.scaleMode = StageScaleMode.NO_SCALE;
    			//setup textures
    			MaterialCarpet = new TextureMaterial(new Carpet().bitmapData,false,true,MipMapping.PER_PIXEL);
    			MaterialTerrain = new TextureMaterial(new Terraintex().bitmapData,false,true,MipMapping.PER_PIXEL);
    			//setup camera
    			camera = new Camera3D();
    			camera.view = new View(stage.stageWidth, stage.stageHeight);
    			addChild(camera.view);
    			addChild(camera.diagram);
    			camera.y = -65;
    			camera.z = 10;
    			camera.lookAt(0,0,0);
    			carpetContainer.addChild(camera);
    			//setup skybox
    			sbox = new DesertSkyBox(20000);
    			rootContainer.addChild(sbox);
    			//setup physics and terrain
    			physics = new GFAlternativa3DPhysics(terrainContainer, 8);
    			//setup initial terrain
    			setupTerrain();
    			//setup carpet
    			setupCarpet();
    			// Listeners
    			stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
    			stage.addEventListener(Event.RESIZE, onResize);
    		}
    		private function setupCarpet():void
    		{
    			carpetContainer.y = 60;
    			carpetContainer.rotationX = -90*Math.PI/180;
    			carpetContainer.rotationY = -180*Math.PI/180;
    			carpet = new WavePlane(2,40,60,1,36,true,true,true,MaterialCarpet,MaterialCarpet);
    			carpet.z = -20;
    			carpetContainer.addChild(carpet);
    			carpet.scaleX = carpet.scaleY = carpet.scaleZ = 0.5;
    			rootContainer.addChild(carpetContainer);
    		}
    		private function setupTerrain():void
    		{
    			createHeightMap();
    			terrain = physics.createTerrain(pmap2,MaterialTerrain, 500,600,40,11,11);
    			terrain.restitution = 1;
    			GFAlt3dTerrain(terrain.terrainMesh).rotationY = -180*Math.PI/180;
    			GFAlt3dTerrain(terrain.terrainMesh).scaleY = GFAlt3dTerrain(terrain.terrainMesh).scaleX = GFAlt3dTerrain(terrain.terrainMesh).scaleZ = 2;
    			changeoverdistance = 1;
    			rootContainer.addChild(terrainContainer);
    		}
    		private function createHeightMap():void
    		{
    			var pmap:BitmapData = new BitmapData(500, 500);
    			var _seed:uint = Math.round(Math.random()*100);
    			pmap.perlinNoise(500,500, 6, _seed, true, false, 1, true);
    			//copy to a new bitmapdata so we can have a flat area
    			//at the start and end so we can seamlessly join them
    			pmap2 = new BitmapData(500, 600,false,0x000000);
    			var rect:Rectangle = new Rectangle(0, 0, 500, 500);
    			var pt:Point = new Point(0, 50);
    			pmap2.copyPixels(pmap,rect,pt);
    		}
    		private function updateTerrain():void
    		{
    			//add more terrain as we progress forward
    			if(carpetContainer.z >= (changeoverdistance * 1200) - 1500)
    			{
    				terrain = physics.createTerrain(pmap2,MaterialTerrain, 500,600,40,11,11);
    				//GFAlt3dTerrain(terrain.terrainMesh).rotationX = -90*Math.PI/180;
    				GFAlt3dTerrain(terrain.terrainMesh).rotationY = -180*Math.PI/180;
    				GFAlt3dTerrain(terrain.terrainMesh).z = changeoverdistance * 1200;
    				GFAlt3dTerrain(terrain.terrainMesh).scaleY = GFAlt3dTerrain(terrain.terrainMesh).scaleX = GFAlt3dTerrain(terrain.terrainMesh).scaleZ = 2;
    				changeoverdistance++;
    			}
    		}
    		private function onEnterFrame(e:Event):void
    		{
    			//always move carpet forward
    			carpetContainer.z += 2;
    			//add new terrain if needed
    			updateTerrain();
    			//integrate jiglib physics
    			physics.engine.integrate(0.1);
    			//render
    			camera.render();
    		}
    		private function onResize(e:Event = null):void {
    			camera.view.width = stage.stageWidth;
    			camera.view.height = stage.stageHeight;
    		}
    		private function randRange(minNum:Number, maxNum:Number):Number
    		{
    			return (Math.floor(Math.random() * (maxNum - minNum + 1)) + minNum);
    		}
    	}
}

To finish this off i have added a skybox around the whole scene so we have a better environment. I have used some sample skybox images from the website http://www.3delyvisions.com/skf1.htm

package
{
    	import alternativa.engine3d.objects.SkyBox;
    	import alternativa.engine3d.materials.TextureMaterial;
    	public class DesertSkyBox extends SkyBox
    	{
    		[Embed(source="backred2.jpg")] static private const backred:Class;
    		[Embed(source="frontred2.jpg")] static private const frontred:Class;
    		[Embed(source="leftred2.jpg")] static private const leftred:Class;
    		[Embed(source="rightred2.jpg")] static private const rightred:Class;
    		[Embed(source="topred2.jpg")] static private const topred:Class;
    		public function DesertSkyBox(size:Number):void
    		{
    			var left:TextureMaterial = new TextureMaterial(new leftred().bitmapData);
    			var right:TextureMaterial = new TextureMaterial(new rightred().bitmapData);
    			var back:TextureMaterial = new TextureMaterial(new backred().bitmapData);
    			var front:TextureMaterial = new TextureMaterial(new frontred().bitmapData);
    			//var bottom:TextureMaterial = new TextureMaterial(new backred().bitmapData);
    			var top:TextureMaterial = new TextureMaterial(new topred().bitmapData);
    			super(size, left, right, back, front, null, top, 0);
    			this.rotationX = -90*Math.PI/180;
    		}
    	}
}

You can download the source files here

Downloads

Comments

    Comments are currently closed