Синтаксис:
Используется csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// ИСПРАВЬ: ограничители
/// <summary>
/// Этот класс помогает поворачивать объект с постоянной скоростью и ограничителями
/// </summary>
public class RotationHelper : MonoBehaviour
{
/// <summary>
/// Цель, к которой надо повернуться
/// </summary>
public Transform target;
/// <summary>
/// Ось тангажа
/// </summary>
public Transform axisPitch;
/// <summary>
/// Ось рыскания
/// </summary>
public Transform axisYaw;
/// <summary>
/// Заморожен ли тангаж
/// </summary>
public bool rotationFreezePitch;
/// <summary>
/// Заморожено ли рыскание
/// </summary>
public bool rotationFreezeYaw;
/// <summary>
/// Ограничен ли тангаж
/// </summary>
public bool limitPitch;
/// <summary>
/// Ограничено ли рыскание
/// </summary>
public bool limitYaw;
/// <summary>
/// Верхний ограничитель тангажа
/// </summary>
public float limitMinusAnglePitch;
/// <summary>
/// Нижний ограничитель тангажа
/// </summary>
public float limitPlusAnglePitch;
/// <summary>
/// Левый ограничитель рыскания
/// </summary>
public float limitMinusAngleYaw;
/// <summary>
/// Правый ограничитель рыскания
/// </summary>
public float limitPlusAngleYaw;
/// <summary>
/// Угловая скорость тангажа
/// </summary>
public float degreesPerSecondPitch;
/// <summary>
/// Угловая скорость рыскания
/// </summary>
public float degreesPerSecondYaw;
/// <summary>
/// Необходимый угол тангажа
/// </summary>
private float anglePitch;
/// <summary>
/// Необходимый угол рыскания
/// </summary>
private float angleYaw;
/// <summary>
/// Последний раз цель была здесь
/// </summary>
private Vector3 lastTargetPosition;
/// <summary>
/// Максимально допустимое отклонение от цели
/// </summary>
public float deviationTarget;
/// <summary>
/// Инициализация класса RotationHelper происходит здесь
/// </summary>
void Start()
{
if (degreesPerSecondPitch == 0)
Debug.LogWarning("WARNING! Degrees Per Second Pitch = 0");
if (degreesPerSecondYaw == 0)
Debug.LogWarning("WARNING! Degrees Per Second Yaw = 0");
if (axisPitch == null)
Debug.LogWarning("WARNING! Axis Pitch = null");
if (axisYaw == null)
Debug.LogWarning("WARNING! Axis Yaw = null");
}
/// <summary>
/// Вызывается каждый кадр
/// </summary>
void Update()
{
float _delta = Time.deltaTime;
if (!rotationFreezePitch && anglePitch != 0)// Если тангаж не заморожен и ещё надо поворачиваться
axisPitch.localEulerAngles = new Vector3(axisPitch.localEulerAngles.x + NextStepAngle(ref anglePitch, degreesPerSecondPitch, _delta),
axisPitch.localEulerAngles.y, axisPitch.localEulerAngles.z);// Поворачиваемся с нужной скоростью
if (!rotationFreezeYaw && angleYaw != 0)// Если рыскание не заморожено и ещё надо поворачиваться
axisYaw.localEulerAngles = new Vector3(axisYaw.localEulerAngles.x, axisYaw.localEulerAngles.y + NextStepAngle(ref angleYaw, degreesPerSecondYaw, _delta),
axisYaw.localEulerAngles.z);// Поворачиваемся с нужной скоростью
if ((lastTargetPosition - target.position).magnitude > deviationTarget)// Если цель сильно сместилась
{
ReTarget();// Перенацеливаемся
lastTargetPosition = target.position;// Запоминаем новую позицию цели
}
}
/// <summary>
/// Обновление цели
/// </summary>
public void ReTarget()
{
if (limitPitch)// Если тангаж ограничен
{
if (!rotationFreezePitch)// Если тангаж не заморожен
anglePitch = ReCalculateLimitedAngleToTarget(target.position, axisPitch, axisPitch.localEulerAngles.x, limitMinusAnglePitch, limitPlusAnglePitch, Axis.Pitch);// Расчитываем требуемый угол тангажа с учётом ограничителей
Debug.Log(name + " Pitch " + anglePitch);
}
else
{
anglePitch = ReCalculateAngleToTarget(target.position, axisPitch, Axis.Pitch);
}// Расчитываем требуемый угол тангажа без учёта ограничителей
if (limitYaw)// Если рыскание ограничено
{
if (!rotationFreezeYaw)// Если рыскание не заморожено
angleYaw = ReCalculateLimitedAngleToTarget(target.position, axisYaw, axisYaw.localEulerAngles.y, limitMinusAngleYaw, limitPlusAngleYaw, Axis.Yaw);// Расчитываем требуемый угол раскания с учётом ограничителей
Debug.Log(name + " Yaw " + angleYaw);
}
else
{
angleYaw = ReCalculateAngleToTarget(target.position, axisYaw, Axis.Yaw);
}// Расчитываем требуемый угол раскания без учёта ограничителей
}
/// <summary>
/// Эта функция расчитывает и возвращает угол, на который нужно повернуть ось, что бы повернуться к указанной цели. Ограничители не учитываются.
/// </summary>
/// <param name="_target">Цель</param>
/// <param name="_axisTransform">Ось</param>
/// <param name="_axis">Вокруг этой оси поворачиваем</param>
/// <returns>Угол, на который нужно повернуться</returns>
public float ReCalculateAngleToTarget(Vector3 _target, Transform _axisTransform, Axis _axis)
{
float _angle = 0f;// Подготавливаем угол
_target = _axisTransform.InverseTransformPoint(_target);// Переносим цель в нашу систему координат
switch (_axis)// Выбираем ось
{
case Axis.Pitch:// Тангаж
_angle = PreparationPitch(_target);
break;// И всё
case Axis.Yaw:// Рыскание
_angle = PreparationYaw(_target);// Вычисляем угол между носом и целью
break;// И всё
case Axis.Roll:// Крен
_angle = PreparationRoll(_target);// Вычисляем угол между носом и целью
break;// И всё
}
return _angle;// Возвращаем угол, на который надо повернуться
}
/// <summary>
/// Эта функция расчитывает и возвращает угол, на который нужно повернуть ось, что бы повернуться к указанной цели. Ограничители учитываются.
/// </summary>
/// <param name="_target">Цель</param>
/// <param name="_axisTransform">Ось</param>
/// <param name="_currentAngleAxis">Текущий угол поворота оси</param>
/// <param name="_limitMinus">Ограничитель оси слева</param>
/// <param name="_limitPlus">Ограничитель оси справа</param>
/// <param name="_axis">Вокруг этой оси поворачиваем</param>
/// <returns>Угол, на который нужно повернуться</returns>
public float ReCalculateLimitedAngleToTarget(Vector3 _target, Transform _axisTransform, float _currentAngleAxis, float _limitMinus, float _limitPlus, Axis _axis)
{
float _angle = 0f;// Подготавливаем угол
_target = _axisTransform.InverseTransformPoint(_target);// Переносим цель в нашу систему координат
switch (_axis)// Выбираем ось
{
case Axis.Pitch:// Тангаж
_angle = PreparationPitch(_target);
break;// И всё
case Axis.Yaw:// Рыскание
_angle = PreparationYaw(_target);// Вычисляем угол между носом и целью
break;// И всё
case Axis.Roll:// Крен
_angle = PreparationRoll(_target);// Вычисляем угол между носом и целью
break;// И всё
}
float _limit;// Подготавливаем единый лимит
if((_angle + _currentAngleAxis) < 0f)// Если угол отрицательный
_limit = _limitMinus;// Устанавливаем единый лимит
else if((_angle + _currentAngleAxis) > 0f)// Если угол положительный
_limit = _limitPlus;// Устанавливаем единый лимит
else// Иначе
return 0f;// Не надо никуда поворачивать
if (Mathf.Abs(_angle + _currentAngleAxis) > Mathf.Abs(_limit))// Если превышен лимит поворота
_angle = _limit - _currentAngleAxis;// Ограничиваем поворот
return _angle;// Возвращаем угол, на который надо повернуться
}
/// <summary>
/// Эта функция расчитывает и возвращает угол на который надо повернуться за один кадр
/// </summary>
/// <param name="_angle">Оставшийся угол, на который надо повернуться</param>
/// <param name="_degreesPerSecond">Скорость поворота в градусах/в_секунду</param>
/// <returns>Угол, на который надо повернуться</returns>
public float NextStepAngle(ref float _angle, float _degreesPerSecond, float deltaTime)
{
float _angleStep = 0;// Подготавливаем шаг поворота
int _plusMinus;// Подготавливаем изменятель знака
if (_angle > 0)// Если угол положительный
_plusMinus = 1;// Устанавливаем изменятель знака
else if (_angle < 0)// Если угол отрицательный
_plusMinus = -1;// Устанавливаем изменятель знака
else// Иначе
return 0f;// Не надо никуда поворачивать
_angleStep = _plusMinus * (deltaTime * _degreesPerSecond);// Поворачиваться будем с постоянной угловой скоростью
if (Mathf.Abs(_angleStep) > Mathf.Abs(_angle))// Если проскочили
_angleStep = _angle;// Сдаём назад
_angle -= _angleStep;// Уменьшаем оставшийся угол
return _angleStep;//
}
/// <summary>
/// Корректирует положение цели и возвращает угол тангажа
/// </summary>
/// <param name="_target">Цель в локальной системе координат оси</param>
/// <returns>Угол, на который надо повернуть</returns>
public float PreparationPitch(Vector3 _target)
{
float _x = 0f;// Цель всегда должна быть точно перед нами
float _y = _target.y;// Устанавливаем компоненту Y
_target.y = 0f;// Обнуляем высоту цели
float _z = _target.magnitude;// Устанавливаем компоненту Z
_target = new Vector3(_x, _y, _z);// Устанавливаем новую позицию цели
//if (_y > 0)// Если цель выше носа
// return -Vector3.SignedAngle(Vector3.forward, _target, Vector3.right);// Вычисляем угол между носом и целью
//else// Иначе
// return Vector3.SignedAngle(Vector3.forward, _target, Vector3.right);// Вычисляем угол между носом и целью
return Vector3.SignedAngle(Vector3.forward, _target, Vector3.right);// Вычисляем угол между носом и целью
}
/// <summary>
/// Корректирует положение цели и возвращает угол рыскания
/// </summary>
/// <param name="_target">Цель в локальной системе координат оси</param>
/// <returns>Угол, на который надо повернуть</returns>
public float PreparationYaw(Vector3 _target)
{
float _x = _target.x;// Устанавливаем компоненту X
float _y = 0f;// Высота цели на уровне носа
float _z = _target.z;// Устанавливаем компоненту Z
_target = new Vector3(_x, _y, _z);// Устанавливаем новую позицию цели
return Vector3.SignedAngle(Vector3.forward, _target, Vector3.up);
}
/// <summary>
/// Корректирует положение цели и возвращает угол крена
/// </summary>
/// <param name="_target">Цель в локальной системе координат оси</param>
/// <returns>Угол, на который надо повернуть</returns>
public float PreparationRoll(Vector3 _target)
{
return Vector3.SignedAngle(Vector3.forward, _target, Vector3.forward);
}
}
/// <summary>
/// Перечисление осей, а точнее поворотов вокруг них
/// </summary>
public enum Axis
{
Pitch, // Тангаж
Yaw, // Рыскание
Roll // Крен
}
using System.Collections.Generic;
using UnityEngine;
// ИСПРАВЬ: ограничители
/// <summary>
/// Этот класс помогает поворачивать объект с постоянной скоростью и ограничителями
/// </summary>
public class RotationHelper : MonoBehaviour
{
/// <summary>
/// Цель, к которой надо повернуться
/// </summary>
public Transform target;
/// <summary>
/// Ось тангажа
/// </summary>
public Transform axisPitch;
/// <summary>
/// Ось рыскания
/// </summary>
public Transform axisYaw;
/// <summary>
/// Заморожен ли тангаж
/// </summary>
public bool rotationFreezePitch;
/// <summary>
/// Заморожено ли рыскание
/// </summary>
public bool rotationFreezeYaw;
/// <summary>
/// Ограничен ли тангаж
/// </summary>
public bool limitPitch;
/// <summary>
/// Ограничено ли рыскание
/// </summary>
public bool limitYaw;
/// <summary>
/// Верхний ограничитель тангажа
/// </summary>
public float limitMinusAnglePitch;
/// <summary>
/// Нижний ограничитель тангажа
/// </summary>
public float limitPlusAnglePitch;
/// <summary>
/// Левый ограничитель рыскания
/// </summary>
public float limitMinusAngleYaw;
/// <summary>
/// Правый ограничитель рыскания
/// </summary>
public float limitPlusAngleYaw;
/// <summary>
/// Угловая скорость тангажа
/// </summary>
public float degreesPerSecondPitch;
/// <summary>
/// Угловая скорость рыскания
/// </summary>
public float degreesPerSecondYaw;
/// <summary>
/// Необходимый угол тангажа
/// </summary>
private float anglePitch;
/// <summary>
/// Необходимый угол рыскания
/// </summary>
private float angleYaw;
/// <summary>
/// Последний раз цель была здесь
/// </summary>
private Vector3 lastTargetPosition;
/// <summary>
/// Максимально допустимое отклонение от цели
/// </summary>
public float deviationTarget;
/// <summary>
/// Инициализация класса RotationHelper происходит здесь
/// </summary>
void Start()
{
if (degreesPerSecondPitch == 0)
Debug.LogWarning("WARNING! Degrees Per Second Pitch = 0");
if (degreesPerSecondYaw == 0)
Debug.LogWarning("WARNING! Degrees Per Second Yaw = 0");
if (axisPitch == null)
Debug.LogWarning("WARNING! Axis Pitch = null");
if (axisYaw == null)
Debug.LogWarning("WARNING! Axis Yaw = null");
}
/// <summary>
/// Вызывается каждый кадр
/// </summary>
void Update()
{
float _delta = Time.deltaTime;
if (!rotationFreezePitch && anglePitch != 0)// Если тангаж не заморожен и ещё надо поворачиваться
axisPitch.localEulerAngles = new Vector3(axisPitch.localEulerAngles.x + NextStepAngle(ref anglePitch, degreesPerSecondPitch, _delta),
axisPitch.localEulerAngles.y, axisPitch.localEulerAngles.z);// Поворачиваемся с нужной скоростью
if (!rotationFreezeYaw && angleYaw != 0)// Если рыскание не заморожено и ещё надо поворачиваться
axisYaw.localEulerAngles = new Vector3(axisYaw.localEulerAngles.x, axisYaw.localEulerAngles.y + NextStepAngle(ref angleYaw, degreesPerSecondYaw, _delta),
axisYaw.localEulerAngles.z);// Поворачиваемся с нужной скоростью
if ((lastTargetPosition - target.position).magnitude > deviationTarget)// Если цель сильно сместилась
{
ReTarget();// Перенацеливаемся
lastTargetPosition = target.position;// Запоминаем новую позицию цели
}
}
/// <summary>
/// Обновление цели
/// </summary>
public void ReTarget()
{
if (limitPitch)// Если тангаж ограничен
{
if (!rotationFreezePitch)// Если тангаж не заморожен
anglePitch = ReCalculateLimitedAngleToTarget(target.position, axisPitch, axisPitch.localEulerAngles.x, limitMinusAnglePitch, limitPlusAnglePitch, Axis.Pitch);// Расчитываем требуемый угол тангажа с учётом ограничителей
Debug.Log(name + " Pitch " + anglePitch);
}
else
{
anglePitch = ReCalculateAngleToTarget(target.position, axisPitch, Axis.Pitch);
}// Расчитываем требуемый угол тангажа без учёта ограничителей
if (limitYaw)// Если рыскание ограничено
{
if (!rotationFreezeYaw)// Если рыскание не заморожено
angleYaw = ReCalculateLimitedAngleToTarget(target.position, axisYaw, axisYaw.localEulerAngles.y, limitMinusAngleYaw, limitPlusAngleYaw, Axis.Yaw);// Расчитываем требуемый угол раскания с учётом ограничителей
Debug.Log(name + " Yaw " + angleYaw);
}
else
{
angleYaw = ReCalculateAngleToTarget(target.position, axisYaw, Axis.Yaw);
}// Расчитываем требуемый угол раскания без учёта ограничителей
}
/// <summary>
/// Эта функция расчитывает и возвращает угол, на который нужно повернуть ось, что бы повернуться к указанной цели. Ограничители не учитываются.
/// </summary>
/// <param name="_target">Цель</param>
/// <param name="_axisTransform">Ось</param>
/// <param name="_axis">Вокруг этой оси поворачиваем</param>
/// <returns>Угол, на который нужно повернуться</returns>
public float ReCalculateAngleToTarget(Vector3 _target, Transform _axisTransform, Axis _axis)
{
float _angle = 0f;// Подготавливаем угол
_target = _axisTransform.InverseTransformPoint(_target);// Переносим цель в нашу систему координат
switch (_axis)// Выбираем ось
{
case Axis.Pitch:// Тангаж
_angle = PreparationPitch(_target);
break;// И всё
case Axis.Yaw:// Рыскание
_angle = PreparationYaw(_target);// Вычисляем угол между носом и целью
break;// И всё
case Axis.Roll:// Крен
_angle = PreparationRoll(_target);// Вычисляем угол между носом и целью
break;// И всё
}
return _angle;// Возвращаем угол, на который надо повернуться
}
/// <summary>
/// Эта функция расчитывает и возвращает угол, на который нужно повернуть ось, что бы повернуться к указанной цели. Ограничители учитываются.
/// </summary>
/// <param name="_target">Цель</param>
/// <param name="_axisTransform">Ось</param>
/// <param name="_currentAngleAxis">Текущий угол поворота оси</param>
/// <param name="_limitMinus">Ограничитель оси слева</param>
/// <param name="_limitPlus">Ограничитель оси справа</param>
/// <param name="_axis">Вокруг этой оси поворачиваем</param>
/// <returns>Угол, на который нужно повернуться</returns>
public float ReCalculateLimitedAngleToTarget(Vector3 _target, Transform _axisTransform, float _currentAngleAxis, float _limitMinus, float _limitPlus, Axis _axis)
{
float _angle = 0f;// Подготавливаем угол
_target = _axisTransform.InverseTransformPoint(_target);// Переносим цель в нашу систему координат
switch (_axis)// Выбираем ось
{
case Axis.Pitch:// Тангаж
_angle = PreparationPitch(_target);
break;// И всё
case Axis.Yaw:// Рыскание
_angle = PreparationYaw(_target);// Вычисляем угол между носом и целью
break;// И всё
case Axis.Roll:// Крен
_angle = PreparationRoll(_target);// Вычисляем угол между носом и целью
break;// И всё
}
float _limit;// Подготавливаем единый лимит
if((_angle + _currentAngleAxis) < 0f)// Если угол отрицательный
_limit = _limitMinus;// Устанавливаем единый лимит
else if((_angle + _currentAngleAxis) > 0f)// Если угол положительный
_limit = _limitPlus;// Устанавливаем единый лимит
else// Иначе
return 0f;// Не надо никуда поворачивать
if (Mathf.Abs(_angle + _currentAngleAxis) > Mathf.Abs(_limit))// Если превышен лимит поворота
_angle = _limit - _currentAngleAxis;// Ограничиваем поворот
return _angle;// Возвращаем угол, на который надо повернуться
}
/// <summary>
/// Эта функция расчитывает и возвращает угол на который надо повернуться за один кадр
/// </summary>
/// <param name="_angle">Оставшийся угол, на который надо повернуться</param>
/// <param name="_degreesPerSecond">Скорость поворота в градусах/в_секунду</param>
/// <returns>Угол, на который надо повернуться</returns>
public float NextStepAngle(ref float _angle, float _degreesPerSecond, float deltaTime)
{
float _angleStep = 0;// Подготавливаем шаг поворота
int _plusMinus;// Подготавливаем изменятель знака
if (_angle > 0)// Если угол положительный
_plusMinus = 1;// Устанавливаем изменятель знака
else if (_angle < 0)// Если угол отрицательный
_plusMinus = -1;// Устанавливаем изменятель знака
else// Иначе
return 0f;// Не надо никуда поворачивать
_angleStep = _plusMinus * (deltaTime * _degreesPerSecond);// Поворачиваться будем с постоянной угловой скоростью
if (Mathf.Abs(_angleStep) > Mathf.Abs(_angle))// Если проскочили
_angleStep = _angle;// Сдаём назад
_angle -= _angleStep;// Уменьшаем оставшийся угол
return _angleStep;//
}
/// <summary>
/// Корректирует положение цели и возвращает угол тангажа
/// </summary>
/// <param name="_target">Цель в локальной системе координат оси</param>
/// <returns>Угол, на который надо повернуть</returns>
public float PreparationPitch(Vector3 _target)
{
float _x = 0f;// Цель всегда должна быть точно перед нами
float _y = _target.y;// Устанавливаем компоненту Y
_target.y = 0f;// Обнуляем высоту цели
float _z = _target.magnitude;// Устанавливаем компоненту Z
_target = new Vector3(_x, _y, _z);// Устанавливаем новую позицию цели
//if (_y > 0)// Если цель выше носа
// return -Vector3.SignedAngle(Vector3.forward, _target, Vector3.right);// Вычисляем угол между носом и целью
//else// Иначе
// return Vector3.SignedAngle(Vector3.forward, _target, Vector3.right);// Вычисляем угол между носом и целью
return Vector3.SignedAngle(Vector3.forward, _target, Vector3.right);// Вычисляем угол между носом и целью
}
/// <summary>
/// Корректирует положение цели и возвращает угол рыскания
/// </summary>
/// <param name="_target">Цель в локальной системе координат оси</param>
/// <returns>Угол, на который надо повернуть</returns>
public float PreparationYaw(Vector3 _target)
{
float _x = _target.x;// Устанавливаем компоненту X
float _y = 0f;// Высота цели на уровне носа
float _z = _target.z;// Устанавливаем компоненту Z
_target = new Vector3(_x, _y, _z);// Устанавливаем новую позицию цели
return Vector3.SignedAngle(Vector3.forward, _target, Vector3.up);
}
/// <summary>
/// Корректирует положение цели и возвращает угол крена
/// </summary>
/// <param name="_target">Цель в локальной системе координат оси</param>
/// <returns>Угол, на который надо повернуть</returns>
public float PreparationRoll(Vector3 _target)
{
return Vector3.SignedAngle(Vector3.forward, _target, Vector3.forward);
}
}
/// <summary>
/// Перечисление осей, а точнее поворотов вокруг них
/// </summary>
public enum Axis
{
Pitch, // Тангаж
Yaw, // Рыскание
Roll // Крен
}
Прикладываю RotationHelper.UnityPackage со скриптом и демо-сценой. Размер 169 КБ, а создано оно в Unity 2017.3.1f1, но должно заработать и в других версиях.