I explainded previously how to add textures, lighting, shadows and sky, which greatly improved the look of the test sample I’m working on. But wouln’t it be nice if we could customize the controller to detect collisions, so that we can’t actually walk through the walls or the ground ?
The basis to do so, is to extend the controller class and override / add customized features. Remember how to create a controller instance ?
1 2 |
// create a controller where the controlled object is the camera, 400 is the speed simpleController = new SimpleObjectController(stage, camera, 400); |
Alternativa 3D provides a basic ellipsoide collider object, so we’re going to make good use of that. What we need to do is pass such a collider object to the controller, as well as the list of collidable objects, in our case the “whole world”, or everything contained in the rootContainer
, where the box objects are stored.
1 2 3 |
//MyController ( collider , collidable , eventSource , object , speed , multiplier , sensitivity ) // controller = new MyController(new EllipsoidCollider(100, 100, 250), rootContainer, stage, camera, 400, 3, 1); |
The last 2 parameters, speedMultiplier and mouseSensitivity, are optional, but they can be used for personal adjustments.
Well, what we need now is to create the MyController class which will extend the controller class. At the same time, this will show you the basics of extending classes in AS3 – something I just learnt as I’m still a beginner in this field :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class MyController extends SimpleObjectController { //constructor public function MyController(collider:EllipsoidCollider, collidable:Object3D, eventSource:InteractiveObject, object:Object3D, speed:Number, speedMultiplier:Number = 3, mouseSensitivity:Number = 1) { super(eventSource, object, speed, speedMultiplier, mouseSensitivity); // call SimpleObjectController constructor this.collider = collider; this.collidable = collidable; super.accelerate(true); // make acceleration default this.maxPitch = 0; // define max pitch (look up limit) this.minPitch = -Math.PI; // define min pitch (look down limit) } } |
Acceleration is made default, so I need to override the accelerate method to make you walk instead of run :
1 2 3 |
override public function accelerate(value:Boolean):void { super.accelerate(!value); // simple inversed logic ! } |
Remember that the controller’s update()
method is called on every frame ? This is where user actions are taken into account and position of the object – in this case the camera – is calculated and updated. We are going to override the method :
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 |
private const GRAVITY:Number = 9800; private var fallSpeed:Number = 0; private var lastTime:int; override public function update():void { var object:Object3D = this.object; if (object == null) return; if (collider && collidable) { var mytime:int = getTimer(); var deltaTime:Number = 0.001*(mytime - lastTime); lastTime = mytime; fallSpeed -= 0.5 * GRAVITY * deltaTime * deltaTime; // fallspeed calculated with basic physics lastPosition.x = object.x; lastPosition.y = object.y; lastPosition.z = object.z; // call parent update to calculate new position // if collision is detected later on, position will be overwritten super.update(); displacement.x = object.x - lastPosition.x; displacement.y = object.y - lastPosition.y; displacement.z = 0; var collisionPoint:Vector3D = new Vector3D(); var collisionPlane:Vector3D = new Vector3D(); var myPos:Vector3D = new Vector3D(object.x, object.y, object.z); // check for collision / on ground check var onGround:Boolean = collider.getCollision(myPos, new Vector3D(0, 0, fallSpeed), collisionPoint, collisionPlane, collidable); if (onGround) { fallSpeed = 0; } else { displacement.z = fallSpeed; } // calculate position after collision according to theoretical displacement var dest:Vector3D = collider.calculateDestination(lastPosition, displacement, collidable, excludedObjects); setObjectPosXYZ(dest.x, dest.y, dest.z); object.x = dest.x; object.y = dest.y; object.z = dest.z; } else { // when no collider or collidable objects are provided, update parent class super.update(); } } |
Here’s a link to the demo. I changed the starting point, so you can experience the collision on ground when falling off the wall.
Once you’ve done that, you’re in the maze and can’t climb back on the wall again. I thought it would be nice to allow that, so I included further customization to add the ability to jump up. It’s more some kind of jetpack feature as you can actually stay in the air.
First thing, I bind the space bar to the ACTION_UP
hook, in the main class :
1 |
controller.bindKey(32,"ACTION_UP"); //SPACE |
I don’t have to, of course. I could create a new hook, and attach the implementation to it, but as ACTION_UP
wasn’t used any more, I thought I could override it for my own purpose :
1 2 3 4 5 6 7 8 |
private const INI_POWER:Number = 50000; // jetpack initial power private const DIM_RATIO:Number = 0.95; // diminution ratio private var jetPower:Number = INI_POWER; // jetpack power private var jetPack:Boolean = false; // jetpack activation flag override public function moveUp(value:Boolean):void { jetPack = value; } |
Next, I need to calculate the effect of the jetpack, which will simply end up producing a opposite fallspeed. While falling is achieved by a negative speed, jumping will be positive. I didn’t think it very far, I just considered the jetpack power as an opposite gravitational power, which will decrease untill having no more effect. Let’s see :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
if (jetPack) { // jump jetPower = jetPower * DIM_RATIO; // jetpack power decrease if (jetPower < 10) { jetPower = 0; fallSpeed = 0; // nullified effect } else { fallSpeed = 0.5 * jetPower * deltaTime * deltaTime; // jumpspeed } } else { // fall fallSpeed -= 0.5 * GRAVITY * deltaTime * deltaTime; jetPower = INI_POWER; // restore initial jetpack power } |
That’s it, I get the wanted behavior ! Further development could be to limit the effect of the jetpack to a restrained period of time, so you wouldn’t stay in the air as long as you wished. Anyway, as usual, you can test the updated demo.