Author: Max Pellizzaro
Date: October 27th 2007
Update: July 12th 2009
version: 3.1.1
In this tutorial, we will study the use of the object Camera3D object. In the previous tutorial, we have just used the camera as a view point of the entire scene, but we actually never used it, neither had we gone through its properties or methods.
A Camera3D is just more than a static object – we can move it around, rotate, roll, tilt, and much more.
Knowing that you can move the Camera is very important because sometimes you will face a simple dilemma: should I move my object around or should I move my camera around?
Well, this is my personal advice: if you need to move only one object around, leaving the others in their original positions, move the single object. But if you want to move all the objects around, consider moving only the camera, and nothing else. You will soon discover that it will be easier and more efficient.
For example, if you implement a 3D maze, you can just draw the walls of the maze, and move the camera around to explore the maze.
But let's stop talking and dig into our example. I have decided to move away from the cube and start using other primitives: Line3D and a Torus.
Remember these few little changes:
The Document class must be changed to Example003.as The name of the class written inside the .as file and the name of the constructor now is: Example003
The new updated version can be found here:
example003_v3.1.1.zip
Use the code below for reference. Do not worry if it is not commented – it is being discussed below.
package
{
import flash.display.Sprite;
import flash.events.*;
import flash.ui.*;
import sandy.core.Scene3D;
import sandy.core.data.*;
import sandy.core.scenegraph.*;
import sandy.materials.*;
import sandy.materials.attributes.*;
import sandy.primitive.*;
public class Example003 extends Sprite
{
private var scene:Scene3D;
private var camera:Camera3D;
public function Example003()
{
// We create the camera
camera = new Camera3D( 300, 300 );
camera.x = 100;
camera.y = 100;
camera.z = -400;
camera.lookAt(0,0,0);
// We create the "group" that is the tree of all the visible objects
var root:Group = createScene();
// We create a Scene and we add the camera and the objects tree
scene = new Scene3D( "scene", this, camera, root );
// Listen to the heart beat and render the scene
addEventListener( Event.ENTER_FRAME, enterFrameHandler );
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressed);
}
// Create the scene graph based on the root Group of the scene
private function createScene():Group
{
// Create the root Group
var g:Group = new Group();
// first we will create a simple coordinate system
var myXLine:Line3D = new Line3D( "x-coord", new Point3D(-50, 0, 0), new Point3D( 50, 0, 0 ));
var myYLine:Line3D = new Line3D( "y-coord", new Point3D(0, -50, 0), new Point3D( 0, 50, 0 ));
var myZLine:Line3D = new Line3D( "z-coord", new Point3D(0, 0, -50), new Point3D( 0, 0, 50 ));
// Create a torus so we have something to show
var torus:Torus = new Torus( "theTorus", 120, 20);
// we define a new material
var materialAttr:MaterialAttributes = new MaterialAttributes(
new LineAttributes( 0.5, 0x2111BB, 0.4 ),
new LightAttributes( true, 0.1)
);
var material:Material = new ColorMaterial( 0xFFCC33, 1, materialAttr );
material.lightingEnable = true;
var app:Appearance = new Appearance( material );
torus.appearance = app;
torus.rotateX = 30;
torus.rotateY = 30;
// We need to add the cube to the group
g.addChild(myXLine);
g.addChild(myYLine);
g.addChild(myZLine);
g.addChild( torus );
return g;
}
// The Event.ENTER_FRAME event handler tells the world to render
private function enterFrameHandler( event : Event ) : void
{
scene.render();
}
private function keyPressed(event:KeyboardEvent):void {
switch(event.keyCode) {
case Keyboard.UP:
camera.tilt +=2;
break;
case Keyboard.DOWN:
camera.tilt -=2;
break;
case Keyboard.RIGHT:
camera.pan -=2;
break;
case Keyboard.LEFT:
camera.pan +=2;
break;
case Keyboard.CONTROL:
camera.roll +=2;
break;
case Keyboard.PAGE_DOWN:
camera.z -=5;
break;
case Keyboard.PAGE_UP:
camera.z +=5;;
break;
}
}
}
}
Now, let’s see what we have changed from the previous tutorial.
First, we have added some extra import statements in order to use some new classes (If you are not familiar with importing classes and packages, see the Adobe documentation or a third-party book on ActionScript 3.0)
import flash.display.Sprite; import flash.events.*; import flash.ui.*; import sandy.core.Scene3D; import sandy.core.data.*; import sandy.core.scenegraph.*; import sandy.materials.*; import sandy.materials.attributes.*; import sandy.primitive.*;
camera = new Camera3D( 300, 300 ); //camera.x = 100; //camera.y = 100; camera.z = -400; //camera.lookAt(0,0,0);
In the first Flash swf example below, I'm not using these settings (I have commented them out). You can uncomment them to see their effect. You just need to know that you can move the camera anywhere in the space using its three properties: x, y, z, and you can tell the camera where in the scene to point, using camera.lookAt().
// first we will create a simple coordinate system var myXLine:Line3D = new Line3D( "x-coord", new Point3D(-50, 0, 0), new Point3D( 50, 0, 0 )); var myYLine:Line3D = new Line3D( "y-coord", new Point3D(0, -50, 0), new Point3D( 0, 50, 0 )); var myZLine:Line3D = new Line3D( "z-coord", new Point3D(0, 0, -50), new Point3D( 0, 0, 50 ));
It is simple to draw the lines–just specify the starting and ending points, which you can define using the Vector class. To make the code simpler to read, I am not attaching any appearance to them, so they will be rendered as solid black lines.
var torus:Torus = new Torus( "theTorus", 120, 20);
Some of you might remember this object from geometry. The constructor takes three parameters: the name, the radius of the ring, and the radius of the cross-section of the ring (how thick the tube will be).
g.addChild(myXLine); g.addChild(myYLine); g.addChild(myZLine); g.addChild( torus);
addEventListener( Event.ENTER_FRAME, enterFrameHandler ); stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressed);
The first listener is responsible of the rendering of the entire scene, while the other is responsible to catch user input events: in this case Keyboard Event. (I’ve implemented the KEY_DOWN event, but feel free to implement any event you feel more comfortable. On the web, you can find classes that allows to hide this implementation and gives you static method to catch the key pressed, but I want to give you here the basic concepts that will allow you to understand what is going on, and let you dig in more sophisticated code).
Let’s investigate what does the keyPressed method:
switch(event.keyCode) { case Keyboard.UP: camera.tilt +=2; break; case Keyboard.DOWN: camera.tilt -=2; break; case Keyboard.RIGHT: camera.pan -=2; break; case Keyboard.LEFT: camera.pan +=2; break; case Keyboard.CONTROL: camera.roll +=2; break; case Keyboard.PAGE_DOWN: camera.z -=5; break; case Keyboard.PAGE_UP: camera.z +=5; break; }
The keyPressed method takes different actions depending on which key was pressed (the Page Up and Page Down keys won't be recognized during authoring but will be detected properly at runtime). And what it does it just use some properties of the class Camera3D. It’s easy to understand what we are doing, so we just need to see the result live.
We now have something to play with. Remember, for the Flash examples below to receive keyboard focus, you must click the mouse inside the scene. (The thin red border around each Flash example was created in the Flash authoring tool for cosmetic reasons and has nothing to do with the tutorial or Sandy.)
Once a Flash example has keyboard focus, you can use the up, down, right, or left arrow keys to move (translate) the camera in each direction. Use the PageUp and PageDown keys to zoom the camera in and out (translate it in the z direction). Use the control key to rotate the camera.
You are moving the Camera, not the object; the object is stationary, as you can see by the fact that its position is constant relative to the coordinate system.
I think you have got an idea of how the Camera3D works, and now you can play with it.