Car Drive On Terrain

大型地图演示
See Demo
Heightmap:

Car Model File
Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
package
{
import flash.display.*;
import flash.events.*;
import flash.ui.Keyboard;
import flash.geom.Vector3D;

import org.papervision3d.cameras.SpringCamera3D;
import org.papervision3d.core.clipping.FrustumClipping;
import org.papervision3d.core.math.Number3D;
import org.papervision3d.materials.*;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.materials.shadematerials.FlatShadeMaterial;
import org.papervision3d.objects.parsers.*;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.lights.PointLight3D;
import org.papervision3d.events.FileLoadEvent;
import org.papervision3d.objects.parsers.Collada;
import org.papervision3d.view.BasicView;
import org.papervision3d.view.stats.StatsView;
import org.papervision3d.view.layer.ViewportLayer;
import org.papervision3d.view.layer.util.ViewportLayerSortMode;

import jiglib.cof.JConfig;
import jiglib.math.*;
import jiglib.physics.*;
import jiglib.geometry.*;
import jiglib.vehicles.JCar;
import jiglib.vehicles.JWheel;
import jiglib.plugin.papervision3d.*;

[SWF(width="800", height="600", backgroundColor="#ffffff", frameRate="60")]
public class CarDriveOnTerrain extends BasicView
{

[Embed(source="res/hightmap2.jpg")]
public var TERRIAN_MAP:Class;

private var springCamera:SpringCamera3D;
private var mylight:PointLight3D;

private var terrain:JTerrain;
private var carBody:JCar;
private var carSkin:Collada;
private var steerFR :DisplayObject3D;
private var steerFL :DisplayObject3D;
private var wheelFR :DisplayObject3D;
private var wheelFL :DisplayObject3D;
private var wheelBR :DisplayObject3D;
private var wheelBL :DisplayObject3D;
private var vplObjects:ViewportLayer;

private var physics:Papervision3DPhysics;

public function CarDriveOnTerrain()
{
super(800, 600, true, true);
stage.addEventListener( KeyboardEvent.KEY_DOWN, keyDownHandler );
stage.addEventListener( KeyboardEvent.KEY_UP, keyUpHandler );

init3D();
}

private function init3D():void
{
physics = new Papervision3DPhysics(scene, 8);

renderer.clipping = new FrustumClipping(FrustumClipping.BOTTOM | FrustumClipping.NEAR);
viewport.containerSprite.sortMode = ViewportLayerSortMode.INDEX_SORT;

springCamera = new SpringCamera3D();
springCamera.y = 5000;
springCamera.mass = 10;
springCamera.damping = 10;
springCamera.stiffness = 1;
springCamera.positionOffset = new Number3D(0, 100, -150);

mylight = new PointLight3D(true, true);
mylight.y = 5000;

var terrainBMD:Bitmap = new TERRIAN_MAP;
var shadeMateria:FlatShadeMaterial = new FlatShadeMaterial(mylight, 0x77ee77);

//create terrain
terrain = physics.createTerrain(terrainBMD.bitmapData, shadeMateria, 10000, 10000, 800, 30, 30);
viewport.getChildLayer(DisplayObject3D(terrain.terrainMesh)).layerIndex = 0;

//init car skin
shadeMateria = new FlatShadeMaterial(mylight,0xeeeeff);
var materiaList:MaterialsList = new MaterialsList();
materiaList.addMaterial(shadeMateria,"all");
carSkin = new Collada("res/car.DAE", materiaList, 0.01);
carSkin.addEventListener(FileLoadEvent.LOAD_COMPLETE,onCarLoaded);

springCamera.target = carSkin;

var stats:StatsView = new StatsView(renderer);
addChild(stats);
}

private function onCarLoaded(e:FileLoadEvent):void
{
scene.addChild(carSkin);

//init car physics
carBody = new JCar(new Pv3dMesh(carSkin));
carBody.setCar(40,5,600);
carBody.chassis.moveTo(new Vector3D( 4000, 100, -3000));
carBody.chassis.mass = 9;
carBody.chassis.sideLengths = new Vector3D(40, 20, 90);
physics.addBody(carBody.chassis);

carBody.setupWheel("WheelFL", new Vector3D( -20, -10, 25), 1.2, 1.2, 3, 10, 0.4, 0.6, 2);
carBody.setupWheel("WheelFR", new Vector3D(20, -10, 25), 1.2, 1.2, 3, 10, 0.4, 0.6, 2);
carBody.setupWheel("WheelBL", new Vector3D( -20, -10, -25), 1.2, 1.2, 3, 10, 0.4, 0.6, 2);
carBody.setupWheel("WheelBR", new Vector3D(20, -10, -25), 1.2, 1.2, 3, 10, 0.4, 0.6, 2);

var shadeMateria:FlatShadeMaterial = new FlatShadeMaterial(mylight, 0x777777);
steerFL = carSkin.getChildByName( "WheelFL", true );
steerFR = carSkin.getChildByName( "WheelFR", true );
wheelFL = carSkin.getChildByName( "WheelFL_PIVOT", true );
wheelFL.material = shadeMateria;
wheelFR = carSkin.getChildByName( "WheelFR_PIVOT", true );
wheelFR.material = shadeMateria;
wheelBL = carSkin.getChildByName( "WheelBL", true );
wheelBL.material = shadeMateria;
wheelBR = carSkin.getChildByName( "WheelBR", true );
wheelBR.material = shadeMateria;

var vplCar:ViewportLayer = viewport.getChildLayer(carSkin.getChildByName("Chassis",true));
vplCar.addDisplayObject3D(wheelFR);
vplCar.addDisplayObject3D(wheelFL);
vplCar.addDisplayObject3D(wheelBR);
vplCar.addDisplayObject3D(wheelBL);
vplCar.layerIndex = 2;

startRendering();
}

private function keyDownHandler(event :KeyboardEvent):void
{
switch(event.keyCode)
{
case Keyboard.UP:
carBody.setAccelerate(1);
break;
case Keyboard.DOWN:
carBody.setAccelerate(-1);
break;
case Keyboard.LEFT:
carBody.setSteer(["WheelFL", "WheelFR"], -1);
break;
case Keyboard.RIGHT:
carBody.setSteer(["WheelFL", "WheelFR"], 1);
break;
case Keyboard.SPACE:
carBody.setHBrake(1);
break;
}
}

private function keyUpHandler(event:KeyboardEvent):void
{
switch(event.keyCode)
{
case Keyboard.UP:
carBody.setAccelerate(0);
break;

case Keyboard.DOWN:
carBody.setAccelerate(0);
break;

case Keyboard.LEFT:
carBody.setSteer(["WheelFL", "WheelFR"], 0);
break;

case Keyboard.RIGHT:
carBody.setSteer(["WheelFL", "WheelFR"], 0);
break;
case Keyboard.SPACE:
carBody.setHBrake(0);
}
}

private function updateWheelSkin():void
{
steerFL.rotationY = carBody.wheels["WheelFL"].getSteerAngle();
steerFR.rotationY = carBody.wheels["WheelFR"].getSteerAngle();

wheelFL.pitch(carBody.wheels["WheelFL"].getRollAngle());
wheelFR.pitch(carBody.wheels["WheelFR"].getRollAngle());
wheelBL.roll(carBody.wheels["WheelBL"].getRollAngle());
wheelBR.roll(carBody.wheels["WheelBR"].getRollAngle());

steerFL.y = carBody.wheels["WheelFL"].getActualPos().y;
steerFR.y = carBody.wheels["WheelFR"].getActualPos().y;
wheelBL.y = carBody.wheels["WheelBL"].getActualPos().y;
wheelBR.y = carBody.wheels["WheelBR"].getActualPos().y;
}

protected override function onRenderTick(event:Event = null):void {
//physics.step();
physics.engine.integrate(0.12);
updateWheelSkin();

renderer.renderScene(scene, springCamera, viewport);
}
}
}

24 thoughts on “Car Drive On Terrain

  1. Can I ask for a little hint, how could I use collada model as terrain?

  2. Hello! 🙂
    May i ask you for a severly your classes?
    JTerrain, pv3dTerrain i need.

    Thanks 🙂

  3. Hey muzer,

    Awesome work. When will this functionality be available for Away3D?

    I have a current project which needs completed by next week and this is exactly what I need. Is the Away3D version near completion? Or should I use Papervision?

  4. Yes, I’m still working that currently, but it’s hard to say when it will be finished, because i cannot spare too much time. while i will do the utmost of my power.

  5. Pure awesomeness!
    My deepest respect, I’ll buy you a beer :p

    Can’t wait to find the time joy test this version!

  6. can’t be too hard to use collada as terrain – all you have to do is to create heightmap (with gradient fills).

  7. Pingback: Creating Successful Flash Games (notes and slides) | ArcadePortal

  8. 很不错的物理引擎,但还未达到能生产flash3D游戏的水准,我会一直关注这个博客。
    当这个引擎足够强大和完善时,我想我会购买它的。
    ps:我是制作flash游戏的。

  9. Is there a way to update the heightmap for the terrain? … ala perlin noise?

  10. Hi,
    I need load different collada in same file then how can i convert DAE object into RigidBody?

    thanks a lot.

  11. Very impressive, especially when you look at the speed you have achieved.
    The game could do with a little tweaking, to make the car jump around more and get more of a feeling of speed.
    Also I wander if you use flash10 to texture the terrain and car, what it would look like.
    But all in all: wow!

  12. Me again, may I say, I like the details on the camera movement. It never gets stuck in the scenery!

  13. Hello,

    I love Jiglib, thank you so much for all your hard work. Not sure how to get in touch with the team but the email was listed on the front page of the jiglibflash.com website is broken. I’ve discovered a bug in the terrain class for away3d meshes: the physics representation is rotated 90 degrees and mirrored compared to the away3d mesh. The one example file does not show this since the heightmap image is symmetrical, so the bug was never detected. I’ve been bashing my head against this for three days and sadly I’m just not the coder you are so I thought I’d send a desperate plea to you to ask that you check it out.

    Perhaps the solution will be super easy. Something along the lines of the loop iterating pixels in the wrong order, or an xy coordinate is negative when it should be positive.

    To reproduce this bug, compile Away3DTerrainTest.as but use a heightmap image that isn’t symmetrical – for example put a mountain in one corner. Then run your compiled away3d flash file you will see the terrain is rendered correctly but the physics response acts as if the hill is invisibly situated on the opposite corner. Gosh it sounds so easy to fix, and I spent several hours trying, but I have failed. I suppose I could simply make two heightmaps, one to send to jiglib and the other mirrored and rotated in photoshop to use for the away3d mesh on screen. But to do so I’ll have to rip apart the physics.createTerrain class.

    I figured that perhaps you are so much more intimately familiar with the engine that the fix will present itself to you in mere seconds. I’m being optimistic, but I figure, might as well try simply asking, right?

    *YOU ROCK! * Thank you so very much. Keep up the wonderful work and if I can help in any way let me know.

    Kind regards,

    Chris K – aka McFunkypants

  14. THE BUG LISTED ABOVE HAS BEEN FIXED!

    Dear Muzerly,

    Thank you so very much! I am truly grateful and I have a deep respect and admiration for your efforts. I consider this an early Christmas present, and I hope to someday return the favour. I will svn update my libs now and am very excited to continue with my project. You should be able to play the finished game in the near future on my blog, which is http://www.mcfunkypants.com as this code is being used for this weekend’s Ludum Dare game jam.

    Again, thank you very much. Your hard work is appreciated and has made a big difference to me and others.

    Have a wonderful holiday.

    Chris K – aka McFunkypants

  15. Dear Muzerly:

    Merry Christmas!

    Thanks to you terrain mesh bug fix, I used away3d to create a downhill toboggan ride:

    http://www.mcfunkypants.com/2010/snowboggan/

    I’m using a heightmap bitmap to generate a terrain mesh and Jiglib physics engine + the Flare particle system. Written as a pure as3 project using the wonderful FlashDevelop. Check it out! Be sure to
    smash all the snowmen for extra points. You can click the restart button on the top left if you get flipped over. I would love to receive your comments. Enjoy! =D

    Thanks again!

    – Chris K aka McFunkypants

    P.S. I warmly welcome you to connect with me on twitter: http://twitter.com/McFunkypants

  16. Pingback: Creating Successful Flash Games (notes and slides) | misterpah

Leave a Reply

Your email address will not be published. Required fields are marked *