package alternativa.engine3d.controllers {
import alternativa.engine3d.*;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.physics.EllipsoidCollider;
import alternativa.types.Map;
import alternativa.types.Point3D;
import alternativa.utils.MathUtils;
import alternativa.utils.ObjectUtils;
import flash.display.DisplayObject;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.utils.getTimer;
use namespace alternativa3d;
/**
* Базовый контроллер для изменения ориентации и положения объекта в сцене с помощью клавиатуры и мыши. В классе
* реализована поддержка назначения обработчиков клавиатурных команд, а также обработчики для основных команд
* перемещения.
*/
public class ObjectController {
/**
* Имя действия для привязки клавиш движения вперёд.
*/
public static const ACTION_FORWARD:String = "ACTION_FORWARD";
/**
* Имя действия для привязки клавиш движения назад.
*/
public static const ACTION_BACK:String = "ACTION_BACK";
/**
* Имя действия для привязки клавиш движения влево.
*/
public static const ACTION_LEFT:String = "ACTION_LEFT";
/**
* Имя действия для привязки клавиш движения вправо.
*/
public static const ACTION_RIGHT:String = "ACTION_RIGHT";
/**
* Имя действия для привязки клавиш движения вверх.
*/
public static const ACTION_UP:String = "ACTION_UP";
/**
* Имя действия для привязки клавиш движения вниз.
*/
public static const ACTION_DOWN:String = "ACTION_DOWN";
/**
* Имя действия для привязки клавиш поворота вверх.
*/
public static const ACTION_PITCH_UP:String = "ACTION_PITCH_UP";
/**
* Имя действия для привязки клавиш поворота вниз.
*/
public static const ACTION_PITCH_DOWN:String = "ACTION_PITCH_DOWN";
/**
* Имя действия для привязки клавиш поворота налево.
*/
public static const ACTION_YAW_LEFT:String = "ACTION_YAW_LEFT";
/**
* Имя действия для привязки клавиш поворота направо.
*/
public static const ACTION_YAW_RIGHT:String = "ACTION_YAW_RIGHT";
/**
* Имя действия для привязки клавиш увеличения скорости.
*/
public static const ACTION_ACCELERATE:String = "ACTION_ACCELERATE";
/**
* Имя действия для привязки клавиш активации обзора мышью.
*/
public static const ACTION_MOUSE_LOOK:String = "ACTION_MOUSE_LOOK";
/**
* Флаг движения вперёд.
*/
protected var _forward:Boolean;
/**
* Флаг движения назад.
*/
protected var _back:Boolean;
/**
* Флаг движения влево.
*/
protected var _left:Boolean;
/**
* Флаг движения вправо.
*/
protected var _right:Boolean;
/**
* Флаг движения вверх.
*/
protected var _up:Boolean;
/**
* Флаг движения вниз.
*/
protected var _down:Boolean;
/**
* Флаг поворота относительно оси X в положительном направлении (взгляд вверх).
*/
protected var _pitchUp:Boolean;
/**
* Флаг поворота относительно оси X в отрицательном направлении (взгляд вниз).
*/
protected var _pitchDown:Boolean;
/**
* Флаг поворота относительно оси Z в положительном направлении (взгляд налево).
*/
protected var _yawLeft:Boolean;
/**
* Флаг активности поворота относительно оси Z в отрицательном направлении (взгляд направо).
*/
protected var _yawRight:Boolean;
/**
* Флаг активности режима ускорения.
*/
protected var _accelerate:Boolean;
/**
* Флаг активности режима поворотов мышью.
*/
protected var _mouseLookActive:Boolean;
/**
* Начальные координаты мышиного курсора в режиме mouse look.
*/
protected var startMouseCoords:Point3D = new Point3D();
/**
* Флаг активности контроллера.
*/
protected var _enabled:Boolean = true;
/**
* Источник событий клавиатуры и мыши
*/
protected var _eventsSource:DisplayObject;
/**
* Ассоциативный массив, связывающий коды клавиатурных клавиш с именами команд.
*/
protected var keyBindings:Map = new Map();
/**
* Ассоциативный массив, связывающий имена команд с реализующими их функциями. Функции должны иметь вид
* function(value:Boolean):void. Значение параметра value указывает, нажата или отпущена соответсвующая команде
* клавиша.
*/
protected var actionBindings:Map = new Map();
/**
* Флаг активности клавиатуры.
*/
protected var _keyboardEnabled:Boolean;
/**
* Флаг активности мыши.
*/
protected var _mouseEnabled:Boolean;
/**
* Общая чувствительность мыши. Коэффициент умножения чувствительности по вертикали и горизонтали.
*/
protected var _mouseSensitivity:Number = 1;
/**
* Коэффициент чувствительности мыши по вертикали. Значение угла в радианах на один пиксель перемещения мыши по вертикали.
*/
protected var _mouseSensitivityY:Number = Math.PI / 360;
/**
* Коэффициент чувствительности мыши по горизонтали. Значение угла в радианах на один пиксель перемещения мыши по горизонтали.
*/
protected var _mouseSensitivityX:Number = Math.PI / 360;
/**
* Результирующий коэффициент чувствительности мыши по вертикали. Значение угла в радианах на один пиксель перемещения мыши по вертикали.
*/
protected var _mouseCoefficientY:Number = _mouseSensitivity * _mouseSensitivityY;
/**
* Результирующий коэффициент чувствительности мыши по горизонтали. Значение угла в радианах на один пиксель перемещения мыши по горизонтали.
*/
protected var _mouseCoefficientX:Number = _mouseSensitivity * _mouseSensitivityX;
/**
* Угловая скорость поворота вокруг поперечной оси в радианах за секунду.
*/
protected var _pitchSpeed:Number = 1;
/**
* Угловая скорость поворота вокруг вертикальной оси в радианах за секунду.
*/
protected var _yawSpeed:Number = 1;
/**
* Скорость поступательного движения в единицах за секунду.
*/
protected var _speed:Number = 100;
/**
* Коэффициент увеличения скорости при соответствующей активной команде.
*/
protected var _speedMultiplier:Number = 2;
/**
* Управляемый объект.
*/
protected var _object:Object3D;
/**
* Время в секундах, прошедшее с последнего вызова метода processInput (обычно с последнего кадра).
*/
protected var lastFrameTime:uint;
/**
* Текущие координаты контроллера.
*/
protected var _coords:Point3D = new Point3D();
/**
* Индикатор движения объекта (перемещения или поворота) в текущем кадре.
*/
protected var _isMoving:Boolean;
/**
* Объект для определения столкновений.
*/
protected var _collider:EllipsoidCollider = new EllipsoidCollider();
/**
* Включение и выключение режима проверки столкновений.
*/
public var checkCollisions:Boolean;
/**
* Функция вида function():void, вызываемая при начале движения объекта. Под движением
* понимается изменение координат или ориентации.
*/
public var onStartMoving:Function;
/**
* Функция вида function():void, вызываемая при прекращении движения объекта. Под движением
* понимается изменение координат или ориентации.
*/
public var onStopMoving:Function;
// Вектор смещения
private var _displacement:Point3D = new Point3D();
/**
* Создаёт новый экземпляр контролллера.
*
* @param eventsSourceObject источник событий клавиатуры и мыши
*/
public function ObjectController(eventsSourceObject:DisplayObject) {
if (eventsSourceObject == null) {
throw new ArgumentError(ObjectUtils.getClassName(this) + ": eventsSourceObject is null");
}
_eventsSource = eventsSourceObject;
actionBindings[ACTION_FORWARD] = moveForward;
actionBindings[ACTION_BACK] = moveBack;
actionBindings[ACTION_LEFT] = moveLeft;
actionBindings[ACTION_RIGHT] = moveRight;
actionBindings[ACTION_UP] = moveUp;
actionBindings[ACTION_DOWN] = moveDown;
actionBindings[ACTION_PITCH_UP] = pitchUp;
actionBindings[ACTION_PITCH_DOWN] = pitchDown;
actionBindings[ACTION_YAW_LEFT] = yawLeft;
actionBindings[ACTION_YAW_RIGHT] = yawRight;
actionBindings[ACTION_ACCELERATE] = accelerate;
actionBindings[ACTION_MOUSE_LOOK] = setMouseLook;
keyboardEnabled = true;
mouseEnabled = true;
}
/**
* Включение и выключение контроллера. Выключенный контроллер пропускает выполнение метода processInput().
*
* @default true
*
* @see #processInput()
*/
public function get enabled():Boolean {
return _enabled;
}
/**
* @private
*/
public function set enabled(value:Boolean):void {
_enabled = value;
}
/**
* Координаты контроллера. Координаты совпадают с координатами центра эллипсоида, используемого для определения
* столкновений. Координаты управляемого объекта могут не совпадать с координатами контроллера.
*
* @see #setObjectCoords()
*/
public function get coords():Point3D {
return _coords.clone();
}
/**
* @private
*/
public function set coords(value:Point3D):void {
_coords.copy(value);
setObjectCoords();
}
/**
* Чтение координат контроллера в заданную переменную.
*
* @param point переменная, в которую записываются координаты контроллера
*/
public function readCoords(point:Point3D):void {
point.copy(_coords);
}
/**
* Управляемый объект.
*/
public function get object():Object3D {
return _object;
}
/**
* @private
* При установке объекта устанавливается сцена для коллайдера.
*/
public function set object(value:Object3D):void {
_object = value;
_collider.scene = _object == null ? null : _object.scene;
}
/**
* Объект, реализующий проверку столкновений для эллипсоида.
*/
public function get collider():EllipsoidCollider {
return _collider;
}
/**
* Активация движения вперёд.
*
* @param value true для начала движения, false для окончания
*/
public function moveForward(value:Boolean):void {
_forward = value;
}
/**
* Активация движения назад.
*
* @param value true для начала движения, false для окончания
*/
public function moveBack(value:Boolean):void {
_back = value;
}
/**
* Активация движения влево.
*
* @param value true для начала движения, false для окончания
*/
public function moveLeft(value:Boolean):void {
_left = value;
}
/**
* Активация движения вправо.
*
* @param value true для начала движения, false для окончания
*/
public function moveRight(value:Boolean):void {
_right = value;
}
/**
* Активация движения вверх.
*
* @param value true для начала движения, false для окончания
*/
public function moveUp(value:Boolean):void {
_up = value;
}
/**
* Активация движения вниз.
*
* @param value true для начала движения, false для окончания
*/
public function moveDown(value:Boolean):void {
_down = value;
}
/**
* Активация поворота вверх.
*
* @param value true для начала движения, false для окончания
*/
public function pitchUp(value:Boolean):void {
_pitchUp = value;
}
/**
* Активация поворота вниз.
*
* @param value true для начала движения, false для окончания
*/
public function pitchDown(value:Boolean):void {
_pitchDown = value;
}
/**
* Активация поворота влево.
*
* @param value true для начала движения, false для окончания
*/
public function yawLeft(value:Boolean):void {
_yawLeft = value;
}
/**
* Активация поворота вправо.
*
* @param value true для начала движения, false для окончания
*/
public function yawRight(value:Boolean):void {
_yawRight = value;
}
/**
* Активация режима увеличенной скорости.
*
* @param value true для включения ускорения, false для выключения
*/
public function accelerate(value:Boolean):void {
_accelerate = value;
}
/**
* Угловая скорость поворота вокруг поперечной оси (радианы в секунду).
*
* @default 1
*/
public function get pitchSpeed():Number {
return _pitchSpeed;
}
/**
* @private
*/
public function set pitchSpeed(spd:Number):void {
_pitchSpeed = spd;
}
/**
* Угловая скорость поворота вокруг вертикальной оси (радианы в секунду).
*
* @default 1
*/
public function get yawSpeed():Number {
return _yawSpeed;
}
/**
* @private
*/
public function set yawSpeed(spd:Number):void {
_yawSpeed = spd;
}
/**
* Скорость движения в единицах за секунду. При установке отрицательного значения берётся модуль.
*
* @default 100
*/
public function get speed():Number {
return _speed;
}
/**
* @private
*/
public function set speed(value:Number):void {
_speed = value < 0 ? -value : value;
}
/**
* Коэффициент увеличения скорости при активном действии ACTION_ACCELERATE.
*
* @default 2
*/
public function get speedMultiplier():Number {
return _speedMultiplier;
}
/**
* @private
*/
public function set speedMultiplier(value:Number):void {
_speedMultiplier = value;
}
/**
* Чувствительность мыши — коэффициент умножения mouseSensitivityX и mouseSensitivityY.
*
* @default 1
*
* @see #mouseSensitivityY()
* @see #mouseSensitivityX()
*/
public function get mouseSensitivity():Number {
return _mouseSensitivity;
}
/**
* @private
*/
public function set mouseSensitivity(sensitivity:Number):void {
_mouseSensitivity = sensitivity;
_mouseCoefficientY = _mouseSensitivity * _mouseSensitivityY;
_mouseCoefficientX = _mouseSensitivity * _mouseSensitivityX;
}
/**
* Чувтсвительность мыши по вертикали.
*
* @default Math.PI / 360
*
* @see #mouseSensitivity()
* @see #mouseSensitivityX()
*/
public function get mouseSensitivityY():Number {
return _mouseSensitivityY;
}
/**
* @private
*/
public function set mouseSensitivityY(value:Number):void {
_mouseSensitivityY = value;
_mouseCoefficientY = _mouseSensitivity * _mouseSensitivityY;
}
/**
* Чувтсвительность мыши по горизонтали.
*
* @default Math.PI / 360
*
* @see #mouseSensitivity()
* @see #mouseSensitivityY()
*/
public function get mouseSensitivityX():Number {
return _mouseSensitivityX;
}
/**
* @private
*/
public function set mouseSensitivityX(value:Number):void {
_mouseSensitivityX = value;
_mouseCoefficientX = _mouseSensitivity * _mouseSensitivityX;
}
/**
* Включение/выключение режима вращения объекта мышью. При включении режима вполняется метод startMouseLook(),
* при выключении — stoptMouseLook().
*
* @see #startMouseLook()
* @see #stopMouseLook()
*/
public function setMouseLook(value:Boolean):void {
if (_mouseLookActive != value) {
_mouseLookActive = value;
if (_mouseLookActive) {
startMouseLook();
} else {
stopMouseLook();
}
}
}
/**
* Метод выполняет необходимые действия при включении режима вращения объекта мышью.
* Реализация по умолчанию записывает начальные глобальные координаты курсора мыши в переменную startMouseCoords.
*
* @see #startMouseCoords
* @see #setMouseLook()
* @see #stopMouseLook()
*/
protected function startMouseLook():void {
startMouseCoords.x = _eventsSource.stage.mouseX;
startMouseCoords.y = _eventsSource.stage.mouseY;
}
/**
* Метод выполняет необходимые действия при выключении вращения объекта мышью. Реализация по умолчанию не делает
* ничего.
*
* @see #setMouseLook()
* @see #startMouseLook()
*/
protected function stopMouseLook():void {
}
/**
* Метод выполняет обработку всех воздействий на объект. Если объект не установлен или свойство enabled
* равно false, метод не выполняется.
*
* Алгоритм работы метода следующий: *
registerKeyboardListeners,
* при выключении — unregisterKeyboardListeners.
*
* @see #registerKeyboardListeners()
* @see #unregisterKeyboardListeners()
*/
public function get keyboardEnabled():Boolean {
return _keyboardEnabled;
}
/**
* @private
*/
public function set keyboardEnabled(value:Boolean):void {
if (_keyboardEnabled != value) {
_keyboardEnabled = value;
if (_keyboardEnabled) {
registerKeyboardListeners();
} else {
unregisterKeyboardListeners();
}
}
}
/**
* @private
* Запуск обработчиков клавиатурных команд.
*/
private function onKeyboardEvent(e:KeyboardEvent):void {
var method:Function = keyBindings[e.keyCode];
if (method != null) {
method.call(this, e.type == KeyboardEvent.KEY_DOWN);
}
}
/**
* Регистрация необходимых обработчиков при включении обработки клавиатурных событий.
*
* @see #unregisterKeyboardListeners()
*/
protected function registerKeyboardListeners():void {
_eventsSource.addEventListener(KeyboardEvent.KEY_DOWN, onKeyboardEvent);
_eventsSource.addEventListener(KeyboardEvent.KEY_UP, onKeyboardEvent);
}
/**
* Удаление обработчиков при выключении обработки клавиатурных событий.
*
* @see #registerKeyboardListeners()
*/
protected function unregisterKeyboardListeners():void {
_eventsSource.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyboardEvent);
_eventsSource.removeEventListener(KeyboardEvent.KEY_UP, onKeyboardEvent);
}
/**
* Включение и выключение обработки мышиных событий. При включении выполняется метод registerMouseListeners,
* при выключении — unregisterMouseListeners.
*
* @see #registerMouseListeners()
* @see #unregisterMouseListeners()
*/
public function get mouseEnabled():Boolean {
return _mouseEnabled;
}
/**
* @private
*/
public function set mouseEnabled(value:Boolean):void {
if (_mouseEnabled != value) {
_mouseEnabled = value;
if (_mouseEnabled) {
registerMouseListeners();
} else {
unregisterMouseListeners();
}
}
}
/**
* Регистрация необходимых обработчиков при включении обработки мышиных событий.
*
* @see #unregisterMouseListeners()
*/
protected function registerMouseListeners():void {
_eventsSource.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
}
/**
* Удаление используемых обработчиков при выключении обработки мышиных событий.
*
* @see #registerMouseListeners()
*/
protected function unregisterMouseListeners():void {
_eventsSource.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
_eventsSource.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
/**
* Активация mouselook
*/
private function onMouseDown(e:MouseEvent):void {
setMouseLook(true);
_eventsSource.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
/**
* Отключение mouselook
*/
private function onMouseUp(e:MouseEvent):void {
setMouseLook(false);
_eventsSource.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
/**
* Установка координат управляемого объекта в зависимости от текущих координат контроллера.
*/
protected function setObjectCoords():void {
_object.coords = _coords;
}
/**
* Индикатор режима увеличенной скорости.
*/
public function get accelerated():Boolean {
return _accelerate;
}
}
}