mirror of
				https://github.com/MapMakersAndProgrammers/alternativa3d-archive.git
				synced 2025-10-30 17:05:17 -07:00 
			
		
		
		
	more versions added
This commit is contained in:
		| @@ -0,0 +1,29 @@ | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 103 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/controllers | ||||
| END | ||||
| FlyController.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 120 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/controllers/FlyController.as | ||||
| END | ||||
| WalkController.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 121 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/controllers/WalkController.as | ||||
| END | ||||
| ObjectController.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 123 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/controllers/ObjectController.as | ||||
| END | ||||
| CameraController.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 123 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/controllers/CameraController.as | ||||
| END | ||||
| @@ -0,0 +1,76 @@ | ||||
| 8 | ||||
|  | ||||
| dir | ||||
| 46043 | ||||
| http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/controllers | ||||
| http://svndev.alternativaplatform.com | ||||
|  | ||||
|  | ||||
|  | ||||
| 2008-09-08T06:50:26.103978Z | ||||
| 468 | ||||
| mike | ||||
|  | ||||
|  | ||||
| svn:special svn:externals svn:needs-lock | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| d9e2387a-1f3e-40e2-b57f-9df5970a2fa5 | ||||
|  | ||||
| FlyController.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 819162f5f890b890eca280272d1c4dd8 | ||||
| 2008-08-29T11:41:21.451513Z | ||||
| 304 | ||||
| wolf | ||||
|  | ||||
| WalkController.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 67cb497ffc9a536d65065dacb16a99be | ||||
| 2008-09-08T06:50:26.103978Z | ||||
| 468 | ||||
| mike | ||||
|  | ||||
| ObjectController.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 20fdfabe14732c8fb81c041f142da4a4 | ||||
| 2008-08-29T11:41:21.451513Z | ||||
| 304 | ||||
| wolf | ||||
|  | ||||
| CameraController.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| cc1c7ecdc58c115dcdab53d2ea0ef1d9 | ||||
| 2008-08-29T11:41:21.451513Z | ||||
| 304 | ||||
| wolf | ||||
|  | ||||
| @@ -0,0 +1 @@ | ||||
| 8 | ||||
| @@ -0,0 +1,889 @@ | ||||
| package alternativa.engine3d.controllers { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Camera3D; | ||||
| 	import alternativa.engine3d.physics.EllipsoidCollider; | ||||
| 	import alternativa.types.Map; | ||||
| 	import alternativa.types.Matrix3D; | ||||
| 	import alternativa.types.Point3D; | ||||
| 	import alternativa.types.Set; | ||||
| 	import alternativa.utils.KeyboardUtils; | ||||
| 	import alternativa.utils.MathUtils; | ||||
| 	 | ||||
| 	import flash.display.DisplayObject; | ||||
| 	import flash.events.KeyboardEvent; | ||||
| 	import flash.events.MouseEvent; | ||||
| 	import flash.geom.Point; | ||||
| 	import flash.utils.getTimer; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
|  | ||||
| 	/** | ||||
| 	 * Контроллер камеры. Контроллер обеспечивает управление движением и поворотами камеры с использованием | ||||
| 	 * клавиатуры и мыши, а также предоставляет простую проверку столкновений камеры с объектами сцены. | ||||
| 	 */	 | ||||
| 	public class CameraController	{ | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш движения вперёд. | ||||
| 		 */ | ||||
| 		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_ROLL_LEFT:String = "ACTION_ROLL_LEFT"; | ||||
| //		public static const ACTION_ROLL_RIGHT:String = "ACTION_ROLL_RIGHT"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш увеличения угла тангажа. | ||||
| 		 */ | ||||
| 		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"; | ||||
|  | ||||
| 		// флаги действий | ||||
| 		private var _forward:Boolean; | ||||
| 		private var _back:Boolean; | ||||
| 		private var _left:Boolean; | ||||
| 		private var _right:Boolean; | ||||
| 		private var _up:Boolean; | ||||
| 		private var _down:Boolean; | ||||
| 		private var _pitchUp:Boolean; | ||||
| 		private var _pitchDown:Boolean; | ||||
| 		private var _yawLeft:Boolean; | ||||
| 		private var _yawRight:Boolean; | ||||
| 		private var _accelerate:Boolean; | ||||
| 		 | ||||
| 		private var _moveLocal:Boolean = true; | ||||
|  | ||||
| 		// Флаг включения управления камерой | ||||
| 		private var _controlsEnabled:Boolean = false; | ||||
|  | ||||
| 		// Значение таймера в начале прошлого кадра | ||||
| 		private var lastFrameTime:uint; | ||||
| 		 | ||||
| 		// Чувствительность обзора мышью. Коэффициент умножения базовых коэффициентов поворотов. | ||||
| 		private var _mouseSensitivity:Number = 1; | ||||
| 		// Коэффициент поворота камеры по тангажу. Значение угла в радианах на один пиксель перемещения мыши по вертикали. | ||||
| 		private var _mousePitch:Number = Math.PI / 360; | ||||
| 		// Результирующий коэффициент поворота камеры мышью по тангажу | ||||
| 		private var _mousePitchCoeff:Number = _mouseSensitivity * _mousePitch; | ||||
| 		// Коэффициент поворота камеры по рысканью. Значение угла в радианах на один пиксель перемещения мыши по горизонтали. | ||||
| 		private var _mouseYaw:Number = Math.PI / 360; | ||||
| 		// Результирующий коэффициент поворота камеры мышью по расканью | ||||
| 		private var _mouseYawCoeff:Number = _mouseSensitivity * _mouseYaw; | ||||
| 		 | ||||
| 		// Вспомогательные переменные для обзора мышью | ||||
| 		private var mouseLookActive:Boolean; | ||||
| 		private var startDragCoords:Point = new Point(); | ||||
| 		private var currentDragCoords:Point = new Point(); | ||||
| 		private var prevDragCoords:Point = new Point(); | ||||
| 		private var startRotX:Number; | ||||
| 		private var startRotZ:Number; | ||||
|  | ||||
| 		// Скорость изменения тангажа в радианах за секунду при управлении с клавиатуры | ||||
| 		private var _pitchSpeed:Number = 1; | ||||
| 		// Скорость изменения рысканья в радианах за секунду при управлении с клавиатуры | ||||
| 		private var _yawSpeed:Number = 1; | ||||
| 		// Скорость изменения крена в радианах за секунду при управлении с клавиатуры | ||||
| //		public var bankSpeed:Number = 2; | ||||
| //		private var bankMatrix:Matrix3D = new Matrix3D(); | ||||
|  | ||||
| 		// Скорость поступательного движения в единицах за секунду | ||||
| 		private var _speed:Number = 100; | ||||
| 		// Коэффициент увеличения скорости при соответствующей нажатой клавише | ||||
| 		private var _speedMultiplier:Number = 2; | ||||
| 		 | ||||
| 		private var velocity:Point3D = new Point3D(); | ||||
| 		private var destination:Point3D = new Point3D(); | ||||
| 		 | ||||
| 		private var _fovStep:Number = Math.PI / 180; | ||||
| 		private var _zoomMultiplier:Number = 0.1; | ||||
| 		 | ||||
| 		// Привязка клавиш к действиям | ||||
| 		private var keyBindings:Map = new Map(); | ||||
| 		// Привязка действий к обработчикам | ||||
| 		private var actionBindings:Map = new Map(); | ||||
| 		 | ||||
| 		// Источник событий клавиатуры и мыши | ||||
| 		private var _eventsSource:DisplayObject; | ||||
| 		// Управляемая камера | ||||
| 		private var _camera:Camera3D;  | ||||
|  | ||||
| 		// Класс реализации определния столкновений | ||||
| 		private var _collider:EllipsoidCollider; | ||||
| 		// Флаг необходимости проверки столкновений | ||||
| 		private var _checkCollisions:Boolean; | ||||
| 		// Радиус сферы для определения столкновений | ||||
| 		private var _collisionRadius:Number = 0; | ||||
| 		// Набор исключаемых из проверки столкновений объектов | ||||
| 		private var _collisionIgnoreSet:Set = new Set(true); | ||||
| 		// Флаг движения | ||||
| 		private var _isMoving:Boolean; | ||||
| 		 | ||||
| 		private var _onStartMoving:Function; | ||||
| 		private var _onStopMoving:Function; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра контроллера. | ||||
| 		 *  | ||||
| 		 * @param eventsSourceObject объект, используемый для получения событий мыши и клавиатуры | ||||
| 		 */ | ||||
| 		public function CameraController(eventsSourceObject:DisplayObject) { | ||||
| 			if (eventsSourceObject == null) { | ||||
| 				throw new ArgumentError("CameraController: eventsSource is null"); | ||||
| 			} | ||||
| 			_eventsSource = eventsSourceObject; | ||||
| 			 | ||||
| 			actionBindings[ACTION_FORWARD] = forward; | ||||
| 			actionBindings[ACTION_BACK] = back; | ||||
| 			actionBindings[ACTION_LEFT] = left; | ||||
| 			actionBindings[ACTION_RIGHT] = right; | ||||
| 			actionBindings[ACTION_UP] = up; | ||||
| 			actionBindings[ACTION_DOWN] = down; | ||||
| 			actionBindings[ACTION_PITCH_UP] = pitchUp; | ||||
| 			actionBindings[ACTION_PITCH_DOWN] = pitchDown; | ||||
| 			actionBindings[ACTION_YAW_LEFT] = yawLeft; | ||||
| 			actionBindings[ACTION_YAW_RIGHT] = yawRight; | ||||
| 			actionBindings[ACTION_ACCELERATE] = accelerate; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Установка привязки клавиш по умолчанию. Данный метод очищает все существующие привязки клавиш и устанавливает следующие: | ||||
| 		 * <table border="1" style="border-collapse: collapse"> | ||||
| 		 * <tr><th>Клавиша</th><th>Действие</th></tr> | ||||
| 		 * <tr><td>W</td><td>ACTION_FORWARD</td></tr> | ||||
| 		 * <tr><td>S</td><td>ACTION_BACK</td></tr> | ||||
| 		 * <tr><td>A</td><td>ACTION_LEFT</td></tr> | ||||
| 		 * <tr><td>D</td><td>ACTION_RIGHT</td></tr> | ||||
| 		 * <tr><td>SPACE</td><td>ACTION_UP</td></tr> | ||||
| 		 * <tr><td>CONTROL</td><td>ACTION_DOWN</td></tr> | ||||
| 		 * <tr><td>SHIFT</td><td>ACTION_ACCELERATE</td></tr> | ||||
| 		 * <tr><td>UP</td><td>ACTION_PITCH_UP</td></tr> | ||||
| 		 * <tr><td>DOWN</td><td>ACTION_PITCH_DOWN</td></tr> | ||||
| 		 * <tr><td>LEFT</td><td>ACTION_YAW_LEFT</td></tr> | ||||
| 		 * <tr><td>RIGHT</td><td>ACTION_YAW_RIGHT</td></tr> | ||||
| 		 * </table> | ||||
| 		 */ | ||||
| 		public function setDefaultBindings():void { | ||||
| 			unbindAll(); | ||||
| 			bindKey(KeyboardUtils.W, ACTION_FORWARD); | ||||
| 			bindKey(KeyboardUtils.S, ACTION_BACK); | ||||
| 			bindKey(KeyboardUtils.A, ACTION_LEFT); | ||||
| 			bindKey(KeyboardUtils.D, ACTION_RIGHT); | ||||
| 			bindKey(KeyboardUtils.SPACE, ACTION_UP); | ||||
| 			bindKey(KeyboardUtils.CONTROL, ACTION_DOWN); | ||||
| 			bindKey(KeyboardUtils.UP, ACTION_PITCH_UP); | ||||
| 			bindKey(KeyboardUtils.DOWN, ACTION_PITCH_DOWN); | ||||
| 			bindKey(KeyboardUtils.LEFT, ACTION_YAW_LEFT); | ||||
| 			bindKey(KeyboardUtils.RIGHT, ACTION_YAW_RIGHT); | ||||
| 			bindKey(KeyboardUtils.SHIFT, ACTION_ACCELERATE); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Направление камеры на точку. | ||||
| 		 *  | ||||
| 		 * @param point координаты точки направления камеры | ||||
| 		 */ | ||||
| 		public function lookAt(point:Point3D):void { | ||||
| 			if (_camera == null) { | ||||
| 				return; | ||||
| 			} | ||||
| 			var dx:Number = point.x - _camera.x; | ||||
| 			var dy:Number = point.y - _camera.y; | ||||
| 			var dz:Number = point.z - _camera.z; | ||||
| 			_camera.rotationZ = -Math.atan2(dx, dy); | ||||
| 			_camera.rotationX = Math.atan2(dz, Math.sqrt(dx * dx + dy * dy)) - MathUtils.DEG90; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Callback-функция, вызываемая при начале движения камеры. | ||||
| 		 */ | ||||
| 		public function get onStartMoving():Function { | ||||
| 			return _onStartMoving; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set onStartMoving(value:Function):void { | ||||
| 			_onStartMoving = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Callback-функция, вызываемая при прекращении движения камеры. | ||||
| 		 */ | ||||
| 		public function get onStopMoving():Function { | ||||
| 			return _onStopMoving; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set onStopMoving(value:Function):void { | ||||
| 			_onStopMoving = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Набор объектов, исключаемых из проверки столкновений. | ||||
| 		 */		 | ||||
| 		public function get collisionIgnoreSet():Set { | ||||
| 			return _collisionIgnoreSet; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Источник событий клавиатуры и мыши. | ||||
| 		 */ | ||||
| 		public function get eventsSource():DisplayObject { | ||||
| 			return _eventsSource; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set eventsSource(value:DisplayObject):void { | ||||
| 			if (_eventsSource != value) { | ||||
| 				if (value == null) { | ||||
| 					throw new ArgumentError("CameraController: eventsSource is null"); | ||||
| 				} | ||||
| 				if (_controlsEnabled) { | ||||
| 					unregisterEventsListeners(); | ||||
| 				} | ||||
| 				_eventsSource = value; | ||||
| 				if (_controlsEnabled) { | ||||
| 					registerEventListeners(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Ассоциированная камера. | ||||
| 		 */ | ||||
| 		public function get camera():Camera3D { | ||||
| 			return _camera; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set camera(value:Camera3D):void { | ||||
| 			if (_camera != value) { | ||||
| 				_camera = value; | ||||
| 				if (value == null) { | ||||
| 					controlsEnabled = false; | ||||
| 				} else { | ||||
| 					createCollider(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Режим движения камеры. Если значение равно <code>true</code>, то перемещения камеры происходят относительно | ||||
| 		 * локальной системы координат, иначе относительно глобальной. | ||||
| 		 *  | ||||
| 		 * @default true  | ||||
| 		 */ | ||||
| 		public function get moveLocal():Boolean { | ||||
| 			return _moveLocal; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set moveLocal(value:Boolean):void { | ||||
| 			_moveLocal = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Включение режима проверки столкновений. | ||||
| 		 */ | ||||
| 		public function get checkCollisions():Boolean { | ||||
| 			return _checkCollisions; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set checkCollisions(value:Boolean):void { | ||||
| 			_checkCollisions = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Радиус сферы для определения столкновений. | ||||
| 		 *  | ||||
| 		 * @default 0 | ||||
| 		 */ | ||||
| 		public function get collisionRadius():Number { | ||||
| 			return _collisionRadius; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set collisionRadius(value:Number):void { | ||||
| 			_collisionRadius = value; | ||||
| 			if (_collider != null) { | ||||
| 				_collider.radiusX = _collisionRadius; | ||||
| 				_collider.radiusY = _collisionRadius; | ||||
| 				_collider.radiusZ = _collisionRadius; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Привязка клавиши к действию. | ||||
| 		 *  | ||||
| 		 * @param keyCode код клавиши | ||||
| 		 * @param action наименование действия | ||||
| 		 */ | ||||
| 		public function bindKey(keyCode:uint, action:String):void { | ||||
| 			var method:Function = actionBindings[action]; | ||||
| 			if (method != null) { | ||||
| 				keyBindings[keyCode] = method; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Очистка привязки клавиши. | ||||
| 		 *  | ||||
| 		 * @param keyCode код клавиши | ||||
| 		 */ | ||||
| 		public function unbindKey(keyCode:uint):void { | ||||
| 			keyBindings.remove(keyCode); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Очистка привязки всех клавиш. | ||||
| 		 */ | ||||
| 		public function unbindAll():void { | ||||
| 			keyBindings.clear(); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация движения камеры вперёд. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function forward(value:Boolean):void { | ||||
| 			_forward = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация движения камеры назад. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function back(value:Boolean):void { | ||||
| 			_back = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Активация движения камеры влево. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function left(value:Boolean):void { | ||||
| 			_left = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация движения камеры вправо. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function right(value:Boolean):void { | ||||
| 			_right = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация движения камеры вверх. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function up(value:Boolean):void { | ||||
| 			_up = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация движения камеры вниз. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function down(value:Boolean):void { | ||||
| 			_down = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация поворота камеры вверх. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function pitchUp(value:Boolean):void { | ||||
| 			_pitchUp = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация поворота камеры вниз. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function pitchDown(value:Boolean):void { | ||||
| 			_pitchDown = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация поворота камеры влево. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function yawLeft(value:Boolean):void { | ||||
| 			_yawLeft = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация поворота камеры вправо. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function yawRight(value:Boolean):void { | ||||
| 			_yawRight = value; | ||||
| 		} | ||||
| 		 | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Активация режима увеличенной скорости. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для включения ускорения, <code>false</code> для выключения | ||||
| 		 */ | ||||
| 		public function accelerate(value:Boolean):void { | ||||
| 			_accelerate = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		private function createCollider():void { | ||||
| 			_collider = new EllipsoidCollider(_camera.scene, _collisionRadius); | ||||
| 			_collider.offsetThreshold = 0.01; | ||||
| 			_collider.collisionSet = _collisionIgnoreSet;				 | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Чувствительность мыши — коэффициент умножения <code>mousePitch</code> и <code>mouseYaw</code>. | ||||
| 		 *  | ||||
| 		 * @default 1 | ||||
| 		 *  | ||||
| 		 * @see #mousePitch() | ||||
| 		 * @see #mouseYaw() | ||||
| 		 */		 | ||||
| 		public function get mouseSensitivity():Number { | ||||
| 			return _mouseSensitivity; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set mouseSensitivity(sensitivity:Number):void { | ||||
| 			_mouseSensitivity = sensitivity; | ||||
| 			_mousePitchCoeff = _mouseSensitivity * _mousePitch; | ||||
| 			_mouseYawCoeff = _mouseSensitivity * _mouseYaw; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Скорость изменения угла тангажа при управлении мышью (радианы на пиксель). | ||||
| 		 *  | ||||
| 		 * @default Math.PI / 360 | ||||
| 		 */		 | ||||
| 		public function get mousePitch():Number { | ||||
| 			return _mousePitch; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set mousePitch(pitch:Number):void { | ||||
| 			_mousePitch = pitch; | ||||
| 			_mousePitchCoeff = _mouseSensitivity * _mousePitch; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Скорость изменения угла рысканья при управлении мышью (радианы на пиксель). | ||||
| 		 *  | ||||
| 		 * @default Math.PI / 360 | ||||
| 		 */		 | ||||
| 		public function get mouseYaw():Number { | ||||
| 			return _mouseYaw; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set mouseYaw(yaw:Number):void { | ||||
| 			_mouseYaw = yaw; | ||||
| 			_mouseYawCoeff = _mouseSensitivity * _mouseYaw; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Угловая скорость по тангажу при управлении с клавиатуры (радианы в секунду). | ||||
| 		 *  | ||||
| 		 * @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; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Скорость поступательного движения (единицы в секунду). | ||||
| 		 */		 | ||||
| 		public function get speed():Number { | ||||
| 			return _speed; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set speed(spd:Number):void { | ||||
| 			_speed = spd; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Коэффициент увеличения скорости при активном действии <code>ACTION_ACCELERATE</code>. | ||||
| 		 *  | ||||
| 		 * @default 2 | ||||
| 		 */ | ||||
| 		public function get speedMultiplier():Number { | ||||
| 			return _speedMultiplier; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set speedMultiplier(value:Number):void { | ||||
| 			_speedMultiplier = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активность управления камеры. | ||||
| 		 *  | ||||
| 		 * @default false | ||||
| 		 */ | ||||
| 		public function get controlsEnabled():Boolean { | ||||
| 			return _controlsEnabled; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set controlsEnabled(value:Boolean):void { | ||||
| 			if (_camera == null || _controlsEnabled == value) return; | ||||
| 			if (value) { | ||||
| 				lastFrameTime = getTimer(); | ||||
| 				registerEventListeners(); | ||||
| 			} | ||||
| 			else { | ||||
| 				unregisterEventsListeners(); | ||||
| 			} | ||||
| 			_controlsEnabled = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Базовый шаг изменения угла зрения в радианах. Реальный шаг получаеся умножением этого значения на величину | ||||
| 		 * <code>MouseEvent.delta</code>. | ||||
| 		 *  | ||||
| 		 * @default Math.PI / 180  | ||||
| 		 */ | ||||
| 		public function get fovStep():Number { | ||||
| 			return _fovStep; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set fovStep(value:Number):void { | ||||
| 			_fovStep = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Множитель при изменении коэффициента увеличения. Закон изменения коэффициента увеличения описывается формулой:<br> | ||||
| 		 * <code>zoom (1 + MouseEvent.delta zoomMultiplier)</code>. | ||||
| 		 *  | ||||
| 		 * @default 0.1  | ||||
| 		 */ | ||||
| 		public function get zoomMultiplier():Number { | ||||
| 			return _zoomMultiplier; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set zoomMultiplier(value:Number):void { | ||||
| 			_zoomMultiplier = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		private function registerEventListeners():void { | ||||
| 			_eventsSource.addEventListener(KeyboardEvent.KEY_DOWN, onKey); | ||||
| 			_eventsSource.addEventListener(KeyboardEvent.KEY_UP, onKey); | ||||
| 			_eventsSource.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); | ||||
| 			_eventsSource.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		private function unregisterEventsListeners():void { | ||||
| 			_eventsSource.removeEventListener(KeyboardEvent.KEY_DOWN, onKey); | ||||
| 			_eventsSource.removeEventListener(KeyboardEvent.KEY_UP, onKey); | ||||
| 			_eventsSource.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); | ||||
| 			_eventsSource.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp); | ||||
| 			_eventsSource.removeEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @param e | ||||
| 		 */ | ||||
| 		private function onKey(e:KeyboardEvent):void { | ||||
| 			var method:Function = keyBindings[e.keyCode]; | ||||
| 			if (method != null) { | ||||
| 				method.call(this, e.type == KeyboardEvent.KEY_DOWN); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @param e | ||||
| 		 */		 | ||||
| 		private function onMouseDown(e:MouseEvent):void { | ||||
| 			mouseLookActive = true;  | ||||
| 			currentDragCoords.x = startDragCoords.x = _eventsSource.stage.mouseX; | ||||
| 			currentDragCoords.y = startDragCoords.y = _eventsSource.stage.mouseY; | ||||
| 			startRotX = _camera.rotationX; | ||||
| 			startRotZ = _camera.rotationZ; | ||||
| 			_eventsSource.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @param e | ||||
| 		 */		 | ||||
| 		private function onMouseUp(e:MouseEvent):void { | ||||
| 			mouseLookActive = false; | ||||
| 			_eventsSource.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @param e | ||||
| 		 */		 | ||||
| 		private function onMouseWheel(e:MouseEvent):void { | ||||
| 			if (_camera.orthographic) { | ||||
| 				_camera.zoom = _camera.zoom * (1 + e.delta * _zoomMultiplier); | ||||
| 			} else { | ||||
| 				_camera.fov -= _fovStep * e.delta; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Обработка управляющих воздействий. | ||||
| 		 * Метод должен вызываться каждый кадр перед вызовом <code>Scene3D.calculate()</code>. | ||||
| 		 *  | ||||
| 		 * @see alternativa.engine3d.core.Scene3D#calculate() | ||||
| 		 */		 | ||||
| 		public function processInput(): void { | ||||
| 			if (!_controlsEnabled || _camera == null) return; | ||||
| 			 | ||||
| 			// Время в секундах от начала предыдущего кадра | ||||
| 			var frameTime:Number = getTimer() - lastFrameTime; | ||||
| 			lastFrameTime += frameTime; | ||||
| 			frameTime /= 1000; | ||||
| 			 | ||||
| 			// Обработка mouselook | ||||
| 			if (mouseLookActive) { | ||||
| 				prevDragCoords.x = currentDragCoords.x; | ||||
| 				prevDragCoords.y = currentDragCoords.y; | ||||
| 				currentDragCoords.x = _eventsSource.stage.mouseX; | ||||
| 				currentDragCoords.y = _eventsSource.stage.mouseY; | ||||
| 				if (!prevDragCoords.equals(currentDragCoords)) { | ||||
| 					_camera.rotationZ = startRotZ + (startDragCoords.x - currentDragCoords.x) * _mouseYawCoeff; | ||||
| 					var rotX:Number = startRotX + (startDragCoords.y - currentDragCoords.y) * _mousePitchCoeff; | ||||
| 					_camera.rotationX = (rotX > 0) ? 0 : (rotX < -MathUtils.DEG180) ? -MathUtils.DEG180 : rotX; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Поворот относительно вертикальной оси (рысканье, yaw) | ||||
| 			if (_yawLeft) { | ||||
| 				_camera.rotationZ += _yawSpeed * frameTime; | ||||
| 			} else if (_yawRight) { | ||||
| 				_camera.rotationZ -= _yawSpeed * frameTime; | ||||
| 			} | ||||
| 			 | ||||
| 			// Поворот относительно поперечной оси (тангаж, pitch) | ||||
| 			if (_pitchUp) { | ||||
| 				rotX = _camera.rotationX + _pitchSpeed * frameTime; | ||||
| 				_camera.rotationX = (rotX > 0) ? 0 : (rotX < -MathUtils.DEG180) ? -MathUtils.DEG180 : rotX; | ||||
| 			} else if (_pitchDown) { | ||||
| 				rotX = _camera.rotationX - _pitchSpeed * frameTime; | ||||
| 				_camera.rotationX = (rotX > 0) ? 0 : (rotX < -MathUtils.DEG180) ? -MathUtils.DEG180 : rotX; | ||||
| 			} | ||||
|  | ||||
| 			// TODO: Поворот относительно продольной оси (крен, roll) | ||||
| 			 | ||||
| 			var frameDistance:Number = _speed * frameTime; | ||||
| 			if (_accelerate) { | ||||
| 				frameDistance *= _speedMultiplier; | ||||
| 			} | ||||
| 			velocity.x = 0; | ||||
| 			velocity.y = 0; | ||||
| 			velocity.z = 0; | ||||
| 			var transformation:Matrix3D = _camera.transformation; | ||||
| 			 | ||||
| 			if (_moveLocal) { | ||||
| 				// Режим относительных пермещений | ||||
| 				// Движение вперед-назад | ||||
| 				if (_forward) { | ||||
| 					velocity.x += frameDistance * transformation.c; | ||||
| 					velocity.y += frameDistance * transformation.g; | ||||
| 					velocity.z += frameDistance * transformation.k; | ||||
| 				} else if (_back) { | ||||
| 					velocity.x -= frameDistance * transformation.c; | ||||
| 					velocity.y -= frameDistance * transformation.g; | ||||
| 					velocity.z -= frameDistance * transformation.k; | ||||
| 				} | ||||
| 				// Движение влево-вправо | ||||
| 				if (_left) { | ||||
| 					velocity.x -= frameDistance * transformation.a; | ||||
| 					velocity.y -= frameDistance * transformation.e; | ||||
| 					velocity.z -= frameDistance * transformation.i; | ||||
| 				} else if (_right) { | ||||
| 					velocity.x += frameDistance * transformation.a; | ||||
| 					velocity.y += frameDistance * transformation.e; | ||||
| 					velocity.z += frameDistance * transformation.i; | ||||
| 				} | ||||
| 				// Движение вверх-вниз | ||||
| 				if (_up) { | ||||
| 					velocity.x -= frameDistance * transformation.b; | ||||
| 					velocity.y -= frameDistance * transformation.f; | ||||
| 					velocity.z -= frameDistance * transformation.j; | ||||
| 				} else if (_down) { | ||||
| 					velocity.x += frameDistance * transformation.b; | ||||
| 					velocity.y += frameDistance * transformation.f; | ||||
| 					velocity.z += frameDistance * transformation.j; | ||||
| 				} | ||||
| 			} | ||||
| 			else { | ||||
| 				// Режим глобальных перемещений | ||||
| 				var cosZ:Number = Math.cos(_camera.rotationZ); | ||||
| 				var sinZ:Number = Math.sin(_camera.rotationZ); | ||||
| 				// Движение вперед-назад | ||||
| 				if (_forward) { | ||||
| 					velocity.x -= frameDistance * sinZ; | ||||
| 					velocity.y += frameDistance * cosZ; | ||||
| 				} else if (_back) { | ||||
| 					velocity.x += frameDistance * sinZ; | ||||
| 					velocity.y -= frameDistance * cosZ; | ||||
| 				} | ||||
| 				// Движение влево-вправо | ||||
| 				if (_left) { | ||||
| 					velocity.x -= frameDistance * cosZ; | ||||
| 					velocity.y -= frameDistance * sinZ; | ||||
| 				} else if (_right) { | ||||
| 					velocity.x += frameDistance * cosZ; | ||||
| 					velocity.y += frameDistance * sinZ; | ||||
| 				} | ||||
| 				// Движение вверх-вниз | ||||
| 				if (_up) { | ||||
| 					velocity.z += frameDistance; | ||||
| 				} else if (_down) { | ||||
| 					velocity.z -= frameDistance; | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			// Коррекция модуля вектора скорости | ||||
| 			if (velocity.x != 0 || velocity.y != 0 || velocity.z != 0) { | ||||
| 				velocity.length = frameDistance; | ||||
| 			} | ||||
| 			 | ||||
| 			// Проверка столкновений | ||||
| 			if (_checkCollisions) { | ||||
| 				_collider.calculateDestination(_camera.coords, velocity, destination); | ||||
| 				_camera.x = destination.x; | ||||
| 				_camera.y = destination.y; | ||||
| 				_camera.z = destination.z; | ||||
| 			} else { | ||||
| 				_camera.x += velocity.x; | ||||
| 				_camera.y += velocity.y; | ||||
| 				_camera.z += velocity.z; | ||||
| 			} | ||||
|  | ||||
| 			// Обработка начала/окончания движения | ||||
| 			if (_camera.changeRotationOrScaleOperation.queued || _camera.changeCoordsOperation.queued) { | ||||
| 				if (!_isMoving) { | ||||
| 					_isMoving = true; | ||||
| 					if (_onStartMoving != null) { | ||||
| 						_onStartMoving.call(this); | ||||
| 					}  | ||||
| 				} | ||||
| 			} else { | ||||
| 				if (_isMoving) { | ||||
| 					_isMoving = false; | ||||
| 					if (_onStopMoving != null) { | ||||
| 						_onStopMoving.call(this); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,450 @@ | ||||
| package alternativa.engine3d.controllers { | ||||
| 	 | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Camera3D; | ||||
| 	import alternativa.types.Matrix3D; | ||||
| 	import alternativa.types.Point3D; | ||||
| 	import alternativa.utils.KeyboardUtils; | ||||
| 	import alternativa.utils.MathUtils; | ||||
| 	 | ||||
| 	import flash.display.DisplayObject; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
|  | ||||
| 	/** | ||||
| 	 * Контроллер, реализующий управление, подобное управлению летательным аппаратом для объекта, находящегося в системе | ||||
| 	 * координат корневого объекта сцены. Повороты выполняются вокруг локальных осей объекта, собственные ускорения | ||||
| 	 * действуют вдоль локальных осей. | ||||
| 	 *  | ||||
| 	 * <p>Соответствия локальных осей для объектов, не являющихся камерой: | ||||
| 	 * <table border="1" style="border-collapse: collapse"> | ||||
| 	 * <tr> | ||||
| 	 * <th>Ось</th><th>Направление</th><th>Поворот</th> | ||||
| 	 * </tr> | ||||
| 	 * <tr> | ||||
| 	 * <td>X</td><td>Вправо</td><td>Тангаж</td> | ||||
| 	 * </tr> | ||||
| 	 * <tr> | ||||
| 	 * <td>Y</td><td>Вперёд</td><td>Крен</td> | ||||
| 	 * </tr> | ||||
| 	 * <tr> | ||||
| 	 * <td>Z</td><td>Вверх</td><td>Рысканье</td> | ||||
| 	 * </tr> | ||||
| 	 * </table> | ||||
| 	 *   | ||||
| 	 * <p>Соответствия локальных осей для объектов, являющихся камерой: | ||||
| 	 * <table border="1" style="border-collapse: collapse"> | ||||
| 	 * <tr> | ||||
| 	 * <th>Ось</th><th>Направление</th><th>Поворот</th> | ||||
| 	 * </tr> | ||||
| 	 * <tr> | ||||
| 	 * <td>X</td><td>Вправо</td><td>Тангаж</td> | ||||
| 	 * </tr> | ||||
| 	 * <tr> | ||||
| 	 * <td>Y</td><td>Вниз</td><td>Рысканье</td> | ||||
| 	 * </tr> | ||||
| 	 * <tr> | ||||
| 	 * <td>Z</td><td>Вперёд</td><td>Крен</td> | ||||
| 	 * </tr> | ||||
| 	 * </table> | ||||
| 	 *  | ||||
| 	 * Поворот мышью реализован следующим образом: в момент активации режима поворота (нажата левая кнопка мыши или | ||||
| 	 * соответствующая кнопка на клавиатуре) текущее положение курсора становится точкой, относительно которой определяются | ||||
| 	 * дальнейшие отклонения. Отклонение курсора по вертикали в пикселях, умноженное на коэффициент чувствительности мыши | ||||
| 	 * по вертикали даёт угловую скорость по тангажу. Отклонение курсора по горизонтали в пикселях, умноженное на коэффициент | ||||
| 	 * чувствительности мыши по горизонтали даёт угловую скорость по крену. | ||||
| 	 */ | ||||
| 	public class FlyController extends ObjectController { | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш поворота по крену влево. | ||||
| 		 */ | ||||
| 		public static const ACTION_ROLL_LEFT:String = "ACTION_ROLL_LEFT"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш поворота по крену вправо. | ||||
| 		 */ | ||||
| 		public static const ACTION_ROLL_RIGHT:String = "ACTION_ROLL_RIGHT"; | ||||
| 		 | ||||
| 		private var _rollLeft:Boolean; | ||||
| 		private var _rollRight:Boolean; | ||||
| 		 | ||||
| 		private var _rollSpeed:Number = 1; | ||||
| 		 | ||||
| 		private var rotations:Point3D; | ||||
| 		private var rollMatrix:Matrix3D = new Matrix3D(); | ||||
| 		private var transformation:Matrix3D = new Matrix3D(); | ||||
| 		private var axis:Point3D = new Point3D(); | ||||
| 		 | ||||
| 		private var velocity:Point3D = new Point3D(); | ||||
| 		private var displacement:Point3D = new Point3D(); | ||||
| 		private var destination:Point3D = new Point3D(); | ||||
| 		private var deltaVelocity:Point3D = new Point3D(); | ||||
| 		private var accelerationVector:Point3D = new Point3D(); | ||||
| 		private var currentTransform:Matrix3D = new Matrix3D(); | ||||
| 		 | ||||
| 		private var _currentSpeed:Number = 1; | ||||
| 		/** | ||||
| 		 * Текущие координаты мышиного курсора в режиме mouse look. | ||||
| 		 */ | ||||
| 		private var currentMouseCoords:Point3D = new Point3D(); | ||||
|  | ||||
| 		/** | ||||
| 		 * Модуль вектора ускорния, получаемого от команд движения.  | ||||
| 		 */ | ||||
| 		public var acceleration:Number = 1000; | ||||
| 		/** | ||||
| 		 * Модуль вектора замедляющего ускорения. | ||||
| 		 */ | ||||
| 		public var deceleration:Number = 50; | ||||
| 		/** | ||||
| 		 * Погрешность определения скорости. Скорость приравнивается к нулю, если её модуль не превышает заданного значения. | ||||
| 		 */ | ||||
| 		public var speedThreshold:Number = 1; | ||||
| 		/** | ||||
| 		 * Переключение инерционного режима. В инерционном режиме отсутствует замедляющее ускорение, в результате чего вектор | ||||
| 		 * скорости объекта остаётся постоянным, если нет управляющих воздействий. При выключенном инерционном режиме к объекту | ||||
| 		 * прикладывается замедляющее ускорение.  | ||||
| 		 */ | ||||
| 		public var inertialMode:Boolean; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */ | ||||
| 		public function FlyController(eventsSourceObject:DisplayObject) { | ||||
| 			super(eventsSourceObject); | ||||
|  | ||||
| 			actionBindings[ACTION_ROLL_LEFT] = rollLeft; | ||||
| 			actionBindings[ACTION_ROLL_RIGHT] = rollRight; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Текущая скорость движения. | ||||
| 		 */ | ||||
| 		public function get currentSpeed():Number { | ||||
| 			return _currentSpeed; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Активация вращения по крену влево. | ||||
| 		 */ | ||||
| 		public function rollLeft(value:Boolean):void { | ||||
| 			_rollLeft = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация вращения по крену вправо. | ||||
| 		 */ | ||||
| 		public function rollRight(value:Boolean):void { | ||||
| 			_rollRight = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Установка привязки клавиш по умолчанию. Данный метод очищает все существующие привязки клавиш и устанавливает следующие: | ||||
| 		 * <table border="1" style="border-collapse: collapse"> | ||||
| 		 * <tr><th>Клавиша</th><th>Действие</th></tr> | ||||
| 		 * <tr><td>W</td><td>ACTION_FORWARD</td></tr> | ||||
| 		 * <tr><td>S</td><td>ACTION_BACK</td></tr> | ||||
| 		 * <tr><td>A</td><td>ACTION_LEFT</td></tr> | ||||
| 		 * <tr><td>D</td><td>ACTION_RIGHT</td></tr> | ||||
| 		 * <tr><td>SPACE</td><td>ACTION_UP</td></tr> | ||||
| 		 * <tr><td>CONTROL</td><td>ACTION_DOWN</td></tr> | ||||
| 		 * <tr><td>UP</td><td>ACTION_PITCH_UP</td></tr> | ||||
| 		 * <tr><td>DOWN</td><td>ACTION_PITCH_DOWN</td></tr> | ||||
| 		 * <tr><td>LEFT</td><td>ACTION_ROLL_LEFT</td></tr> | ||||
| 		 * <tr><td>RIGHT</td><td>ACTION_ROLL_RIGHT</td></tr> | ||||
| 		 * <tr><td>Q</td><td>ACTION_YAW_LEFT</td></tr> | ||||
| 		 * <tr><td>E</td><td>ACTION_YAW_RIGHT</td></tr> | ||||
| 		 * <tr><td>M</td><td>ACTION_MOUSE_LOOK</td></tr> | ||||
| 		 * </table> | ||||
| 		 */		 | ||||
| 		override public function setDefaultBindings():void { | ||||
| 			unbindAll(); | ||||
| 			bindKey(KeyboardUtils.W, ACTION_FORWARD); | ||||
| 			bindKey(KeyboardUtils.S, ACTION_BACK); | ||||
| 			bindKey(KeyboardUtils.A, ACTION_LEFT); | ||||
| 			bindKey(KeyboardUtils.D, ACTION_RIGHT); | ||||
| 			bindKey(KeyboardUtils.SPACE, ACTION_UP); | ||||
| 			bindKey(KeyboardUtils.CONTROL, ACTION_DOWN); | ||||
| 			bindKey(KeyboardUtils.UP, ACTION_PITCH_UP); | ||||
| 			bindKey(KeyboardUtils.DOWN, ACTION_PITCH_DOWN); | ||||
| 			bindKey(KeyboardUtils.LEFT, ACTION_ROLL_LEFT); | ||||
| 			bindKey(KeyboardUtils.RIGHT, ACTION_ROLL_RIGHT); | ||||
| 			bindKey(KeyboardUtils.Q, ACTION_YAW_LEFT); | ||||
| 			bindKey(KeyboardUtils.E, ACTION_YAW_RIGHT); | ||||
| 			bindKey(KeyboardUtils.M, ACTION_MOUSE_LOOK); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод выполняет поворот объекта относительно локальных осей в соответствии с имеющимися воздействиями. | ||||
| 		 *  | ||||
| 		 * @param frameTime длительность текущего кадра в секундах  | ||||
| 		 */ | ||||
| 		override protected function rotateObject(frameTime:Number):void { | ||||
| 			var transformation:Matrix3D = _object.transformation; | ||||
| 			if (_mouseLookActive) { | ||||
| 				currentMouseCoords.x = _eventsSource.stage.mouseX; | ||||
| 				currentMouseCoords.y = _eventsSource.stage.mouseY; | ||||
| 				if (!currentMouseCoords.equals(startMouseCoords)) { | ||||
| 					var deltaYaw:Number = (currentMouseCoords.x - startMouseCoords.x) * _mouseCoefficientX; | ||||
| 					if (_object is Camera3D) { | ||||
| 						axis.x = transformation.c; | ||||
| 						axis.y = transformation.g; | ||||
| 						axis.z = transformation.k; | ||||
| 					} else { | ||||
| 						axis.x = transformation.b; | ||||
| 						axis.y = transformation.f; | ||||
| 						axis.z = transformation.j; | ||||
| 					} | ||||
| 					 | ||||
| 					rotateObjectAroundAxis(axis, deltaYaw * frameTime); | ||||
|  | ||||
| 					currentTransform.toTransform(0, 0, 0, _object.rotationX, _object.rotationY, _object.rotationZ, 1, 1, 1); | ||||
| 					var deltaPitch:Number = (startMouseCoords.y - currentMouseCoords.y) * _mouseCoefficientY; | ||||
| 					axis.x = currentTransform.a; | ||||
| 					axis.y = currentTransform.e; | ||||
| 					axis.z = currentTransform.i; | ||||
|  | ||||
| 					rotateObjectAroundAxis(axis, deltaPitch * frameTime); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Поворот относительно продольной оси (крен, roll) | ||||
| 			if (_rollLeft) { | ||||
| 				if (_object is Camera3D) { | ||||
| 					axis.x = transformation.c; | ||||
| 					axis.y = transformation.g; | ||||
| 					axis.z = transformation.k; | ||||
| 				} else { | ||||
| 					axis.x = transformation.b; | ||||
| 					axis.y = transformation.f; | ||||
| 					axis.z = transformation.j; | ||||
| 				} | ||||
| 				rotateObjectAroundAxis(axis, -_rollSpeed * frameTime); | ||||
| 			} else if (_rollRight) { | ||||
| 				if (_object is Camera3D) { | ||||
| 					axis.x = transformation.c; | ||||
| 					axis.y = transformation.g; | ||||
| 					axis.z = transformation.k; | ||||
| 				} else { | ||||
| 					axis.x = transformation.b; | ||||
| 					axis.y = transformation.f; | ||||
| 					axis.z = transformation.j; | ||||
| 				} | ||||
| 				rotateObjectAroundAxis(axis, _rollSpeed * frameTime); | ||||
| 			} | ||||
| 			 | ||||
| 			// Поворот относительно поперечной оси (тангаж, pitch) | ||||
| 			if (_pitchUp) { | ||||
| 				axis.x = transformation.a; | ||||
| 				axis.y = transformation.e; | ||||
| 				axis.z = transformation.i; | ||||
| 				rotateObjectAroundAxis(axis, _pitchSpeed * frameTime); | ||||
| 			} else if (_pitchDown) { | ||||
| 				axis.x = transformation.a; | ||||
| 				axis.y = transformation.e; | ||||
| 				axis.z = transformation.i; | ||||
| 				rotateObjectAroundAxis(axis, -_pitchSpeed * frameTime); | ||||
| 			} | ||||
|  | ||||
| 			// Поворот относительно вертикальной оси (рысканье, yaw) | ||||
| 			if (_yawRight) { | ||||
| 				if (_object is Camera3D) { | ||||
| 					axis.x = transformation.b; | ||||
| 					axis.y = transformation.f; | ||||
| 					axis.z = transformation.j; | ||||
| 					rotateObjectAroundAxis(axis, _yawSpeed * frameTime); | ||||
| 				} else { | ||||
| 					axis.x = transformation.c; | ||||
| 					axis.y = transformation.g; | ||||
| 					axis.z = transformation.k; | ||||
| 					rotateObjectAroundAxis(axis, -_yawSpeed * frameTime); | ||||
| 				} | ||||
| 			} else if (_yawLeft) { | ||||
| 				if (_object is Camera3D) { | ||||
| 					axis.x = transformation.b; | ||||
| 					axis.y = transformation.f; | ||||
| 					axis.z = transformation.j; | ||||
| 					rotateObjectAroundAxis(axis, -_yawSpeed * frameTime); | ||||
| 				} else { | ||||
| 					axis.x = transformation.c; | ||||
| 					axis.y = transformation.g; | ||||
| 					axis.z = transformation.k; | ||||
| 					rotateObjectAroundAxis(axis, _yawSpeed * frameTime); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод вычисляет вектор потенциального смещения эллипсоида. | ||||
| 		 *  | ||||
| 		 * @param frameTime длительность текущего кадра в секундах  | ||||
| 		 * @param displacement в эту переменную записывается вычисленное потенциальное смещение объекта | ||||
| 		 */ | ||||
| 		override protected function getDisplacement(frameTime:Number, displacement:Point3D):void { | ||||
| 			// Движение вперед-назад | ||||
| 			accelerationVector.x = 0; | ||||
| 			accelerationVector.y = 0; | ||||
| 			accelerationVector.z = 0; | ||||
| 			if (_forward) { | ||||
| 				accelerationVector.y = 1; | ||||
| 			} else if (_back) { | ||||
| 				accelerationVector.y = -1; | ||||
| 			} | ||||
| 			// Движение влево-вправо | ||||
| 			if (_right) { | ||||
| 				accelerationVector.x = 1; | ||||
| 			} else if (_left) { | ||||
| 				accelerationVector.x = -1; | ||||
| 			} | ||||
| 			// Движение ввверх-вниз | ||||
| 			if (_up) { | ||||
| 				accelerationVector.z = 1; | ||||
| 			} else if (_down) { | ||||
| 				accelerationVector.z = -1; | ||||
| 			} | ||||
| 			 | ||||
| 			var speedLoss:Number; | ||||
| 			var len:Number; | ||||
| 			 | ||||
| 			if (accelerationVector.x != 0 || accelerationVector.y != 0 || accelerationVector.z != 0) { | ||||
| 				// Управление активно | ||||
| 				if (_object is Camera3D) { | ||||
| 					var tmp:Number = accelerationVector.z; | ||||
| 					accelerationVector.z = accelerationVector.y; | ||||
| 					accelerationVector.y = -tmp; | ||||
| 				} | ||||
| 				accelerationVector.normalize(); | ||||
| 				accelerationVector.x *= acceleration; | ||||
| 				accelerationVector.y *= acceleration; | ||||
| 				accelerationVector.z *= acceleration; | ||||
| 				currentTransform.toTransform(0, 0, 0, _object.rotationX, _object.rotationY, _object.rotationZ, 1, 1, 1); | ||||
| 				accelerationVector.transform(currentTransform); | ||||
| 				deltaVelocity.x = accelerationVector.x; | ||||
| 				deltaVelocity.y = accelerationVector.y; | ||||
| 				deltaVelocity.z = accelerationVector.z; | ||||
| 				deltaVelocity.x *= frameTime; | ||||
| 				deltaVelocity.y *= frameTime; | ||||
| 				deltaVelocity.z *= frameTime; | ||||
|  | ||||
| 				if (!inertialMode) { | ||||
| 					speedLoss = deceleration * frameTime; | ||||
| 					var dot:Number = Point3D.dot(velocity, accelerationVector); | ||||
| 					if (dot > 0) { | ||||
| 						len = accelerationVector.length; | ||||
| 						var x:Number = accelerationVector.x / len; | ||||
| 						var y:Number = accelerationVector.y / len; | ||||
| 						var z:Number = accelerationVector.z / len; | ||||
| 						len = dot / len; | ||||
| 						x = velocity.x - len * x; | ||||
| 						y = velocity.y - len * y; | ||||
| 						z = velocity.z - len * z; | ||||
| 						len = Math.sqrt(x*x + y*y + z*z); | ||||
| 						if (len > speedLoss) { | ||||
| 							x *= speedLoss / len; | ||||
| 							y *= speedLoss / len; | ||||
| 							z *= speedLoss / len; | ||||
| 						} | ||||
| 						velocity.x -= x; | ||||
| 						velocity.y -= y; | ||||
| 						velocity.z -= z; | ||||
| 					} else { | ||||
| 						len = velocity.length; | ||||
| 						velocity.length = (len > speedLoss) ? (len - speedLoss) : 0; | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				velocity.x += deltaVelocity.x; | ||||
| 				velocity.y += deltaVelocity.y; | ||||
| 				velocity.z += deltaVelocity.z; | ||||
|  | ||||
| 				if (velocity.length > _speed) { | ||||
| 					velocity.length = _speed; | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Управление неактивно | ||||
| 				if (!inertialMode) { | ||||
| 					speedLoss = deceleration * frameTime; | ||||
| 					len = velocity.length; | ||||
| 					velocity.length = (len > speedLoss) ? (len - speedLoss) : 0; | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			displacement.x = velocity.x * frameTime; | ||||
| 			displacement.y = velocity.y * frameTime; | ||||
| 			displacement.z = velocity.z * frameTime; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод применяет потенциальный вектор смещения к эллипсоиду с учётом столкновений с геометрией сцены, если включён | ||||
| 		 * соотвествующий режим. | ||||
| 		 *    | ||||
| 		 * @param frameTime время кадра в секундах | ||||
| 		 * @param displacement векотр потенциального смещения эллипсоида | ||||
| 		 */ | ||||
| 		override protected function applyDisplacement(frameTime:Number, displacement:Point3D):void { | ||||
| 			if (checkCollisions) { | ||||
| 				_collider.calculateDestination(_coords, displacement, destination); | ||||
|  | ||||
| 				displacement.x = destination.x - _coords.x; | ||||
| 				displacement.y = destination.y - _coords.y; | ||||
| 				displacement.z = destination.z - _coords.z; | ||||
| 			} else { | ||||
| 				destination.x = _coords.x + displacement.x; | ||||
| 				destination.y = _coords.y + displacement.y; | ||||
| 				destination.z = _coords.z + displacement.z; | ||||
| 			} | ||||
|  | ||||
| 			velocity.x = displacement.x / frameTime; | ||||
| 			velocity.y = displacement.y / frameTime; | ||||
| 			velocity.z = displacement.z / frameTime; | ||||
|  | ||||
| 			_coords.x = destination.x; | ||||
| 			_coords.y = destination.y; | ||||
| 			_coords.z = destination.z; | ||||
| 			setObjectCoords(); | ||||
| 			 | ||||
| 			var len:Number = Math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z); | ||||
| 			if (len < speedThreshold) { | ||||
| 				velocity.x = 0; | ||||
| 				velocity.y = 0; | ||||
| 				velocity.z = 0; | ||||
| 				_currentSpeed = 0; | ||||
| 			} else { | ||||
| 				_currentSpeed = len; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Поворот объекта вокруг заданной оси. | ||||
| 		 *  | ||||
| 		 * @param axis | ||||
| 		 * @param angle | ||||
| 		 */		 | ||||
| 		private function rotateObjectAroundAxis(axis:Point3D, angle:Number):void { | ||||
| 			transformation.toTransform(0, 0, 0, _object.rotationX, _object.rotationY, _object.rotationZ, 1, 1, 1); | ||||
| 			rollMatrix.fromAxisAngle(axis, angle); | ||||
| 			rollMatrix.inverseCombine(transformation); | ||||
| 			rotations = rollMatrix.getRotations(rotations); | ||||
| 			_object.rotationX = rotations.x; | ||||
| 			_object.rotationY = rotations.y; | ||||
| 			_object.rotationZ = rotations.z; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Направление объекта на точку. В результате работы метода локальная ось объекта, соответствующая направлению "вперёд" | ||||
| 		 * будет направлена на указанную точку, а угол поворота вокруг этой оси будет равен нулю. | ||||
| 		 *  | ||||
| 		 * @param point координаты точки, на которую должен быть направлен объект | ||||
| 		 */ | ||||
| 		public function lookAt(point:Point3D):void { | ||||
| 			if (_object == null) { | ||||
| 				return; | ||||
| 			} | ||||
| 			var dx:Number = point.x - _object.x; | ||||
| 			var dy:Number = point.y - _object.y; | ||||
| 			var dz:Number = point.z - _object.z; | ||||
| 			_object.rotationX = Math.atan2(dz, Math.sqrt(dx * dx + dy * dy)) - (_object is Camera3D ? MathUtils.DEG90 : 0); | ||||
| 			_object.rotationY = 0; | ||||
| 			_object.rotationZ = -Math.atan2(dx, dy); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,839 @@ | ||||
| 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. Значение параметра <code>value</code> указывает, нажата или отпущена соответсвующая команде | ||||
| 		 * клавиша. | ||||
| 		 */ | ||||
| 		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; | ||||
| 		/** | ||||
| 		 * Функция вида <code>function():void</code>, вызываемая при начале движения объекта. Под движением | ||||
| 		 * понимается изменение координат или ориентации. | ||||
| 		 */ | ||||
| 		public var onStartMoving:Function; | ||||
| 		/** | ||||
| 		 * Функция вида <code>function():void</code>, вызываемая при прекращении движения объекта. Под движением | ||||
| 		 * понимается изменение координат или ориентации. | ||||
| 		 */ | ||||
| 		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; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Включение и выключение контроллера. Выключенный контроллер пропускает выполнение метода <code>processInput()</code>. | ||||
| 		 *  | ||||
| 		 * @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 <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function moveForward(value:Boolean):void { | ||||
| 			_forward = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация движения назад. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function moveBack(value:Boolean):void { | ||||
| 			_back = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Активация движения влево. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function moveLeft(value:Boolean):void { | ||||
| 			_left = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация движения вправо. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function moveRight(value:Boolean):void { | ||||
| 			_right = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация движения вверх. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function moveUp(value:Boolean):void { | ||||
| 			_up = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация движения вниз. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function moveDown(value:Boolean):void { | ||||
| 			_down = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация поворота вверх. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function pitchUp(value:Boolean):void { | ||||
| 			_pitchUp = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация поворота вниз. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function pitchDown(value:Boolean):void { | ||||
| 			_pitchDown = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация поворота влево. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function yawLeft(value:Boolean):void { | ||||
| 			_yawLeft = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация поворота вправо. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function yawRight(value:Boolean):void { | ||||
| 			_yawRight = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Активация режима увеличенной скорости. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для включения ускорения, <code>false</code> для выключения | ||||
| 		 */ | ||||
| 		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; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Коэффициент увеличения скорости при активном действии <code>ACTION_ACCELERATE</code>. | ||||
| 		 *  | ||||
| 		 * @default 2 | ||||
| 		 */ | ||||
| 		public function get speedMultiplier():Number { | ||||
| 			return _speedMultiplier; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set speedMultiplier(value:Number):void { | ||||
| 			_speedMultiplier = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Чувствительность мыши — коэффициент умножения <code>mouseSensitivityX</code> и <code>mouseSensitivityY</code>. | ||||
| 		 *  | ||||
| 		 * @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; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Включение/выключение режима вращения объекта мышью. При включении режима вполняется метод <code>startMouseLook()</code>, | ||||
| 		 * при выключении — <code>stoptMouseLook()</code>. | ||||
| 		 *  | ||||
| 		 * @see #startMouseLook() | ||||
| 		 * @see #stopMouseLook() | ||||
| 		 */ | ||||
| 		public function setMouseLook(value:Boolean):void { | ||||
| 			if (_mouseLookActive != value) { | ||||
| 				_mouseLookActive = value; | ||||
| 				if (_mouseLookActive) { | ||||
| 					startMouseLook(); | ||||
| 				} else { | ||||
| 					stopMouseLook(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод выполняет необходимые действия при включении режима вращения объекта мышью. | ||||
| 		 * Реализация по умолчанию записывает начальные глобальные координаты курсора мыши в переменную <code>startMouseCoords</code>. | ||||
| 		 *  | ||||
| 		 * @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 { | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод выполняет обработку всех воздействий на объект. Если объект не установлен или свойство <code>enabled</code> | ||||
| 		 * равно <code>false</code>, метод не выполняется. | ||||
| 		 * <p> | ||||
| 		 * Алгоритм работы метода следующий: | ||||
| 		 * <ul> | ||||
| 		 * <li> Вычисляется время в секундах, прошедшее с последнего вызова метода (с последнего кадра). Это время считается | ||||
| 		 * 	длительностью текущего кадра; | ||||
| 		 * <li> Вызывается метод rotateObject(), который изменяет ориентацию объекта в соответствии с воздействиями; | ||||
| 		 * <li> Вызывается метод getDisplacement(), который вычисляет потенциальное перемещение объекта; | ||||
| 		 * <li> Вызывается метод applyDisplacement(), которому передаётся вектор перемещения, полученный на предыдущем шаге. | ||||
| 		 * Задачей метода является применение заданного вектора перемещения; | ||||
| 		 * <li> При необходимости вызываются обработчики начала и прекращения движения управляемого объекта;  | ||||
| 		 * </ul> | ||||
| 		 */ | ||||
| 		public function processInput():void { | ||||
| 			if (!_enabled || _object == null) { | ||||
| 				return; | ||||
| 			} | ||||
| 			var frameTime:Number = getTimer() - lastFrameTime; | ||||
| 			lastFrameTime += frameTime; | ||||
| 			if (frameTime > 100) { | ||||
| 				frameTime = 100; | ||||
| 			} | ||||
| 			frameTime /= 1000; | ||||
| 			 | ||||
| 			rotateObject(frameTime); | ||||
| 			getDisplacement(frameTime, _displacement); | ||||
| 			applyDisplacement(frameTime, _displacement); | ||||
| 			 | ||||
| 			// Обработка начала/окончания движения | ||||
| 			if (_object.changeRotationOrScaleOperation.queued || _object.changeCoordsOperation.queued) { | ||||
| 				if (!_isMoving) { | ||||
| 					_isMoving = true; | ||||
| 					if (onStartMoving != null) { | ||||
| 						onStartMoving.call(this); | ||||
| 					}  | ||||
| 				} | ||||
| 			} else { | ||||
| 				if (_isMoving) { | ||||
| 					_isMoving = false; | ||||
| 					if (onStopMoving != null) { | ||||
| 						onStopMoving.call(this); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод выполняет поворот объекта в соответствии с имеющимися воздействиями. Реализация по умолчанию не делает ничего. | ||||
| 		 *  | ||||
| 		 * @param frameTime длительность текущего кадра в секундах  | ||||
| 		 */ | ||||
| 		protected function rotateObject(frameTime:Number):void { | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод вычисляет потенциальное смещение объекта за кадр. Реализация по умолчанию не делает ничего. | ||||
| 		 *   | ||||
| 		 * @param frameTime длительность текущего кадра в секундах  | ||||
| 		 * @param displacement в эту переменную записывается вычисленное потенциальное смещение объекта | ||||
| 		 */ | ||||
| 		protected function getDisplacement(frameTime:Number, displacement:Point3D):void { | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод применяет потенциальное смещение объекта. Реализация по умолчанию не делает ничего. | ||||
| 		 *  | ||||
| 		 * @param frameTime длительность текущего кадра в секундах  | ||||
| 		 * @param displacement смещение объекта, которое нужно обработать | ||||
| 		 */ | ||||
| 		protected function applyDisplacement(frameTime:Number, displacement:Point3D):void { | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Метод выполняет привязку клавиши к действию. Одной клавише может быть назначено только одно действие. | ||||
| 		 *  | ||||
| 		 * @param keyCode код клавиши | ||||
| 		 * @param action наименование действия | ||||
| 		 *  | ||||
| 		 * @see #unbindKey() | ||||
| 		 * @see #unbindAll() | ||||
| 		 */ | ||||
| 		public function bindKey(keyCode:uint, action:String):void { | ||||
| 			var method:Function = actionBindings[action]; | ||||
| 			if (method != null) { | ||||
| 				keyBindings[keyCode] = method; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Очистка привязки клавиши. | ||||
| 		 *  | ||||
| 		 * @param keyCode код клавиши | ||||
| 		 *  | ||||
| 		 * @see #bindKey() | ||||
| 		 * @see #unbindAll() | ||||
| 		 */ | ||||
| 		public function unbindKey(keyCode:uint):void { | ||||
| 			keyBindings.remove(keyCode); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Очистка привязки всех клавиш. | ||||
| 		 *  | ||||
| 		 * @see #bindKey() | ||||
| 		 * @see #unbindKey() | ||||
| 		 */ | ||||
| 		public function unbindAll():void { | ||||
| 			keyBindings.clear(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод устанавливает привязки клавиш по умолчанию. Реализация по умолчанию не делает ничего. | ||||
| 		 *  | ||||
| 		 * @see #bindKey() | ||||
| 		 * @see #unbindKey() | ||||
| 		 * @see #unbindAll() | ||||
| 		 */ | ||||
| 		public function setDefaultBindings():void { | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Включение и выключение обработки клавиатурных событий. При включении выполняется метод <code>registerKeyboardListeners</code>, | ||||
| 		 * при выключении — <code>unregisterKeyboardListeners</code>. | ||||
| 		 *  | ||||
| 		 * @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); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Включение и выключение обработки мышиных событий. При включении выполняется метод <code>registerMouseListeners</code>, | ||||
| 		 * при выключении — <code>unregisterMouseListeners</code>. | ||||
| 		 *  | ||||
| 		 * @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; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,508 @@ | ||||
| package alternativa.engine3d.controllers { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Camera3D; | ||||
| 	import alternativa.engine3d.core.Mesh; | ||||
| 	import alternativa.engine3d.core.Object3D; | ||||
| 	import alternativa.engine3d.physics.Collision; | ||||
| 	import alternativa.types.Matrix3D; | ||||
| 	import alternativa.types.Point3D; | ||||
| 	import alternativa.utils.KeyboardUtils; | ||||
| 	import alternativa.utils.MathUtils; | ||||
| 	 | ||||
| 	import flash.display.DisplayObject; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Контроллер, реализующий управление движением объекта, находящегося в системе координат корневого объекта сцены.  | ||||
| 	 *  | ||||
| 	 * <p>Контроллер предоставляет два режима движения: режим ходьбы с учётом силы тяжести и режим полёта, в котором сила | ||||
| 	 * тяжести не учитывается. В обоих режимах может быть включена проверка столкновений с объектами сцены. Если проверка | ||||
| 	 * столкновений отключена, то в режиме ходьбы сила тяжести также игнорируется и дополнительно появляется возможность | ||||
| 	 * движения по вертикали. | ||||
| 	 *  | ||||
| 	 * <p>Для всех объектов, за исключением <code>Camera3D</code>, направлением "вперёд" считается направление его оси | ||||
| 	 * <code>Y</code>, направлением "вверх" — направление оси <code>Z</code>. Для объектов класса | ||||
| 	 * <code>Camera3D</code> направление "вперёд" совпадает с направлением локальной оси <code>Z</code>, а направление | ||||
| 	 * "вверх" противоположно направлению локальной оси <code>Y</code>. | ||||
| 	 *  | ||||
| 	 * <p>Вне зависимости от того, включена проверка столкновений или нет, координаты при перемещении расчитываются для | ||||
| 	 * эллипсоида, параметры которого устанавливаются через свойство <code>collider</code>. Координаты управляемого | ||||
| 	 * объекта вычисляются исходя из положения центра эллипсоида и положения объекта на вертикальной оси эллипсоида, | ||||
| 	 * задаваемого параметром <code>objectZPosition</code>. | ||||
| 	 *  | ||||
| 	 * <p>Команда <code>ACTION_UP</code> в режиме ходьбы при ненулевой гравитации вызывает прыжок, в остальных случаях | ||||
| 	 * происходит движение вверх. | ||||
| 	 */	 | ||||
| 	public class WalkController extends ObjectController { | ||||
| 		/** | ||||
| 		 * Величина ускорения свободного падения. При положительном значении сила тяжести направлена против оси Z, | ||||
| 		 * при отрицательном — по оси Z. | ||||
| 		 */ | ||||
| 		public var gravity:Number = 0; | ||||
| 		/** | ||||
| 		 * Вертикальная скорость прыжка. | ||||
| 		 */ | ||||
| 		public var jumpSpeed:Number = 0; | ||||
| 		/** | ||||
| 		 * Объект, на котором стоит эллипсоид при ненулевой гравитации. | ||||
| 		 */ | ||||
| 		private var _groundMesh:Mesh; | ||||
| 		/** | ||||
| 		 * Погрешность определения скорости. В режиме полёта или в режиме ходьбы при нахождении на поверхности | ||||
| 		 * скорость приравнивается к нулю, если её модуль не превышает заданного значения. | ||||
| 		 */ | ||||
| 		public var speedThreshold:Number = 1; | ||||
|  | ||||
| 		// Коэффициент эффективности управления перемещением при нахождении в воздухе в режиме ходьбы и нулевой гравитации. | ||||
| 		private var _airControlCoefficient:Number = 1; | ||||
|  | ||||
| 		private var _currentSpeed:Number = 0; | ||||
| 		 | ||||
| 		private var minGroundCos:Number = Math.cos(MathUtils.toRadian(70)); | ||||
| 		 | ||||
| 		private var destination:Point3D = new Point3D(); | ||||
| 		private var collision:Collision = new Collision(); | ||||
| 		 | ||||
| 		private var _objectZPosition:Number = 0.5; | ||||
| 		private var _flyMode:Boolean; | ||||
| 		private	var _onGround:Boolean; | ||||
| 		 | ||||
| 		private var velocity:Point3D = new Point3D(); | ||||
| 		private var tmpVelocity:Point3D = new Point3D(); | ||||
| 		 | ||||
| 		private var controlsActive:Boolean; | ||||
| 		 | ||||
| 		private var inJump:Boolean; | ||||
| 		private var startRotX:Number; | ||||
| 		private var startRotZ:Number; | ||||
|  | ||||
| 		// Координаты мышиного курсора в режиме mouse look в предыдущем кадре. | ||||
| 		private var prevMouseCoords:Point3D = new Point3D(); | ||||
| 		// Текущие координаты мышиного курсора в режиме mouse look. | ||||
| 		private var currentMouseCoords:Point3D = new Point3D(); | ||||
|  | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */ | ||||
| 		public function WalkController(eventSourceObject:DisplayObject) { | ||||
| 			super(eventSourceObject); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Объект, на котором стоит эллипсоид при ненулевой гравитации. | ||||
| 		 */ | ||||
| 		public function get groundMesh():Mesh { | ||||
| 			return _groundMesh; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Направление объекта на точку. В результате работы метода локальная ось объекта, соответствующая направлению "вперёд" | ||||
| 		 * будет направлена на указанную точку, а угол поворота вокруг этой оси будет равен нулю. | ||||
| 		 *  | ||||
| 		 * @param point координаты точки, на которую должен быть направлен объект | ||||
| 		 */ | ||||
| 		public function lookAt(point:Point3D):void { | ||||
| 			if (_object == null) { | ||||
| 				return; | ||||
| 			} | ||||
| 			var dx:Number = point.x - _object.x; | ||||
| 			var dy:Number = point.y - _object.y; | ||||
| 			var dz:Number = point.z - _object.z; | ||||
| 			_object.rotationX = Math.atan2(dz, Math.sqrt(dx * dx + dy * dy)) - (_object is Camera3D ? MathUtils.DEG90 : 0); | ||||
| 			_object.rotationY = 0; | ||||
| 			_object.rotationZ = -Math.atan2(dx, dy); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Коэффициент эффективности управления перемещением в режиме ходьбы при нахождении в воздухе и ненулевой гравитации. | ||||
| 		 * Значение 0 обозначает полное отсутствие контроля, значение 1 указывает, что управление так же эффективно, как при | ||||
| 		 * нахождении на поверхности. | ||||
| 		 *  | ||||
| 		 * @default 1 | ||||
| 		 */ | ||||
| 		public function get airControlCoefficient():Number { | ||||
| 			return _airControlCoefficient; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set airControlCoefficient(value:Number):void { | ||||
| 			_airControlCoefficient = value > 0 ? value : -value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Максимальный угол наклона поверхности в радианах, на которой возможен прыжок и на которой объект стоит на месте | ||||
| 		 * в отсутствие управляющих воздействий. Если угол наклона поверхности превышает заданное значение, свойство | ||||
| 		 * <code>onGround</code> будет иметь значение <code>false</code>. | ||||
| 		 *  | ||||
| 		 * @see #onGround | ||||
| 		 */ | ||||
| 		public function get maxGroundAngle():Number { | ||||
| 			return Math.acos(minGroundCos);  | ||||
| 		} | ||||
| 			 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set maxGroundAngle(value:Number):void { | ||||
| 			minGroundCos = Math.cos(value); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Положение управляемого объекта на оси Z эллипсоида. Значение 0 указывает положение в нижней точке эллипсоида, | ||||
| 		 * значение 1 -- положение в верхней точке эллипсоида. | ||||
| 		 */		 | ||||
| 		public function get objectZPosition():Number { | ||||
| 			return _objectZPosition; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set objectZPosition(value:Number):void { | ||||
| 			_objectZPosition = value; | ||||
| 			setObjectCoords(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Включене и выключение режима полёта. | ||||
| 		 *  | ||||
| 		 * @default false | ||||
| 		 */		 | ||||
| 		public function get flyMode():Boolean { | ||||
| 			return _flyMode; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set flyMode(value:Boolean):void { | ||||
| 			_flyMode = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Индикатор положения эллипсоида на поверхности в режиме ходьбы. Считается, что эллипсоид находится на поверхности, | ||||
| 		 * если угол наклона поверхности под ним не превышает заданного свойством <code>maxGroundAngle</code> значения. | ||||
| 		 *  | ||||
| 		 * @see #maxGroundAngle  | ||||
| 		 */ | ||||
| 		public function get onGround():Boolean { | ||||
| 			return _onGround; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Модуль текущей скорости. | ||||
| 		 */ | ||||
| 		public function get currentSpeed():Number { | ||||
| 			return _currentSpeed; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Установка привязки клавиш по умолчанию. Данный метод очищает все существующие привязки клавиш и устанавливает следующие: | ||||
| 		 * <table border="1" style="border-collapse: collapse"> | ||||
| 		 * <tr><th>Клавиша</th><th>Действие</th></tr> | ||||
| 		 * <tr><td>W</td><td>ACTION_FORWARD</td></tr> | ||||
| 		 * <tr><td>S</td><td>ACTION_BACK</td></tr> | ||||
| 		 * <tr><td>A</td><td>ACTION_LEFT</td></tr> | ||||
| 		 * <tr><td>D</td><td>ACTION_RIGHT</td></tr> | ||||
| 		 * <tr><td>SPACE</td><td>ACTION_UP</td></tr> | ||||
| 		 * <tr><td>CONTROL</td><td>ACTION_DOWN</td></tr> | ||||
| 		 * <tr><td>SHIFT</td><td>ACTION_ACCELERATE</td></tr> | ||||
| 		 * <tr><td>UP</td><td>ACTION_PITCH_UP</td></tr> | ||||
| 		 * <tr><td>DOWN</td><td>ACTION_PITCH_DOWN</td></tr> | ||||
| 		 * <tr><td>LEFT</td><td>ACTION_YAW_LEFT</td></tr> | ||||
| 		 * <tr><td>RIGHT</td><td>ACTION_YAW_RIGHT</td></tr> | ||||
| 		 * <tr><td>M</td><td>ACTION_MOUSE_LOOK</td></tr> | ||||
| 		 * </table> | ||||
| 		 */ | ||||
| 		override public function setDefaultBindings():void { | ||||
| 			unbindAll(); | ||||
| 			bindKey(KeyboardUtils.W, ACTION_FORWARD); | ||||
| 			bindKey(KeyboardUtils.S, ACTION_BACK); | ||||
| 			bindKey(KeyboardUtils.A, ACTION_LEFT); | ||||
| 			bindKey(KeyboardUtils.D, ACTION_RIGHT); | ||||
| 			bindKey(KeyboardUtils.SPACE, ACTION_UP); | ||||
| 			bindKey(KeyboardUtils.CONTROL, ACTION_DOWN); | ||||
| 			bindKey(KeyboardUtils.UP, ACTION_PITCH_UP); | ||||
| 			bindKey(KeyboardUtils.DOWN, ACTION_PITCH_DOWN); | ||||
| 			bindKey(KeyboardUtils.LEFT, ACTION_YAW_LEFT); | ||||
| 			bindKey(KeyboardUtils.RIGHT, ACTION_YAW_RIGHT); | ||||
| 			bindKey(KeyboardUtils.SHIFT, ACTION_ACCELERATE); | ||||
| 			bindKey(KeyboardUtils.M, ACTION_MOUSE_LOOK); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод выполняет поворот объекта в соответствии с имеющимися воздействиями. Взгляд вверх и вниз ограничен | ||||
| 		 * отклонением в 90 градусов от горизонтали. | ||||
| 		 *  | ||||
| 		 * @param frameTime длительность текущего кадра в секундах  | ||||
| 		 */ | ||||
| 		override protected function rotateObject(frameTime:Number):void { | ||||
| 			// Mouse look | ||||
| 			var rotX:Number; | ||||
| 			if (_mouseLookActive) { | ||||
| 				prevMouseCoords.copy(currentMouseCoords); | ||||
| 				currentMouseCoords.x = _eventsSource.stage.mouseX; | ||||
| 				currentMouseCoords.y = _eventsSource.stage.mouseY; | ||||
| 				if (!prevMouseCoords.equals(currentMouseCoords)) { | ||||
| 					_object.rotationZ = startRotZ + (startMouseCoords.x - currentMouseCoords.x) * _mouseCoefficientX; | ||||
| 					rotX = startRotX + (startMouseCoords.y - currentMouseCoords.y) * _mouseCoefficientY; | ||||
| 					if (_object is Camera3D) { | ||||
| 						// Коррекция поворота для камеры | ||||
| 						_object.rotationX = (rotX > MathUtils.DEG90) ? 0 : (rotX < -MathUtils.DEG90) ? -Math.PI : rotX - MathUtils.DEG90; | ||||
| 					} else { | ||||
| 						_object.rotationX = (rotX > MathUtils.DEG90) ? MathUtils.DEG90 : (rotX < -MathUtils.DEG90) ? -MathUtils.DEG90 : rotX; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			// Повороты влево-вправо | ||||
| 			if (_yawLeft) { | ||||
| 				_object.rotationZ += _yawSpeed * frameTime; | ||||
| 			} else if (_yawRight) { | ||||
| 				_object.rotationZ -= _yawSpeed * frameTime; | ||||
| 			} | ||||
| 			// Взгляд вверх-вниз | ||||
| 			rotX = NaN; | ||||
| 			if (_pitchUp) { | ||||
| 				rotX = _object.rotationX + _pitchSpeed * frameTime; | ||||
| 			} else if (_pitchDown) { | ||||
| 				rotX = _object.rotationX - _pitchSpeed * frameTime; | ||||
| 			} | ||||
| 			if (!isNaN(rotX)) { | ||||
| 				if (_object is Camera3D) { | ||||
| 					// Коррекция поворота для камеры | ||||
| 					_object.rotationX = (rotX > 0) ? 0 : (rotX < -Math.PI) ? -Math.PI : rotX; | ||||
| 				} else { | ||||
| 					_object.rotationX = (rotX > MathUtils.DEG90) ? MathUtils.DEG90 : (rotX < -MathUtils.DEG90) ? -MathUtils.DEG90 : rotX; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод вычисляет вектор потенциального смещения эллипсоида, учитывая режим перемещения, команды перемещения и силу тяжести. | ||||
| 		 *  | ||||
| 		 * @param frameTime длительность текущего кадра в секундах  | ||||
| 		 * @param displacement в эту переменную записывается вычисленное потенциальное смещение объекта | ||||
| 		 */ | ||||
| 		override protected function getDisplacement(frameTime:Number, displacement:Point3D):void { | ||||
| 			var cos:Number = 0; | ||||
| 			if (checkCollisions && !_flyMode) { | ||||
| 				// Проверка наличия под ногами поверхности | ||||
| 				displacement.x = 0; | ||||
| 				displacement.y = 0; | ||||
| 				displacement.z = - 0.5 * gravity * frameTime * frameTime; | ||||
| 				if (_collider.getCollision(_coords, displacement, collision)) { | ||||
| 					cos = collision.normal.z; | ||||
| 					_groundMesh = collision.face._mesh; | ||||
| 				} else { | ||||
| 					_groundMesh = null; | ||||
| 				} | ||||
| 			} | ||||
| 			_onGround = cos > minGroundCos; | ||||
| 			 | ||||
| 			if (_onGround && inJump) { | ||||
| 				inJump = false; | ||||
| 			} | ||||
| 			 | ||||
| 			var len:Number; | ||||
| 			var x:Number; | ||||
| 			var y:Number; | ||||
| 			var z:Number; | ||||
|  | ||||
| 			// При наличии управляющих воздействий расчитывается приращение скорости  | ||||
| 			controlsActive = _forward || _back || _right || _left || _up || _down; | ||||
| 			if (controlsActive) { | ||||
| 				if (_flyMode) { | ||||
| 					tmpVelocity.x = 0; | ||||
| 					tmpVelocity.y = 0; | ||||
| 					tmpVelocity.z = 0; | ||||
| 					// Режим полёта, ускорения направлены вдоль локальных осей | ||||
| 					// Ускорение вперёд-назад | ||||
| 					if (_forward) { | ||||
| 						tmpVelocity.y = 1; | ||||
| 					} else if (_back) { | ||||
| 						tmpVelocity.y = -1; | ||||
| 					} | ||||
| 					// Ускорение влево-вправо | ||||
| 					if (_right) { | ||||
| 						tmpVelocity.x = 1; | ||||
| 					} else if (_left) { | ||||
| 						tmpVelocity.x = -1; | ||||
| 					} | ||||
| 					// Ускорение вверх-вниз | ||||
| 					if (_up) { | ||||
| 						tmpVelocity.z = 1; | ||||
| 					} else if (_down) { | ||||
| 						tmpVelocity.z = -1; | ||||
| 					} | ||||
| 					var matrix:Matrix3D = _object.transformation; | ||||
| 					x = tmpVelocity.x; | ||||
| 					if (_object is Camera3D) { | ||||
| 						y = -tmpVelocity.z; | ||||
| 						z = tmpVelocity.y; | ||||
| 					} else { | ||||
| 						y = tmpVelocity.y; | ||||
| 						z = tmpVelocity.z; | ||||
| 					} | ||||
| 					// Поворот вектора из локальной системы координат объекта в глобальную | ||||
| 					velocity.x += (x * matrix.a + y * matrix.b + z * matrix.c) * _speed; | ||||
| 					velocity.y += (x * matrix.e + y * matrix.f + z * matrix.g) * _speed; | ||||
| 					velocity.z += (x * matrix.i + y * matrix.j + z * matrix.k) * _speed; | ||||
| 				} else { | ||||
| 					 | ||||
| 					// Режим хождения, ускорения вперёд-назад-влево-вправо лежат в глобальной плоскости XY, вверх-вниз направлены вдоль глобальной оси Z | ||||
| 					var heading:Number = _object.rotationZ; | ||||
| 					var headingCos:Number = Math.cos(heading); | ||||
| 					var headingSin:Number = Math.sin(heading); | ||||
| 					 | ||||
| 					var spd:Number = _speed; | ||||
| 					if (gravity != 0 && !_onGround) { | ||||
| 						spd *= _airControlCoefficient; | ||||
| 					} | ||||
| 					 | ||||
| 					// Вперёд-назад | ||||
| 					if (_forward) { | ||||
| 						velocity.x -= spd * headingSin; | ||||
| 						velocity.y += spd * headingCos; | ||||
| 					} else if (_back) { | ||||
| 						velocity.x += spd * headingSin; | ||||
| 						velocity.y -= spd * headingCos; | ||||
| 					} | ||||
| 					// Влево-вправо | ||||
| 					if (_right) { | ||||
| 						velocity.x += spd * headingCos; | ||||
| 						velocity.y += spd * headingSin; | ||||
| 					} else if (_left) { | ||||
| 						velocity.x -= spd * headingCos; | ||||
| 						velocity.y -= spd * headingSin; | ||||
| 					} | ||||
| 					if (gravity == 0) { | ||||
| 						// Ускорение вверх-вниз | ||||
| 						if (_up) { | ||||
| 							velocity.z += _speed; | ||||
| 						} else if (_down) { | ||||
| 							velocity.z -= _speed; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Управление неактивно, замедление движения | ||||
| 				len = 1 / Math.pow(3, frameTime * 10); | ||||
| 				if (_flyMode || gravity == 0) { | ||||
| 					velocity.x *= len; | ||||
| 					velocity.y *= len; | ||||
| 					velocity.z *= len; | ||||
| 				} else { | ||||
| 					if (_onGround) { | ||||
| 						velocity.x *= len; | ||||
| 						velocity.y *= len; | ||||
| 						if (velocity.z < 0) { | ||||
| 							velocity.z *= len; | ||||
| 						} | ||||
| 					} else { | ||||
| 						if (cos > 0 && velocity.z > 0) { | ||||
| 							velocity.z = 0; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			// Прыжок | ||||
| 			if (_onGround && _up && !inJump) { | ||||
| 				velocity.z = jumpSpeed; | ||||
| 				inJump = true; | ||||
| 				_onGround = false; | ||||
| 				cos = 0; | ||||
| 			} | ||||
| 			// В режиме ходьбы добавляется ускорение свободного падения, если находимся не на ровной поверхности | ||||
| 			if (!(_flyMode || _onGround)) { | ||||
| 				velocity.z -= gravity * frameTime; | ||||
| 			} | ||||
| 			 | ||||
| 			// Ограничение скорости | ||||
| 			var max:Number = _accelerate ? _speed * _speedMultiplier : _speed; | ||||
| 			if (_flyMode || gravity == 0) { | ||||
| 				len = velocity.length; | ||||
| 				if (len > max) { | ||||
| 					velocity.length = max; | ||||
| 				} | ||||
| 			} else { | ||||
| 				len = Math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y); | ||||
| 				if (len > max) { | ||||
| 					velocity.x *= max / len; | ||||
| 					velocity.y *= max / len; | ||||
| 				} | ||||
| 				if (cos > 0 && velocity.z > 0) { | ||||
| 					velocity.z = 0; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Cмещение за кадр | ||||
| 			displacement.x = velocity.x * frameTime; | ||||
| 			displacement.y = velocity.y * frameTime; | ||||
| 			displacement.z = velocity.z * frameTime; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод применяет потенциальный вектор смещения к эллипсоиду с учётом столкновений с геометрией сцены, если включён | ||||
| 		 * соотвествующий режим. | ||||
| 		 *    | ||||
| 		 * @param frameTime время кадра в секундах | ||||
| 		 * @param displacement векотр потенциального смещения эллипсоида | ||||
| 		 */ | ||||
| 		override protected function applyDisplacement(frameTime:Number, displacement:Point3D):void { | ||||
| 			if (checkCollisions) { | ||||
| 				_collider.calculateDestination(_coords, displacement, destination); | ||||
|  | ||||
| 				displacement.x = destination.x - _coords.x; | ||||
| 				displacement.y = destination.y - _coords.y; | ||||
| 				displacement.z = destination.z - _coords.z; | ||||
| 			} else { | ||||
| 				destination.x = _coords.x + displacement.x; | ||||
| 				destination.y = _coords.y + displacement.y; | ||||
| 				destination.z = _coords.z + displacement.z; | ||||
| 			} | ||||
|  | ||||
| 			velocity.x = displacement.x / frameTime; | ||||
| 			velocity.y = displacement.y / frameTime; | ||||
| 			velocity.z = displacement.z / frameTime; | ||||
|  | ||||
| 			_coords.x = destination.x; | ||||
| 			_coords.y = destination.y; | ||||
| 			_coords.z = destination.z; | ||||
| 			setObjectCoords(); | ||||
| 			 | ||||
| 			var len:Number = Math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z); | ||||
| 			if (len < speedThreshold) { | ||||
| 				velocity.x = 0; | ||||
| 				velocity.y = 0; | ||||
| 				velocity.z = 0; | ||||
| 				_currentSpeed = 0; | ||||
| 			} else { | ||||
| 				_currentSpeed = len; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод устанавливает координаты управляемого объекта в зависимости от параметра <code>objectZPosition</code>. | ||||
| 		 *  | ||||
| 		 * @see #objectZPosition | ||||
| 		 */ | ||||
| 		override protected function setObjectCoords():void { | ||||
| 			_object.x = _coords.x; | ||||
| 			_object.y = _coords.y; | ||||
| 			_object.z = _coords.z + (2 * _objectZPosition - 1) * _collider.radiusZ; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод выполняет необходимые действия при включении вращения объекта мышью. | ||||
| 		 */		 | ||||
| 		override protected function startMouseLook():void { | ||||
| 			super.startMouseLook(); | ||||
| 			startRotX = _object is Camera3D ? _object.rotationX + MathUtils.DEG90 : _object.rotationX; | ||||
| 			startRotZ = _object.rotationZ; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Tubix
					Tubix