Working with Away3D Cameras

Exclusive offer: get 50% off this eBook here
Away3D 3.6 Cookbook

Away3D 3.6 Cookbook — Save 50%

Over 80 practical recipes for creating stunning graphics and effects with the fascinating Away3D engine

£20.99    £10.50
by Michael Ivanov | June 2011 | Open Source Web Graphics & Video

Three dimensions are better than two — and it's not a secret anymore that 3D is here to stay. Gone are the days when Flash was just used for 2D animations. In the last few years, online Flash content has undergone a revolution with the introduction of real-time 3D engines for Flash. Away3D is the big daddy of them all—which makes it the ultimate resource for top-rated 3D content development and for powering today's coolest games and Flash sites.

In this article by Michael Ivanov, author of Away3D 3.6 Cookbook, we will cover:

  • Creating an FPS controller
  • Creating Camera Depth of Field Effect
  • Following a third-person view with a spring camera

 

Away3D 3.6 Cookbook

Away3D 3.6 Cookbook

Over 80 practical recipes for creating stunning graphics and effects with the fascinating Away3D engine

        Read more about this book      

(For more resources on Away3D, see here.)

Introduction

Cameras are an absolutely essential part of the 3D world of computer graphics. In fact, no real-time 3D engine can exist without having a camera object. Cameras are our eyes into the 3D world.

Away3D has a decent set of cameras, which at the time of writing, consists of Camera3D, TargetCamera3D, HoverCamera3D, and SpringCam classes. Although they have similar base features, each one has some additional functionality to make it different.

Creating an FPS controller

There are different scenarios where you wish to get a control of the camera in first person, such as in FPS video games. Basically, we want to move and rotate our camera in any horizontal direction defined by the combination of x and y rotation of the user mouse and by keyboard keys input. In this recipe, you will learn how to develop such a class from scratch, which can then be useful in your consequential projects where FPS behavior is needed.

Getting ready

Set up a basic Away3D scene extending AwayTemplate and give it the name FPSDemo. Then, create one more class which should extend Sprite and give it the name FPSController.

How to do it...

FPSController class encapsulates all the functionalities of the FPS camera. It is going to receive the reference to the scene camera and apply FPS behavior "behind the curtain".

FPSDemo class is a basic Away3D scene setup where we are going to test our FPSController:

FPSController.as

package utils
{
public class FPSController extends Sprite
{
private var _stg:Stage;
private var _camera:Object3D
private var _moveLeft:Boolean=false;
private var _moveRight:Boolean=false;
private var _moveForward:Boolean=false;
private var _moveBack:Boolean=false;
private var _controllerHeigh:Number;
private var _camSpeed:Number=0;
private static const CAM_ACCEL:Number=2;
private var _camSideSpeed:Number=0;
private static const CAM_SIDE_ACCEL:Number=2;
private var _forwardLook:Vector3D=new Vector3D();
private var _sideLook:Vector3D=new Vector3D();
private var _camTarget:Vector3D=new Vector3D();
private var _oldPan:Number=0;
private var _oldTilt:Number=0;
private var _pan:Number=0;
private var _tilt:Number=0;
private var _oldMouseX:Number=0;
private var _oldMouseY:Number=0;
private var _canMove:Boolean=false;
private var _gravity:Number;
private var _jumpSpeed:Number=0;
private var _jumpStep:Number;
private var _defaultGrav:Number;
private static const GRAVACCEL:Number=1.2;
private static const MAX_JUMP:Number=100;
private static const FRICTION_FACTOR:Number=0.75;
private static const DEGStoRADs:Number = Math.PI / 180;

public function FPSController(camera:Object3D,stg:Stage,
height:Number=20,gravity:Number=5,jumpStep:Number=5)
{
_camera=camera;
_stg=stg;
_controllerHeigh=height;
_gravity=gravity;
_defaultGrav=gravity;
_jumpStep=jumpStep;
init();
}
private function init():void{
_camera.y=_controllerHeigh;
addListeners();
}
private function addListeners():void{
_stg.addEventListener(MouseEvent.MOUSE_DOWN,
onMouseDown,false,0,true);
_stg.addEventListener(MouseEvent.MOUSE_UP,
onMouseUp,false,0,true);
_stg.addEventListener(KeyboardEvent.KEY_DOWN,
onKeyDown,false,0,true);
_stg.addEventListener(KeyboardEvent.KEY_UP,
onKeyUp,false,0,true);

}
private function onMouseDown(e:MouseEvent):void{
_oldPan=_pan;
_oldTilt=_tilt;
_oldMouseX=_stg.mouseX+400;
_oldMouseY=_stg.mouseY-300;
_canMove=true;
}
private function onMouseUp(e:MouseEvent):void{
_canMove=false;
}
private function onKeyDown(e:KeyboardEvent):void{
switch(e.keyCode)
{
case 65:_moveLeft = true;break;
case 68:_moveRight = true;break;
case 87:_moveForward = true;break;
case 83:_moveBack = true;break;
case Keyboard.SPACE:
if(_camera.y<MAX_JUMP+_controllerHeigh){
_jumpSpeed=_jumpStep;
}else{
_jumpSpeed=0;
}

break;
}
}
private function onKeyUp(e:KeyboardEvent):void{
switch(e.keyCode)
{
case 65:_moveLeft = false;break;
case 68:_moveRight = false;break;
case 87:_moveForward = false;break;
case 83:_moveBack = false;break;
case Keyboard.SPACE:_jumpSpeed=0;break;
}
}
public function walk():void{
_camSpeed *= FRICTION_FACTOR;
_camSideSpeed*= FRICTION_FACTOR;
if(_moveForward){ _camSpeed+=CAM_ACCEL;}
if(_moveBack){_camSpeed-=CAM_ACCEL;}
if(_moveLeft){_camSideSpeed-=CAM_SIDE_ACCEL;}
if(_moveRight){_camSideSpeed+=CAM_SIDE_ACCEL;}
if (_camSpeed < 2 && _camSpeed > -2){
_camSpeed=0;
}
if (_camSideSpeed < 0.05 && _camSideSpeed > -0.05){
_camSideSpeed=0;
}
_forwardLook=_camera.transform.deltaTransformVector(new
Vector3D(0,0,1));
_forwardLook.normalize();
_camera.x+=_forwardLook.x*_camSpeed;
_camera.z+=_forwardLook.z*_camSpeed;

_sideLook=_camera.transform.deltaTransformVector(new
Vector3D(1,0,0));
_sideLook.normalize();
_camera.x+=_sideLook.x*_camSideSpeed;
_camera.z+=_sideLook.z*_camSideSpeed;

_camera.y+=_jumpSpeed;
if(_canMove){
_pan = 0.3*(_stg.mouseX+400 - _oldMouseX) + _oldPan;
_tilt = -0.3*(_stg.mouseY-300 - _oldMouseY) +
_oldTilt;
if (_tilt > 70){
_tilt = 70;
}

if (_tilt < -70){
_tilt = -70;
}
}
var panRADs:Number=_pan*DEGStoRADs;
var tiltRADs:Number=_tilt*DEGStoRADs;
_camTarget.x = 100*Math.sin( panRADs) * Math.cos
(tiltRADs) +_camera.x;
_camTarget.z = 100*Math.cos( panRADs) * Math.cos
(tiltRADs) +_camera.z;
_camTarget.y = 100*Math.sin(tiltRADs) +_camera.y;
if(_camera.y>_controllerHeigh){
_gravity*=GRAVACCEL;
_camera.y-=_gravity;
}
if(_camera.y<=_controllerHeigh ){
_camera.y=_controllerHeigh;
_gravity=_defaultGrav;
}
_camera.lookAt(_camTarget);
}
}
}

Now let's put it to work in the main application:

FPSDemo.as
package
{
public class FPSDemo extends AwayTemplate
{
[Embed(source="assets/buildings/CityScape.3ds",mimeType="
application/octet-stream")]
private var City:Class;
[Embed(source="assets/buildings/CityScape.png")]
private var CityTexture:Class;
private var _cityModel:Object3D;
private var _fpsWalker:FPSController;
public function FPSDemo()
{
super();
}

override protected function initGeometry() : void{
parse3ds();
}
private function parse3ds():void{
var max3ds:Max3DS=new Max3DS();
_cityModel=max3ds.parseGeometry(City);
_view.scene.addChild(_cityModel);
_cityModel.materialLibrary.getMaterial("bakedAll [Plane0").
material=new BitmapMaterial(Cast.bitmap(new CityTexture()));
_cityModel.scale(3);
_cityModel.x=0;
_cityModel.y=0;
_cityModel.z=700;
_cityModel.rotate(Vector3D.X_AXIS,-90); _cam.z=-1000;
_fpsWalker=new FPSController(_cam,stage,_view,20,12,250);
}
override protected function onEnterFrame(e:Event) : void{
super.onEnterFrame(e);
_fpsWalker.walk();
}
}
}

How it works...

FPSController class looks a tad scary, but that is only at first glance. First we pass the following arguments into the constructor:

  1. camera: Camera3D reference (here Camera3D, by the way, is the most appropriate one for FPS).
  2. stg: References to flash stage because we are going to assign listeners to it from within the class.
  3. height: It is the camera distance from the ground. We imply here that the ground is at 0,0,0.
  4. gravity: Gravity force for jump.
  5. JumpStep: Jump altitude.

Next we define listeners for mouse UP and DOWN states as well as events for registering input from A,W,D,S keyboard keys to be able to move the FPSController in four different directions.

In the onMouseDown() event handler, we update the old pan, tilt the previous mouseX and mouseY values as well as by assigning the current values when the mouse has been pressed to _oldPan, _oldTilt, _oldMouseX, and _oldMouseY variables accordingly. That is a widely used technique. We need to do this trick in order to have nice and continuous transformation of the camera each time we start moving the FPSController. In the methods onKeyUp() and onKeyDown(), we switch the flags that indicate to the main movement execution code. This will be seen shortly and we will also see which way the camera should be moved according to the relevant key press. The only part that is different here is the block of code inside the Keyboard.SPACE case. This code activates jump behavior when the space key is pressed.

On the SPACE bar, the camera jumpSpeed (that, by default, is zero) receives the _jumpStep incremented value and this, in case the camera has not already reached the maximum altitude of the jump defined by MAX_JUMP, is added to the camera ground height.

Now it's the walk() function's turn. This method is supposed to be called on each frame in the main class:

_camSpeed *= FRICTION_FACTOR;
_camSideSpeed*= FRICTION_FACTOR;

Two preceding lines slow down, or in other words apply friction to the front and side movements. Without applying the friction. It will take a lot of time for the controller to stop completely after each movement as the velocity decrease is very slow due to the easing.

Next we want to accelerate the movements in order to have a more realistic result. Here is acceleration implementation for four possible walk directions:

if(_moveForward){ _camSpeed+= CAM_ACCEL;}
if(_moveBack){_camSpeed-= CAM_ACCEL;}
if(_moveLeft){_camSideSpeed-= CAM_SIDE_ACCEL;}
if(_moveRight){_camSideSpeed+= CAM_SIDE_ACCEL;}

The problem is that because we slow down the movement by continuously dividing current speed when applying the drag, the speed value actually never becomes zero. Here we define the range of values closest to zero and resetting the side and front speeds to 0 as soon as they enter this range:

if (_camSpeed < 2 && _camSpeed > -2){
_camSpeed=0;

}

if (_camSideSpeed < 0.05 && _camSideSpeed > -0.05){
_camSideSpeed=0;
}

Now we need to create an ability to move the camera in the direction it is looking. To achieve this we have to transform the forward vector, which present the forward look of the camera, into the camera space denoted by _camera transformation matrix. We use the deltaTransformVector() method as we only need the transformation portion of the matrix dropping out the translation part:

_forwardLook=_camera.transform.deltaTransformVector(new
Vector3D(0,0,1));
_forwardLook.normalize();
_camera.x+=_forwardLook.x*_camSpeed;
_camera.z+=_forwardLook.z*_camSpeed;

Here we make pretty much the same change as the previous one but for the sideways movement transforming the side vector by the camera's matrix:

_sideLook=_camera.transform.deltaTransformVector(new Vector3D(1,
0,0));
_sideLook.normalize();
_camera.x+=_sideLook.x*_camSideSpeed;
_camera.z+=_sideLook.z*_camSideSpeed;

And we also have to acquire base values for rotations from mouse movement. _pan is for the horizontal (x-axis) and _tilt is for the vertical (y-axis) rotation:

if(_canMove){
_pan = 0.3*(_stg.mouseX+400 - _oldMouseX) + _oldPan;
_tilt = -0.3*(_stg.mouseY-300 - _oldMouseY) + _oldTilt;

if (_tilt > 70){
_tilt = 70;
}

if (_tilt < -70){
_tilt = -70;
}
}

We also limit the y-rotation so that the controller would not rotate too low into the ground and conversely, too high into zenith. Notice that this entire block is wrapped into a _canMove Boolean flag that is set to true only when the mouse DOWN event is dispatched. We do it to prevent the rotation when the user doesn't interact with the controller.

Finally we need to incorporate the camera local rotations into the movement process. So that while moving, you will be able to rotate the camera view too:

var panRADs:Number=_pan*DEGStoRADs;
var tiltRADs:Number=_tilt*DEGStoRADs;
_camTarget.x = 100*Math.sin( panRADs) * Math.cos(tiltRADs) +
_camera.x;
_camTarget.z = 100*Math.cos( panRADs) * Math.cos(tiltRADs) +
_camera.z;
_camTarget.y = 100*Math.sin(tiltRADs) +_camera.y;

And the last thing is applying gravity force each time the controller jumps up:

if(_camera.y>_controllerHeigh){
_gravity*=GRAVACCEL;
_camera.y-=_gravity;
}
if(_camera.y<=_controllerHeigh ){
_camera.y=_controllerHeigh;
_gravity=_defaultGrav;
}

Here we first check whether the camera y-position is still bigger than its height, this means that the camera is in the "air" now. If true, we apply gravity acceleration to gravity because, as we know, in real life, the falling body constantly accelerates over time. In the second statement, we check whether the camera has reached its default height. If true, we reset the camera to its default y-position and also reset the gravity property as it has grown significantly from the acceleration addition during the last jump.

To test it in a real application, we should initiate an instance of the FPSController class. Here is how it is done in FPSDemo.as:

_fpsWalker=new FPSController(_cam,stage,20,12,250);

We pass to it our scene camera3D instance and the rest of the parameters that were discussed previously.

The last thing to do is to set the walk() method to be called on each frame:

override protected function onEnterFrame(e:Event) : void{
super.onEnterFrame(e);
_fpsWalker.walk();
}

Now you can start developing the Away3D version of Unreal Tournament!

Away3D 3.6 Cookbook Over 80 practical recipes for creating stunning graphics and effects with the fascinating Away3D engine
Published: May 2011
eBook Price: £20.99
Book Price: £33.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on Away3D, see here.)

Creating Camera Depth of Field Effect

The depth of field (DOF) in optics is a distance at which a target object is focused. The unfocused objects around become blurry. You have seen this effect probably hundreds of times when filming with your home camera. In 3D, scene DOF effect enables you to define a distance from the camera at which an object is being fully focused, whereas the rest of the objects that are out of range of this distance become gradually blurred. Away3D gives us the ability to simulate this effect when using cameras in conjunction with DepthOfFieldSprite billboard. Let's see how to do it.

It is worth noting that depth of field effect is a CPU-intensive process as it makes use of the Flash generic blur filter. It may significantly slow down your application if used extensively.

Getting ready

Create a basic Away3D scene using AwayTemplate. Make sure you embed the dofText.png image file into the code, as it will serve as graphics fill for DepthOfFieldSprite object.

How to do it...

We are going to use here our brand new FPSController for camera manipulation. Also, we create a group of DepthOfFieldSprite objects and put them in an array of rows and columns that expends into depth of the viewport. That way, we can better see and understand the DOF effect in action:

DOFDemo.as

package
{
public class DOFDemo extends AwayTemplate
{
[Embed(source="assets/dofText.png")]
private var DofTexture:Class;
private var _fpsWalker:FPSController;
private var dc:DofCache;
private var _bitMat:BitmapMaterial;
public function DOFDemo()
{
super();
}
override protected function initMaterials() : void{
_bitMat=new BitmapMaterial(Cast.bitmap(new DofTexture()));
}
override protected function initGeometry() : void{
var xt:Number=0;
var yt:Number=0;
var zt:Number=0;

for(var i:int=0;i<7;++i){
for(var b:int=0;b<7;++b){
for(var c:int=0;c<4;++c){
xt=i*200+200;
yt=b*200+200;
zt=c*200+200;
var dof:DepthOfFieldSprite=new DepthOfFieldSprite(_
bitMat,10,10);
_view.scene.addSprite(dof);
dof.x=xt;
dof.y=yt-400;
dof.z=zt;
}
}
}
DofCache.usedof=true;
DofCache.maxblur=10;
DofCache.focus=550;
DofCache.aperture=45;
DofCache.doflevels=10;
_fpsWalker=new FPSController(_cam,stage,120,12,250);
}
override protected function onEnterFrame(e:Event) : void{
super.onEnterFrame(e);
_fpsWalker.walk();
}
}
}

You should see the following result:

Working with Away3D Cameras

How it works...

First, in the initGeometry() method, we run a 3-dimensional loop in order to deploy the sprites in an array so that each row of objects expends into the depth, evenly creating a number of good looking columns of aligned sprites.

Now comes the fun part. We set static DofCache.usedof=true in order to enable the DOF effect. DofCache is closely related to DepthOfFieldSprite. When we create a set of DepthOfFieldSprite objects, their bitmaps are automatically cached for future use of DofCache. Therefore, tweaking the DofCache settings, affects all the sprites.

We set up the following properties of DofCache:

  • DofCache.usedof=true: This enables the DOF effect.
  • DofCache.maxblur: This is a maximum amount of blur to be applied by blur filter.
  • DofCache.focus: It is a distance from the camera to objects to be fully focused.
  • DofCache.aperture: This one needs some explanation. If you wish to isolate your focused object from the rest, like as if you were focusing your home camera on a flower with the background totally out of focus, you need to set a shallow depth of field. The way to influence DOF is to control camera aperture. The less the value of an aperture property, the larger the DOF. And conversely, to get smaller aperture (deeper DOF), we should increase the aperture property value.
  • DofCache.doflevels: It is not easy to understand doflevels influence if we do not play around with its values. This property is responsible for the distributing of strength of the blur effect to several levels so that when the focused objects exceeds the defined focus range, its blur doesn't disappear abruptly, but rather diminishes over a number of steps defined by the levels of property. This creates smoother focus transition over a group of objects.

There's more...

There is another way to set DOF. And it is directly through Camera3D properties. Let's try it.

In the previous example, comment out the entire DofCache block and put instead:

_cam.enableDof();
_cam.doflevels=10;
_cam.aperture=520;
_cam.maxblur=5;
_cam.focus=30;

Here is our result, an array of sprites with DOF effect applied:

Working with Away3D Cameras

However, there is a problem with using this approach. Even though we basically get the DOF effect, it (the effect) doesn't behave like the effect created with DofCache. The blur transition is not smooth. Also we are forced to use the camera focus property to define the DOF distance. And because this focus influences the real focus of the camera, we get noticeable perspective distortion, as you can see on the preceding image. Nevertheless, in certain scenarios, this approach can be handy.

Creating DOF on Mesh Objects

Now, I will show you how we can get simple DOF effects on a regular geometry. Notice that in this showcase, you will see only a basic implementation of the blur effect based on the distance of the camera to its target, but it gives an idea of how to convert it into a full scale "Mesh DOF Engine".

For this purpose, we can create a basic Away3D scene and add to it our downtown city model. Here is the code:

FPSModelBlur.as

package
{
public class FPSModelBlur extends AwayTemplate
{
[Embed(source="assets/buildings/CityScape.3ds",mimeType="
application/octet-stream")]
private var City:Class;
[Embed(source="assets/buildings/CityScape.png")]
private var CityTexture:Class;
private var _cityModel:Object3D;
private var _fpsWalker:FPSController;
private var _distToCam:Number;
private var _blurF:BlurFilter=new BlurFilter();
private var _blurVal:Number=1;
private var _oldBlurVal:Number=1;
private var _oldDistToCam:Number=1;
private const MINRANGE:Number=500;
private const MAXRANGE:Number=2500;
public function FPSModelBlur()
{
super();
}
override protected function initGeometry() : void{
parse3ds();
}

private function parse3ds():void{
var max3ds:Max3DS=new Max3DS();
_cityModel=max3ds.parseGeometry(City);
_view.scene.addChild(_cityModel);
_cityModel.materialLibrary.getMaterial("bakedAll
[Plane0").material=new BitmapMaterial(Cast.bitmap(new
CityTexture()));
_cityModel.scale(3);
_cityModel.x=0;
_cityModel.y=0;
_cityModel.z=700;
_cityModel.rotate(Vector3D.X_AXIS,-90);
_cam.z=-1000;
_cityModel.ownCanvas=true;
_fpsWalker=new FPSController(_cam,stage,20,12,250);
}
override protected function onEnterFrame(e:Event) : void{
super.onEnterFrame(e);
if(_cityModel){
_distToCam=_cityModel.distanceTo(_cam);
_blurF.blurX=_blurVal/100;
_blurF.blurY=_blurVal/100;
_cityModel.filters=[_blurF];
_blurVal=_distToCam*_oldBlurVal/(_oldDistToCam);
_oldDistToCam=_distToCam;
_oldBlurVal=_blurVal;
if(_distToCam>=MAXRANGE){
_blurVal=_blurF.blurX*100;
}
if(_distToCam<=MINRANGE){
_blurVal=0;
}
}
_fpsWalker.walk();
}
}
}

As seen in the following image, Downtown model mesh is being blurred as we move away from it:

Working with Away3D Cameras

All the "magic" is in the onEnterFrame() function. We check each frame's distance from the target object to the camera. Then we set the blur values of the blur filter which is calculated by dividing _blurVal by 100 to get a number that fits into valid blur values range of the generic Flash Blur filter. Now we need to put into dependence the blur strength with the current distance of the camera from the target. These lines take care of it:

_blurVal=_distToCam*_oldBlurVal/(_oldDistToCam);
_oldDistToCam=_distToCam;
_oldBlurVal=_blurVal;

Finally, we check against predefined MAXRANGE and MINRANGE values when to stop increasing or decreasing the blur. So, in our case, when the camera distance from the target is greater than 2500, the blur ceases to increase further. And when the camera is in range of 500 pixels from the target, the blur value is zero (actually, no blur).

If you want to achieve similar effect of DOF as in the previous example, you need to create a system that will manage a DOF of each object separately. The best way here is to take the code from the onEnterFrame() function and to put it in a separate class (that is, to be a DOF manager) which would receive a reference to an object and to the scene camera. This way each primitive will receive its own blur depending on the distance from the camera. Also before attempting this, you should know that applying filters on geometry is quite a memory-intensive task, as you probably might have noticed in this example. Therefore, applying such effects to many meshes can completely render your application useless.

Following a third-person view with a spring camera

The Away3D camera arsenal has got an additional camera class called SpringCam. SpringCam is actually a Camera3D extended into a camera with additional physical behavior which is as the camera's name implies—springing. SpringCam is well suited to create 3D person camera control systems. If you plan to develop a racing game or any other 3D person view application, SpringCam is just for that.

SpringCam has got three important physical properties to set. No need to take a classic mechanics crash course to set them, but without understanding their meaning, it can take you hours to set up the desired behavior for the camera.

Stiffness—controls the stretching factor of the spring. That is how far the camera can stretch during the spring movement. The bigger value means less expansion and more fixed spring behavior. You should be careful though if the damping and mass are low. When increasing the stiffness, you can encounter some crazy spring bounces. It is recommended to keep in the range of 1 and 20.

Damping—is a friction force of the spring. Its purpose is to control the strength of the spring bounce. The higher the value, the weaker the springs force.

Mass—is the camera mass. It controls the weight of the camera. Setting it higher will slow down SpringCam movement after its target. It is best to keep the value below 120.

It is important to understand that these three parameters simulate real world physics forces. As such, they are interdependent. When tweaking each of them, you should take into account the values of the other two, as their magnitudes dictate to a great extent how high or low should the value be of the third. Eventually the best approach is to experiment with different ratios till you find the best match.

Getting ready

Set up a basic Away3D scene using AwayTemplate.

In this demo we also use a slightly modified version of FPSController called SimpleWalker. So that, instead of a camera, it will wrap a geometry model. It is found in this article's source code folder.

How to do it...

In this program we set up environment with some geometry dispersed around so that we can have a better view of the spring camera behavior. We also create a cube primitive which imitates the target object of the SpringCam. In a real life scenario it could be a human character or a car:

SpringCamFly.as

package
{
public class SpringCamFly extends AwayTemplate
{
private var _springCam:SpringCam;
private var _camTarget:Cube;
private var _colMat:ColorMaterial;
private var _fpsWalker:SimpleWalker;
private var _angle:Number=0;
private var _numBuilds:int=5;
private var _radius:int=70;
private var _buildIter:int=1;
private var _playerMat:BitmapMaterial;
public function SpringCamFly()
{
super();
initCamera();
}
private function initCamera():void{
_springCam=new SpringCam();
_springCam.stiffness=2.05;
_springCam.damping=5;
_springCam.positionOffset=new Vector3D(0,5,0);
_springCam.zoom=5;
_springCam.mass=45;
_view.camera=_springCam;
_springCam.target=_camTarget;
}
override protected function initMaterials() : void{
_colMat=new ColorMaterial(Math.floor(Math.random()*
0xffffff));
var btmp:BitmapData=new BitmapData(32,32);
btmp.perlinNoise(5,5,12,12345,true,true);
_playerMat=new BitmapMaterial(btmp);
}
override protected function initListeners() : void{
super.initListeners();
}
override protected function initGeometry() : void{
_camTarget=new Cube({material:_playerMat});
_camTarget.width=20;
_camTarget.height=60;
_camTarget.depth=20;
_view.scene.addChild(_camTarget);
_fpsWalker=new SimpleWalker(_camTarget,stage,_view,
20,5,5);seedBuildings();
}
private function seedBuildings():void{
for(var b:int=0;b<_numBuilds;++b){
var h:Number=Math.floor(Math.random()*400+80);
_angle=Math.PI*2/_numBuilds*b;
_colMat=new ColorMaterial(Math.round(Math.random()*
0x565656));
var build:Cube=new Cube({width:20,height:h,depth:20});
build.cubeMaterials.back=_colMat;
build.cubeMaterials.bottom=_colMat;
build.cubeMaterials.front=_colMat;
build.cubeMaterials.left=_colMat;
build.cubeMaterials.right=_colMat;
build.cubeMaterials.top=_colMat;
build.movePivot(0,-build.height/2,0);
build.x=Math.cos(_angle)*_radius*_buildIter*2;
build.z=Math.sin(_angle)*_radius*_buildIter*2;
build.y=0;
_view.scene.addChild(build);
}
_buildIter++;
if(_buildIter<7){
_numBuilds*=_buildIter/2;
seedBuildings();
}
}
override protected function onEnterFrame(e:Event) : void{
super.onEnterFrame(e);
if(_springCam&&_fpsWalker){
_springCam.view;
_fpsWalker.walk();
}
}
}
}

How it works...

We set our SpringCam in the initCamera() function. Then in the initGeometry() function, we first created the camera target called _camTarget which was passed into the SimpleWalker class, which was the modified version of FPSController. The next method, named seedBuilding(), creates for us cubes of different sizes and spreads them in a circular array around the scene. In the onEnterFrame() function, we have to call the _springCam.view property which activates SpringCam spring behavior and also the walk() method of the SimpleWalker class that executes target movement control in real time as it does in FPSController.

The most important task is to find and assign optimal values for desired camera behavior to damping, stiffness, and mass properties. Also, it is important not to forget to call _springCam.view in the onEnterFrame() method, as not doing it will leave the camera motionless.

As the target moves, the camera follows it adjusting its rotation to the target's direction. The camera spring behavior is expressed when its distance to the target stretches when the last accelerates and, conversely, when its speed starts to die, the camera accelerates towards its default position relative to the target.

In the following image, we have a perlin noise textured cube that stands for the 3rd person model. As you move around the scene, the camera smoothly follows the character:

Working with Away3D Cameras

There's more...

The SpringCam can also serve as a first person camera. We can achieve this by just two lines of code. This is when lookOffset and positionOffset properties come into play.

First let's move the z-position of the camera forward so that, by default, it is located in front of the target. In initCameraFunction(), change the positionOffset to this:

_springCam.positionOffset=new Vector3D(0,15,200);

After that, we need to offset z-value for lookOffset, because currently the camera looks at the target position. Do it by writing this line:

_springCam.lookOffset=new Vector3D(0,0,400);

Here is a result, by applying the lookOffset, we get a first person view:

Working with Away3D Cameras

And we are done! Nice FPS with just two lines of code!

Summary

In this article, you got acquainted with Away3D cameras. You also learnt to set up a First Person Controller, create cool camera effects, and follow a third-person view with a spring camera.


Further resources on this subject:


Away3D 3.6 Cookbook Over 80 practical recipes for creating stunning graphics and effects with the fascinating Away3D engine
Published: May 2011
eBook Price: £20.99
Book Price: £33.99
See more
Select your format and quantity:

About the Author :


Michael Ivanov

Although initially dreaming of becoming a historian, in the middle of his undergraduate studies in the Hebrew University of Jerusalem Michael already understood that his real passion is computers. Today he has been a professional Flash developer working in the field of web and game development for more than 5 years .Being a researcher by nature with a strong hunger for technological exploration he constantly broadens his professional knowledge by exploring a wide range of technologies from different fields of computer science where real-time 3D engines are of primary interest. For the past two years Michael has been working for Neurotech Solutions Ltd, which is a rapidly growing Israeli startup bringing revolutionizing solutions in the field of research and treatment of ADHD, as lead programmer. Michael led the development of unique ADHD training game programs, which are based on the Adobe Flash platform powered by open source 3D libraries such as PV3D and Away3D. Although in everyday life he works mostly on RIA and desktop application development, his true passion is 3D graphics and game programming. In his little spare time Michael works with such technologies as Java3D Epic's UDK, Unity3D, and wide range of Flash open source 3D libraries from which Away3D is his favorite .Michael is a zealous promoter of cutting-edge 3D technologies, especially those that are open source, and misses no opportunity to speak on these subjects at local game industry events and conferences. Born in Russia, today Michael has lived and worked in Israel for more than 12 years. When he is not writing a code he enjoys playing around with his three-year old son David as well as reading history book and running.

You can find him in the Web on his personal blog http://blog.alladvanced.net, or as an active member of the Away3D developers community group at: http://groups.google.com/group/away3d-dev?pli=1

Books From Packt


Away3D 3.6 Essentialsx
Away3D 3.6 Essentials

Inkscape 0.48 Illustrator's Cookbook
Inkscape 0.48 Illustrator's Cookbook

OpenSceneGraph 3.0: Beginner's Guide
OpenSceneGraph 3.0: Beginner's Guide

Python 2.6 Graphics Cookbook
Python 2.6 Graphics Cookbook

Inkscape 0.48 Essentials for Web Designers
Inkscape 0.48 Essentials for Web Designers

OGRE 3D 1.7 Beginner's Guide
OGRE 3D 1.7 Beginner's Guide

Blender 2.5 HOTSHOT
Blender 2.5 HOTSHOT

Blender 2.5 Materials and Textures Cookbook
Blender 2.5 Materials and Textures Cookbook


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software