mirror of
				https://github.com/MapMakersAndProgrammers/alternativa3d-archive.git
				synced 2025-10-30 17:05:17 -07:00 
			
		
		
		
	
		
			
				
	
	
		
			508 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			ActionScript
		
	
	
	
	
	
			
		
		
	
	
			508 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			ActionScript
		
	
	
	
	
	
| 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;
 | ||
| 		}
 | ||
| 	}
 | ||
| } | 
