Tuesday, October 6, 2009

Throwing Dice With the Jiglib Physics Engine and Away3D

Posted on 7:34 PM by -

In this tutorial we'll build a box which we can throw dice into. To do this we'll use Away3D as the 3D engine and Jiglib as the physics engine. Let's get stuck in..


PG

Author: Yagiz Gurgul

I'm a Flash Platform Developer. I usually work on AIR and Flash. And I'm also a good snowboarder :)

Step 1: New ActionScript 3.0 File

As the title suggests, first we'll create a new ActionScript 3.0 file.

Step 2: Edit Profile

In the Properties panel press the Edit button.

Step 3: Choose Flash Player Version

Choose Flash Player 9 profile and click OK.

Step 4: Get the Away3D Engine!

Save your file as "3D Dice.fla" in a new folder called "3D Dice". Now download the Away3D engine from Away3D Downloads. We'll use version 2.3.3 for Flash Player 9. Unpack an archive and copy all folders into the "3D Dice" folder.

Step 5: Get the Jiglib Physics Engine for Flash!

You need to install an SVN program to get these source files. Here's the SVN address. If you don't want to deal with all that you can also get them from the source files for this tut. After you move the Away3D and Jiglib classes to your 3D Dice folder your documents should look like this:

Step 6: Import Textures

I've drawn some textures for our dice. Of course, you can change these but they aren't bad :) You can find other textures in the source file.
Dice Textures:
Now we need to give them linkage names in order to attach them in runtime (do this for every image in your library one by one):
Select Image, then right-click > Properties
Export for ActionScript > Remove ".png" part

Step 7: Start Coding

OK, we're ready to start coding. First we import our classes:
  1. import away3d.cameras.*;  
  2. import away3d.containers.*;  
  3. import away3d.materials.*;  
  4. import away3d.primitives.*  
  5. import away3d.lights.DirectionalLight3D  
  1. import jiglib.physics.RigidBody;  
  2. import jiglib.plugin.away3d.Away3DPhysics;  
  3. import jiglib.plugin.away3d.Away3dMesh;  
  4. import jiglib.math.JNumber3D  

Step 8: Setup Variables

After importing our classes we need to define our variables to use them in the coming steps.
  1. var scene:Scene3D;  
  2. var camera:HoverCamera3D;  
  3. var view:View3D;  
  4. var light:DirectionalLight3D;  
  5. var physics:Away3DPhysics;  
  1. var boxWidth:Number = 250  
  2. var boxHeight:Number = 30  
  3. var boxDepth:Number = 250  
  4. var boxThickness:Number = 5  
  1. var diceTextures:Array = [ new WhiteShadingBitmapMaterial( new diceTexture1(0,0) ),  
  2.                             new WhiteShadingBitmapMaterial( new diceTexture2(0,0) ),  
  3.                             new WhiteShadingBitmapMaterial( new diceTexture3(0,0) ),  
  4.                             new WhiteShadingBitmapMaterial( new diceTexture4(0,0) ),  
  5.                             new WhiteShadingBitmapMaterial( new diceTexture5(0,0) ),  
  6.                             new WhiteShadingBitmapMaterial( new diceTexture6(0,0) ) ]  
  1. var wallTexture:WhiteShadingBitmapMaterial = new WhiteShadingBitmapMaterial( new walltexture(0,0))  
  2. var groundTexture:WhiteShadingBitmapMaterial = new WhiteShadingBitmapMaterial( new groundtexture(0,0))  
  1. var diceScale:Number = 30  
  2. var dices:Array = new Array()  
  3. var diceRandomForce:Number = 50  
As you may have guessed, the first ones are for Away3D. The variable names are simple, so you can guess what they are for.
diceTextures holds the dice face textures. We take dice texture images from the library and put them into WhiteShadingBitmapMaterial. We choose this material because it'll keep things light and to further improve performance it will also be flat. WhiteShadingBitmapMaterial is the best for our needs.
wallTexture and groundTexture use different images. Using either of these in our case would look terrible.

Step 9: Setup Away3D

Next we need to build Away3D.
  1. function initAway3D():void {  
  2.    scene = new Scene3D();  
  1. camera = new HoverCamera3D();  
  2. camera.distance = 300  
  1.   light = new DirectionalLight3D({color:0xFFFFFF, ambient:0.25, diffuse:0.75, specular:0.9})  
  2.   scene.addChild(light)  
  3.     
  4.   view=new View3D({scene:scene,camera:camera});  
  5.   view.x=stage.stageWidth/2;  
  6.   view.y=stage.stageHeight/2;  
  7.   addChild(view);  
  8.   
  9. physics = new Away3DPhysics(view,4)  
The first line in this function creates our 3D scene, into which we add 3D objects. For the camera we choose HoverCamera3D. HoverCamera is the best way to turn the camera around the objects. Actually you don't need to use light for this project, but It makes the experiment cool :) We create the View and set it in the middle of the scene.
Lastly, we create new Away3DPhysics. The first paremeter is "View3D" and the second is "gravity" (I use 4 but If you want you can try another number).

Step 10: Creating Walls

  1. function createWalls():void {  
  2.     var left:RigidBody = physics.createCube({width:boxThickness, height:boxHeight, depth:boxDepth});  
  3.     left.movable = false;  
  4.     left.x = -(boxWidth+boxThickness)/2  
  5.     Away3dMesh(left.skin).mesh.material = wallTexture  
  6.       
  7.     var right:RigidBody = physics.createCube({width:boxThickness, height:boxHeight, depth:boxDepth});  
  8.     right.movable = false;  
  9.     right.x = (boxWidth+boxThickness)/2  
  10.     Away3dMesh(right.skin).mesh.material = wallTexture  
  11.       
  12.     var front:RigidBody = physics.createCube({width:boxWidth, height:boxHeight, depth:boxThickness});  
  13.     front.movable = false;  
  14.     front.z =  (boxDepth+boxThickness)/2  
  15.     Away3dMesh(front.skin).mesh.material = wallTexture  
  16.       
  17.     var back:RigidBody = physics.createCube({width:boxWidth, height:boxHeight, depth:boxThickness});  
  18.     back.movable = false;  
  19.     back.z = -(boxDepth+boxThickness)/2  
  20.     Away3dMesh(back.skin).mesh.material = wallTexture  
  21.       
  22.     var ground:RigidBody = physics.createCube({width:boxWidth, height:boxThickness, depth:boxDepth, segmentsW:2, segmentsH:2});  
  23.     ground.movable = false;  
  24.     ground.y = -(boxHeight+boxThickness)/2  
  25.     Away3dMesh(ground.skin).mesh.material = groundTexture  
  26.     Away3dMesh(ground.skin).mesh.pushback = true  
  27. }  
It looks like a mess right :) Actually no. Click the walls of the box in the following demo to learn how we set their position:
We use Cubes as walls, but to do that we use physics.createCube, you can't set material in parameters directly. Jiglib Away3D plugin doesn't allow this (though you can change Away3DPhysics.as file to allow for it if you want). To change material we need to get the original Away3D object:
  1. Away3dMesh(rigidObject.skin).mesh  
By using this we attach our textures to the faces of our walls. We set movable to false because we don't want them to be moved right? :) When we create ground we also set its pushback property to true, so the ground won't be able to jump over the walls.

Step 11: Creating a Die

  1. function createDice():void {  
  2.     var dice:RigidBody = physics.createCube({width:diceScale, height:diceScale, depth:diceScale});  
  3.     dice.y=500  
  4.     dice.movable=true  
  5.     Cube(Away3dMesh(dice.skin).mesh).cubeMaterials.left = diceTextures[0]  
  6.     Cube(Away3dMesh(dice.skin).mesh).cubeMaterials.right = diceTextures[1]  
  7.     Cube(Away3dMesh(dice.skin).mesh).cubeMaterials.front = diceTextures[2]  
  8.     Cube(Away3dMesh(dice.skin).mesh).cubeMaterials.back = diceTextures[3]  
  9.     Cube(Away3dMesh(dice.skin).mesh).cubeMaterials.top = diceTextures[4]  
  10.     Cube(Away3dMesh(dice.skin).mesh).cubeMaterials.bottom = diceTextures[5]  
  11.     dices.push(dice)  
  12. }  
As you can see it's really simple. We basically create a cube and attach textures to its faces. To attach different textures to different faces we use cubeMaterials. cubeMaterials have 6 properties that we use. These are:
  • front
  • back
  • top
  • bottom
  • left
  • right
You can set any material to them, but we use elements of diceTextures we created in step 8.

Step 12: Resetting Dice

  1. function resetOnlyPositions():void {  
  2.     for(var i:int = 0; i
  3.         dices[i].moveTo( new JNumber3D( 0, 100 + i*(diceScale+10) , 0 ) )  
  4.     }  
  5. }  
  6. function resetAllDices(e:MouseEvent):void {  
  7.     for(var i:int = 0; i
  8.         dices[i].moveTo( new JNumber3D( 0, 100 + i*(diceScale+10) , 0 ) )  
  9.         addRandomForce(dices[i])  
  10.     }  
  11. }  
In these functions we reset their positions. The first one is for beginning. The second one runs when the user click the stage. The second function also adds random forces to the dice. This makes our dice start to tumble.

Step 13: Adding Random Forces to Dices

  1. function addRandomForce(rigid:RigidBody) {  
  2.       
  3.     var forceX:Number =  + Math.random()*( diceRandomForce )  
  4.     var forceY:Number =  + Math.random()*( diceRandomForce )  
  5.     var forceZ:Number =  + Math.random()*( diceRandomForce )  
  6.       
  7.     rigid.addBodyForce(new JNumber3D(forceX,forceY,forceZ) , new JNumber3D( rigid.x + diceScale ,rigid.y,rigid.z) )  
  8.     rigid.addBodyForce(new JNumber3D(-forceX,-forceY,-forceZ) , new JNumber3D(rigid.x - diceScale,rigid.y,rigid.z) )  
  9.       
  10. }  
First we get random values for our forces. With these values we apply forces in opposite directions to opposing sides of each die. This forces the dice to spin.

Step 14: Listeners

  1. function initListeners():void {  
  2.     stage.addEventListener(Event.ENTER_FRAME, render);  
  3.     stage.addEventListener(MouseEvent.MOUSE_DOWN,resetAllDices)  
  4. }  
In this function we add MOUSE_DOWN and ENTER_FRAME listeners to the stage.

Step 15: Rendering

  1. function render( e:Event ):void {  
  2.     view.render();  
  3.     camera.targetpanangle = stage.mouseX/stage.stageWidth*360  
  4.     camera.targettiltangle = stage.mouseY/stage.stageHeight*30  
  5.     camera.hover();  
  6.     physics.step();  
  7.       
  8.     light.x = camera.x  
  9.     light.y = camera.y  
  10.     light.z = camera.z  
  11. }  
The first line of this function renders the 3D. Then we use true proportion in math to rotate the camera by using mouse positions. Then we equalize the direction of the light to the positions of our camera. This makes our light dynamic and makes our experiment looks pretty cool.

Step 16: Last Step

  1. initAway3D();  
  2. createWalls();  
  3. createDice();  
  4. createDice();  
  5. resetOnlyPositions()  
  6. initListeners();  
We call our functions one by one. I called createDice() function twice, so there are 2 dice in the demo. You can add however many you want.
Ok. We're ready. Test your work and click the stage :)

Conclusion

In this tutorial we learned how to use Jiglib with Away3D and build a simple experiment.
I hope you liked it, thanks for reading!

No Response to "Throwing Dice With the Jiglib Physics Engine and Away3D"

Leave A Reply