FirstPersonCamera3D updated to v1.5
January 18th, 2009
Hi All:
—
[EDIT : CLICK HERE FIND THE LATEST VERSION]
—
I’ve made a few changes to the FirstPersonCamera3D class I first introduced in this post. The class now has two extra properties:
myCamera.removeMappedKeys()
and
myCamera.mode
myCamera.mode sets or returns the camera mode. The mode can be either auto or manual. Setting the mode to auto will make the camera look around automatically based on the mouse movement. Setting it to manual will make the camera look around only when you click and drag.
myCamera.removeMappedKeys() unregisters the keys used for movement using the mapKeys method. Remember, if no keys are specified, the arrow keys are used as default.
Here’s a sample setup:
firstPersonCamera = new FirstPersonCamera3D();
firstPersonCamera.initialize(stage);
firstPersonCamera.mode = "manual"
firstPersonCamera.y = 750;
firstPersonCamera.mapKeys();
firstPersonCamera.initialize(stage);
firstPersonCamera.mode = "manual"
firstPersonCamera.y = 750;
firstPersonCamera.mapKeys();
Here’s the new class:
package
{
import caurina.transitions.Tweener;
import flash.display.Stage;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.utils.*;
import org.papervision3d.cameras.Camera3D;
/**
* Camera3D object with First Person functionality
* @author Reynaldo Columna
*
*/
public class FirstPersonCamera3D extends Camera3D
{
protected static const UP_ARROW:int = 38;
protected static const LEFT_ARROW:int = 37;
protected static const RIGHT_ARROW:int = 39;
protected static const DOWN_ARROW:int = 40;
private static var FORWARD_KEY:int;
private static var LEFT_KEY:int;
private static var RIGHT_KEY:int;
private static var BACK_KEY:int;
protected var _sensitivity:Number;
protected var _maxRotationX:Number;
protected var _maxRotationY:Number;
protected var _stageReference:Stage;
protected var _moveIncrement:Number;
protected var _mode:String;
private var forwardTrigger:uint;
private var leftTrigger:uint;
private var rightTrigger:uint;
private var backTrigger:uint;
private var intervals:Array;
private var mouseDownPositionX:Number;
private var mouseDownPositionY:Number;
private var currentRotationY:Number;
private var currentRotationX:Number;
private var movingForward:Boolean;
private var movingLeft:Boolean;
private var movingRight:Boolean;
private var movingBack:Boolean;
private var doManualLook:Boolean;
/**
* Constructs the camera just as you would with a Camera3D object
* @param fov
* @param near
* @param far
* @param useCulling
* @param useProjection
*
*/
public function FirstPersonCamera3D(fov:Number=60, near:Number=10, far:Number=5000, useCulling:Boolean=false, useProjection:Boolean=false)
{
super(fov, near, far, useCulling, useProjection);
intervals = [forwardTrigger, leftTrigger, rightTrigger, backTrigger];
movingForward = false;
movingLeft = false;
movingRight = false;
movingBack = false;
}
/**
* Initializes the Camera functionality
* @param $stage A reference to the stage in order to add the key listeners
* @param $moveIncrement A number which represents the move increment
*
*/
public function initialize($stage:Stage, $moveIncrement:Number = 250):void
{
stageReference = $stage;
moveIncrement = $moveIncrement;
addListeners();
}
/**
* Maps the keys to be used for movement. If no keys are specified, the arrow keys are used as defauilt.
* @param $keys An object with the following values: forward, back, left and right. Each should be a key code.
*
*/
public function mapKeys($keys:Object = null):void
{
FirstPersonCamera3D.FORWARD_KEY = (($keys != null) ? $keys.forward : FirstPersonCamera3D.UP_ARROW) || FirstPersonCamera3D.UP_ARROW;
FirstPersonCamera3D.BACK_KEY = (($keys != null) ? $keys.back : FirstPersonCamera3D.DOWN_ARROW) || FirstPersonCamera3D.DOWN_ARROW;
FirstPersonCamera3D.LEFT_KEY = (($keys != null) ? $keys.left : FirstPersonCamera3D.LEFT_ARROW) || FirstPersonCamera3D.LEFT_ARROW;
FirstPersonCamera3D.RIGHT_KEY = (($keys != null) ? $keys.right : FirstPersonCamera3D.RIGHT_ARROW) || FirstPersonCamera3D.RIGHT_ARROW;
}
/**
* Unregisters mapped keys
*
*/
public function removeKeyMapping():void
{
clearInterval(leftTrigger);
clearInterval(rightTrigger);
clearInterval(backTrigger);
clearInterval(backTrigger);
FirstPersonCamera3D.FORWARD_KEY = FirstPersonCamera3D.BACK_KEY = FirstPersonCamera3D.LEFT_KEY = FirstPersonCamera3D.RIGHT_KEY = -1;
}
/**
* Makes the camera "look" around based on the mouse position
* @param $sensitivity Number 0 to 1 representing how sensible the camera is to the mouse movement.
* @param $maxRotationX Number representing the max horizontal rotation (0 to 360)
* @param $maxRotationY Number representing the max vertical rotation (0 to 360)
*
*/
public function look($sensitivity:Number = 0.5, $maxRotationX:Number = 90, $maxRotationY:Number = 90):void
{
sensitivity = $sensitivity;
maxRotationX = $maxRotationX;
maxRotationY = $maxRotationY;
if(mode == "auto"){
var horizontalDegrees:Number = map(stageReference.mouseX, 0, stageReference.stageWidth, maxRotationX * -1, maxRotationX);
var verticalDegrees:Number = map(stageReference.mouseY, 0, stageReference.stageHeight, maxRotationY * -1, maxRotationY);
rotationX += (verticalDegrees - rotationX) * sensitivity;
rotationY += (horizontalDegrees - rotationY) * sensitivity;
}else if(mode == "manual"){
if(doManualLook){
var horizontalDistance:Number = stageReference.mouseX - mouseDownPositionX;
var verticalDistance:Number = stageReference.mouseY - mouseDownPositionY;
var finalRotationX:Number = currentRotationX + (verticalDistance * (sensitivity * .5));
var finalRotationY:Number = currentRotationY + (horizontalDistance * (sensitivity * .5));
rotationX += (finalRotationX - rotationX) * sensitivity;
rotationY += (finalRotationY - rotationY) * sensitivity;
}
}
}
/**
* Clears the camera
*
*/
public function clear():void
{
removeListeners();
for(var a:Number = 0; a<intervals.length; a++){
if(intervals[a] != undefined){
clearInterval(intervals[a]);
intervals[a] = null;
}
}
intervals = null;
stageReference = null;
}
private function addListeners():void
{
stageReference.addEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown);
stageReference.addEventListener(KeyboardEvent.KEY_UP, handleKeyUp);
}
private function removeListeners():void
{
stageReference.removeEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown);
stageReference.removeEventListener(KeyboardEvent.KEY_UP, handleKeyUp);
}
private function move($direction:String, $distance:Number):void
{
var distanceZ:Number
var distanceX:Number
switch ($direction) {
case "FORWARD" :
distanceZ = $distance * Math.cos(rotationY*(Math.PI/180));
distanceX = $distance * Math.sin(rotationY*(Math.PI/180));
Tweener.addTween(this, {z:z+distanceZ, x:x+distanceX, time:0.5, transition:"linear"});
break;
case "REVERSE" :
distanceZ = $distance * Math.cos(rotationY*(Math.PI/180));
distanceX = $distance * Math.sin(rotationY*(Math.PI/180));
Tweener.addTween(this, {z:z-distanceZ, x:x-distanceX, time:0.5, transition:"linear"});
break;
case "STRAFELEFT" :
distanceZ = $distance * Math.sin(rotationY*(Math.PI/180));
distanceX = $distance * Math.cos(rotationY*(Math.PI/180));
Tweener.addTween(this, {z:z+distanceZ, x:x-distanceX, time:0.5, transition:"linear"});
break;
case "STRAFERIGHT" :
distanceZ = $distance * Math.sin(rotationY*(Math.PI/180));
distanceX = $distance * Math.cos(rotationY*(Math.PI/180));
Tweener.addTween(this, {z:z-distanceZ, x:x+distanceX, time:0.5, transition:"linear"});
break;
}
}
private function map(value:Number, min1:Number, max1:Number, min2:Number, max2:Number):Number
{
return min2 + (max2 - min2) * (value - min1) / (max1 - min1);
}
/*
Properties
*/
/**
* Sets or returns the look sensitivity
* @return Number
*
*/
public function get sensitivity():Number { return _sensitivity }
public function set sensitivity(value:Number):void { _sensitivity = value; }
/**
* Sets or returns the maximum horizontal rotation
* @return Number
*
*/
public function get maxRotationX():Number { return _maxRotationX }
public function set maxRotationX(value:Number):void { _maxRotationX = value; }
/**
* Sets or returns the maximum vertical rotation
* @return Number
*
*/
public function get maxRotationY():Number { return _maxRotationY }
public function set maxRotationY(value:Number):void { _maxRotationY = value; }
/**
* Sets or returns a reference to the stage
* @return Number
*
*/
public function get stageReference():Stage { return _stageReference }
public function set stageReference(value:Stage):void { _stageReference = value; }
/**
* Sets or returns the movement increment
* @return Number
*
*/
public function get moveIncrement():Number { return _moveIncrement }
public function set moveIncrement(value:Number):void { _moveIncrement = value; }
/**
* Sets or returns the camera mode. The mode can be either auto or manual. Setting the mode to
* auto will make the camera look around automatically based on the mouse movement. Setting it
* to manual will make the camera look around only when you click and drag.
* @return
*
*/
public function get mode():String
{
return _mode
}
public function set mode(value:String):void
{
_mode = value;
switch(_mode.toLowerCase())
{
case "manual" :
stageReference.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
stageReference.addEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
break
case "auto" :
stageReference.removeEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
stageReference.removeEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
break
}
}
/*
Handlers
*/
private function handleKeyDown(evt:KeyboardEvent):void
{
switch(evt.keyCode)
{
case FirstPersonCamera3D.FORWARD_KEY:
if(!movingForward){
forwardTrigger = setInterval(move, 100, "FORWARD", moveIncrement);
movingForward = true;
}
break;
case FirstPersonCamera3D.BACK_KEY:
if(!movingBack){
backTrigger = setInterval(move, 100, "REVERSE", moveIncrement);
movingBack = true;
}
break;
case FirstPersonCamera3D.LEFT_KEY:
if(!movingLeft){
leftTrigger = setInterval(move, 100, "STRAFELEFT", moveIncrement);
movingLeft = true;
}
break;
case FirstPersonCamera3D.RIGHT_KEY:
if(!movingRight){
rightTrigger = setInterval(move, 100, "STRAFERIGHT", moveIncrement);
movingRight = true;
}
break;
}
}
private function handleKeyUp(evt:KeyboardEvent):void
{
switch(evt.keyCode)
{
case FirstPersonCamera3D.FORWARD_KEY:
clearInterval(forwardTrigger);
movingForward = false;
break;
case FirstPersonCamera3D.BACK_KEY:
clearInterval(backTrigger);
movingBack = false;
break;
case FirstPersonCamera3D.LEFT_KEY:
clearInterval(leftTrigger);
movingLeft = false;
break;
case FirstPersonCamera3D.RIGHT_KEY:
clearInterval(rightTrigger);
movingRight = false;
break;
}
}
private function handleMouseDown($event:MouseEvent):void
{
mouseDownPositionX = stageReference.mouseX;
mouseDownPositionY = stageReference.mouseY;
currentRotationY = rotationY;
currentRotationX = rotationX;
doManualLook = true;
}
private function handleMouseUp($event:MouseEvent):void
{
doManualLook = false;
}
}
}
{
import caurina.transitions.Tweener;
import flash.display.Stage;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.utils.*;
import org.papervision3d.cameras.Camera3D;
/**
* Camera3D object with First Person functionality
* @author Reynaldo Columna
*
*/
public class FirstPersonCamera3D extends Camera3D
{
protected static const UP_ARROW:int = 38;
protected static const LEFT_ARROW:int = 37;
protected static const RIGHT_ARROW:int = 39;
protected static const DOWN_ARROW:int = 40;
private static var FORWARD_KEY:int;
private static var LEFT_KEY:int;
private static var RIGHT_KEY:int;
private static var BACK_KEY:int;
protected var _sensitivity:Number;
protected var _maxRotationX:Number;
protected var _maxRotationY:Number;
protected var _stageReference:Stage;
protected var _moveIncrement:Number;
protected var _mode:String;
private var forwardTrigger:uint;
private var leftTrigger:uint;
private var rightTrigger:uint;
private var backTrigger:uint;
private var intervals:Array;
private var mouseDownPositionX:Number;
private var mouseDownPositionY:Number;
private var currentRotationY:Number;
private var currentRotationX:Number;
private var movingForward:Boolean;
private var movingLeft:Boolean;
private var movingRight:Boolean;
private var movingBack:Boolean;
private var doManualLook:Boolean;
/**
* Constructs the camera just as you would with a Camera3D object
* @param fov
* @param near
* @param far
* @param useCulling
* @param useProjection
*
*/
public function FirstPersonCamera3D(fov:Number=60, near:Number=10, far:Number=5000, useCulling:Boolean=false, useProjection:Boolean=false)
{
super(fov, near, far, useCulling, useProjection);
intervals = [forwardTrigger, leftTrigger, rightTrigger, backTrigger];
movingForward = false;
movingLeft = false;
movingRight = false;
movingBack = false;
}
/**
* Initializes the Camera functionality
* @param $stage A reference to the stage in order to add the key listeners
* @param $moveIncrement A number which represents the move increment
*
*/
public function initialize($stage:Stage, $moveIncrement:Number = 250):void
{
stageReference = $stage;
moveIncrement = $moveIncrement;
addListeners();
}
/**
* Maps the keys to be used for movement. If no keys are specified, the arrow keys are used as defauilt.
* @param $keys An object with the following values: forward, back, left and right. Each should be a key code.
*
*/
public function mapKeys($keys:Object = null):void
{
FirstPersonCamera3D.FORWARD_KEY = (($keys != null) ? $keys.forward : FirstPersonCamera3D.UP_ARROW) || FirstPersonCamera3D.UP_ARROW;
FirstPersonCamera3D.BACK_KEY = (($keys != null) ? $keys.back : FirstPersonCamera3D.DOWN_ARROW) || FirstPersonCamera3D.DOWN_ARROW;
FirstPersonCamera3D.LEFT_KEY = (($keys != null) ? $keys.left : FirstPersonCamera3D.LEFT_ARROW) || FirstPersonCamera3D.LEFT_ARROW;
FirstPersonCamera3D.RIGHT_KEY = (($keys != null) ? $keys.right : FirstPersonCamera3D.RIGHT_ARROW) || FirstPersonCamera3D.RIGHT_ARROW;
}
/**
* Unregisters mapped keys
*
*/
public function removeKeyMapping():void
{
clearInterval(leftTrigger);
clearInterval(rightTrigger);
clearInterval(backTrigger);
clearInterval(backTrigger);
FirstPersonCamera3D.FORWARD_KEY = FirstPersonCamera3D.BACK_KEY = FirstPersonCamera3D.LEFT_KEY = FirstPersonCamera3D.RIGHT_KEY = -1;
}
/**
* Makes the camera "look" around based on the mouse position
* @param $sensitivity Number 0 to 1 representing how sensible the camera is to the mouse movement.
* @param $maxRotationX Number representing the max horizontal rotation (0 to 360)
* @param $maxRotationY Number representing the max vertical rotation (0 to 360)
*
*/
public function look($sensitivity:Number = 0.5, $maxRotationX:Number = 90, $maxRotationY:Number = 90):void
{
sensitivity = $sensitivity;
maxRotationX = $maxRotationX;
maxRotationY = $maxRotationY;
if(mode == "auto"){
var horizontalDegrees:Number = map(stageReference.mouseX, 0, stageReference.stageWidth, maxRotationX * -1, maxRotationX);
var verticalDegrees:Number = map(stageReference.mouseY, 0, stageReference.stageHeight, maxRotationY * -1, maxRotationY);
rotationX += (verticalDegrees - rotationX) * sensitivity;
rotationY += (horizontalDegrees - rotationY) * sensitivity;
}else if(mode == "manual"){
if(doManualLook){
var horizontalDistance:Number = stageReference.mouseX - mouseDownPositionX;
var verticalDistance:Number = stageReference.mouseY - mouseDownPositionY;
var finalRotationX:Number = currentRotationX + (verticalDistance * (sensitivity * .5));
var finalRotationY:Number = currentRotationY + (horizontalDistance * (sensitivity * .5));
rotationX += (finalRotationX - rotationX) * sensitivity;
rotationY += (finalRotationY - rotationY) * sensitivity;
}
}
}
/**
* Clears the camera
*
*/
public function clear():void
{
removeListeners();
for(var a:Number = 0; a<intervals.length; a++){
if(intervals[a] != undefined){
clearInterval(intervals[a]);
intervals[a] = null;
}
}
intervals = null;
stageReference = null;
}
private function addListeners():void
{
stageReference.addEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown);
stageReference.addEventListener(KeyboardEvent.KEY_UP, handleKeyUp);
}
private function removeListeners():void
{
stageReference.removeEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown);
stageReference.removeEventListener(KeyboardEvent.KEY_UP, handleKeyUp);
}
private function move($direction:String, $distance:Number):void
{
var distanceZ:Number
var distanceX:Number
switch ($direction) {
case "FORWARD" :
distanceZ = $distance * Math.cos(rotationY*(Math.PI/180));
distanceX = $distance * Math.sin(rotationY*(Math.PI/180));
Tweener.addTween(this, {z:z+distanceZ, x:x+distanceX, time:0.5, transition:"linear"});
break;
case "REVERSE" :
distanceZ = $distance * Math.cos(rotationY*(Math.PI/180));
distanceX = $distance * Math.sin(rotationY*(Math.PI/180));
Tweener.addTween(this, {z:z-distanceZ, x:x-distanceX, time:0.5, transition:"linear"});
break;
case "STRAFELEFT" :
distanceZ = $distance * Math.sin(rotationY*(Math.PI/180));
distanceX = $distance * Math.cos(rotationY*(Math.PI/180));
Tweener.addTween(this, {z:z+distanceZ, x:x-distanceX, time:0.5, transition:"linear"});
break;
case "STRAFERIGHT" :
distanceZ = $distance * Math.sin(rotationY*(Math.PI/180));
distanceX = $distance * Math.cos(rotationY*(Math.PI/180));
Tweener.addTween(this, {z:z-distanceZ, x:x+distanceX, time:0.5, transition:"linear"});
break;
}
}
private function map(value:Number, min1:Number, max1:Number, min2:Number, max2:Number):Number
{
return min2 + (max2 - min2) * (value - min1) / (max1 - min1);
}
/*
Properties
*/
/**
* Sets or returns the look sensitivity
* @return Number
*
*/
public function get sensitivity():Number { return _sensitivity }
public function set sensitivity(value:Number):void { _sensitivity = value; }
/**
* Sets or returns the maximum horizontal rotation
* @return Number
*
*/
public function get maxRotationX():Number { return _maxRotationX }
public function set maxRotationX(value:Number):void { _maxRotationX = value; }
/**
* Sets or returns the maximum vertical rotation
* @return Number
*
*/
public function get maxRotationY():Number { return _maxRotationY }
public function set maxRotationY(value:Number):void { _maxRotationY = value; }
/**
* Sets or returns a reference to the stage
* @return Number
*
*/
public function get stageReference():Stage { return _stageReference }
public function set stageReference(value:Stage):void { _stageReference = value; }
/**
* Sets or returns the movement increment
* @return Number
*
*/
public function get moveIncrement():Number { return _moveIncrement }
public function set moveIncrement(value:Number):void { _moveIncrement = value; }
/**
* Sets or returns the camera mode. The mode can be either auto or manual. Setting the mode to
* auto will make the camera look around automatically based on the mouse movement. Setting it
* to manual will make the camera look around only when you click and drag.
* @return
*
*/
public function get mode():String
{
return _mode
}
public function set mode(value:String):void
{
_mode = value;
switch(_mode.toLowerCase())
{
case "manual" :
stageReference.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
stageReference.addEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
break
case "auto" :
stageReference.removeEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
stageReference.removeEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
break
}
}
/*
Handlers
*/
private function handleKeyDown(evt:KeyboardEvent):void
{
switch(evt.keyCode)
{
case FirstPersonCamera3D.FORWARD_KEY:
if(!movingForward){
forwardTrigger = setInterval(move, 100, "FORWARD", moveIncrement);
movingForward = true;
}
break;
case FirstPersonCamera3D.BACK_KEY:
if(!movingBack){
backTrigger = setInterval(move, 100, "REVERSE", moveIncrement);
movingBack = true;
}
break;
case FirstPersonCamera3D.LEFT_KEY:
if(!movingLeft){
leftTrigger = setInterval(move, 100, "STRAFELEFT", moveIncrement);
movingLeft = true;
}
break;
case FirstPersonCamera3D.RIGHT_KEY:
if(!movingRight){
rightTrigger = setInterval(move, 100, "STRAFERIGHT", moveIncrement);
movingRight = true;
}
break;
}
}
private function handleKeyUp(evt:KeyboardEvent):void
{
switch(evt.keyCode)
{
case FirstPersonCamera3D.FORWARD_KEY:
clearInterval(forwardTrigger);
movingForward = false;
break;
case FirstPersonCamera3D.BACK_KEY:
clearInterval(backTrigger);
movingBack = false;
break;
case FirstPersonCamera3D.LEFT_KEY:
clearInterval(leftTrigger);
movingLeft = false;
break;
case FirstPersonCamera3D.RIGHT_KEY:
clearInterval(rightTrigger);
movingRight = false;
break;
}
}
private function handleMouseDown($event:MouseEvent):void
{
mouseDownPositionX = stageReference.mouseX;
mouseDownPositionY = stageReference.mouseY;
currentRotationY = rotationY;
currentRotationX = rotationX;
doManualLook = true;
}
private function handleMouseUp($event:MouseEvent):void
{
doManualLook = false;
}
}
}
is it hittest to object?
Very nice rey, i bet people will love this one:)
@Deneme hittest to object? What are you referring to exactly?
@FlashBookmarks Thanks Ringo! I’m hoping they’ll come back with suggestions and enhancements. Right now is dependent on Tweener, but I’m probably gonna change that soon.
how can i make distance between cameran and model.
@Deneme Right now the class does not support collisions or hittest. I am working on a version 2 of the class which will:
1) Have a “setBounds” method to threshold the furthest most front, back, left and right bounds.
2) Be tween engine independent.
and
3) Have the ability set objects to “collide” with.
This version should be ready sometime this week. 1 and 2 are already done, working on 3 and some other features.
hi there, can anyone please explain to me why I cant load my exported swf, which work perfectly in pv3d swf?
with this kind of error, it already taken me three days.
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at FirstPersonCamera3D/::addListeners()
at FirstPersonCamera3D/initialize()
at Era01/::setupCamera()
at Era01/init()
at Era01$iinit()