Using the JigLibFlash Physics Engine with Sandy 3D
Lorem Ipsum... This is work in progress, so stay tuned.
A disclaimer is appropriate: I'm new to this stuff, so you're warned that
what follows doesn't necessarily reflect best praxis :)
Proofreading also remains.
The jiglibflsh physics engine
ToDo:Present the physics engine and why we need it - some background on forerunners and porting. About the proxy, adapter class, Façade etc - companion objects.
Get the Sandy 3D engine and the jiglibflash AS3 3D physics engine to get started. You download the jiglibflash_r92.swc or the naked source code from the SVN repository (See Checkut). I'm using Flash Develop, so I add the SWC to my library path. You'll find more information on the jiglibflash blog. The API docs give an overview but right now not much comment.
The hello physics world
It's always good to have someone who clears the way for you, when you start an endeavor out in the wild and unknown. I had great help from the the short but inspiring New_API_Very_Short_Tutorial and How to create basic physics 3d scene.
Like other 3D engines, Sandy has a stub class with all settings for a normal Sandy application, to get us up and running quickly. It is called BasicView, and creates the scene, the camera and a canvas to draw on. It also comes with some helpful utility methods for creating primitive 3D objects and materials to dress them in. It even takes care of advancing the frames for us. All we have to do, is to extend BasicView, and we are ready to create the objects we want to get on stage.
So lets start by creating a test class that inherits from BasicView which inherits from Sprite. We create in the default ( nameless ) package, and we need to import a few things from the Sandy and jiglib libraries. If we forget to import some needed classes, the compiler will no doubt remind us.
package{ import flash.events.Event; import jiglib.plugin.sandy3d.Sandy3DPhysics; import jiglib.physics.RigidBody; public class TestClass extends BasicView { private var physics:Sandy3DPhysics;
Our TestClass inherits from BasicView which inherits from Sprite, so the class has all the properties and mathods of BasicView and Sprite. The consequence is that we can add displayable objects directly to our class.
From the jiglib Sandy plugin, we need to import Sandy3DMesh that represents the Sandy Shape3D, and SAndy3DPhysics which represents the jiglibflash physics engine. We also import RidgidBody, the physical counterpart to the visible Sandy 3D object, and some classes from the Sandy library, as we shall see.
public function TestClass() { super.init(400, 400); physics = new Sandy3DPhysics(scene, 10); createBox(); render(); }
The constructor first initiates its base class, which means that we get a scene with a camera and a canvas. The camera uses it's standard settings and draws on a canvas 400 by 400 pixels.
The physics engine needs access to our scene and its objects, so we pass it to the constructor when we create the proxy or adapter class between the Sandy world and the physics engine. We also pass the animation speed we want the engine to have.
Then we create a simple object, a sphere and finally call the render() method, to start rendering our scene. We will discuss that in a moment.
private function createSphere():void { var sphere:RigidBody = physics.createSphere("sphere", null, 50, 7, 5); }
To get a physics enabled sphere we call the createSphere method on the adapter class. We pass a name, an appearance, the radius and the horizontal and vertical quality values. What we get back is not a simple Shape3D. The createSphere method really creates twins, a RigidBody object that carries the physical properties and a Shape3D object, representing the visible properties of the sphere. We need an appearance for our object to be visible on screen, but here I have chosen not to explicitly set the material. Using null as a value the standard WireFrameMaterial is used.
It returns the rigid body, but creates the Sandy shape behind the scenes. It also adds the Sandy Sphere to the root node of the scene.
Now that we have our simple scene set up, we are ready to render it. In a BasicView, we start rendering by calling the render() method. This is not the sane as the scene.render() we are used to from pure Sandy applications. The BasicView render() method starts the rendering by installing a listener for ENTER_FRAME events. When the events are fired a simpleRender method is called, that in its turn calls scene.render().
It may seem too complicated and a bit overkill, but there is a good reason for this. The simpleRender method is a hook of sorts, to hang other behavior on. If we do nothing more, we get the normal non physical behavior. It is commonplace in Sandy applications, to create an ENTER_FRAME event handler, where we move the camera or an object to get frame based animations. But here we want physics, like gravity, forces, collisions and constraints. We do this by overriding the simpleRender method and let the physics twin of our visible sphere the locomotion.
override public function simpleRender( event:Event = null ):void { physics.step( ); super.simpleRender( event ); }
So much talk and so simple code. The overriding version of the method is called and calls for a physics step by the physics engine. The it does the old fashioned call to scene.render via the original simpleRender method.
That's all for the Hello JigLib example. Lets see the result.
[Show me] - Here is the full code.
Well, not much to see and not for long, but enough to get our feet a
little moist.
The sphere looks just a mesh as expected, and the only physics influencing
our object, seems to be the old mother Earth. The sphere just continues
until it reaches the center of the planet, one would presume ;)
Stop that ball
OK, so we need something that can stop the ball from falling. Let's make a floor and see what happens. In JigLib parlance it's called a JPlane. As for the sphere, both the JPlane and the Plane3D are created and the latter is added to the scene.
Here is the new createScene method, appropriately renamed from createSphere.
private function createScene():void { var appearance:Appearance = makeColorAppearance(); var sphere:RigidBody = physics.createSphere("sphere", null, 20, 7, 5); sphere.y = 150; var ground:RigidBody = physics.createGround("ground", appearance, 200, -100); }
We start the sphere falling from a little bit higher up and we place the floor a bit lower down. Here we also add an appearance to the floor. If we are lazy we get the standard appearance from the convenience method makeColorAppearance inherited from BasicView.
[Here is the result] and here is the code
Ah, great deal with the physics engine. We don't have to detect the collision with the floor. The ball stops and even makes a little bouncing, when it hits the ground.
A myth buster
For the gravity to be able to work on the ball, it must have a mass. Otherwise the ball would just float where we place it at the height 150. So what's the mass of our ball? Peeing inside the physics engine or lazily trace the mass property, reveals that the standard mass is 1, if nothing else is said. We can easily change that, to get bodies with different masses.
Some people think that heavier bodies fall faster than lighter ones, so let's test this hypothesis. Let's be extravagant and create two balls with different masses and see which one hits the ground first. Here is our new createScene method.
private function createScene():void { // Create some materials var appearance:Appearance = makeColorAppearance(); var attributes:MaterialAttributes = new MaterialAttributes(new LightAttributes(true)); var ballMaterial1:ColorMaterial = new ColorMaterial(0xE0B42F, 1, attributes); var ballMaterial2:ColorMaterial = new ColorMaterial(0xAA1200, 1, attributes); ballMaterial1.lightingEnable = true; ballMaterial2.lightingEnable = true; var sphere:RigidBody = physics.createSphere("sphere", new Appearance( ballMaterial1 ), 20, 7, 5); sphere.y = 150; var sphere2:RigidBody = physics.createSphere("sphere", new Appearance( ballMaterial2 ), 20, 7, 5); sphere2.y = 150; sphere2.x = 60; sphere2.mass = 5; var ground:RigidBody = physics.createGround("ground", appearance, 200, -100); }
Here we start to create our own appearances, to make the balls a little nicer to look at. I'm going to propose other materials in upcoming examples, normally without comments. Other Sandy examples explains how to do that.
[Here you go] and the source
The brownish ball is the heavier one, and as you certainly expected, that myth is busted.
Collisions
A bit more interesting would be to see what happens if the two balls collide. We can easily check that, if we let the balls start from different heights and falling so close to each other, that the last one is bound to hit the first. So let's start the heavy read ball a bit higher up and with a horizontal distance less than the radius from the light ball.
We will set ball2.y = 200 and ball2.x = 25 and run again.
[Watch the collision] The source again
Wow! You should also test to see if the balls behave differently in case the light ball hits the heavier one.
Next up: Jumping balls