more versions added

This commit is contained in:
Tubix
2024-10-05 12:11:16 +01:00
parent 413f563f33
commit c32c7e8c34
7661 changed files with 1343635 additions and 0 deletions

View File

@@ -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>, направлением "вверх" &mdash; направление оси <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,
* при отрицательном &mdash; по оси 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;
}
}
}