Хлопающая ротация дочерних объектов в единстве - PullRequest
0 голосов
/ 11 ноября 2018

У меня есть игровой объект, который содержит еще один игровой объект, который должен вращаться по направлению к цели (представьте танковую башню). И вот я создал следующий скрипт:

public class Rotator : MonoBehaviour {

    public GameObject _enemy;

    void Update () {
        var actualTarget = _enemy.transform.position;
        var targetDir = actualTarget - transform.position;
        var step = 2 * Time.deltaTime;
        var target = Quaternion.LookRotation(targetDir.normalized, Vector3.up);
        var actual = target * Quaternion.Inverse(transform.parent.rotation);
        var targetRotation = Quaternion.Slerp(transform.localRotation, actual, step);

        targetRotation.eulerAngles = ClampRotation(targetRotation.eulerAngles);
        transform.localRotation = targetRotation;
    }

    private static Vector3 ClampRotation(Vector3 eulerAngles) {
        var x = Mathf.Clamp(eulerAngles.x > 180 ? eulerAngles.x - 360 : eulerAngles.x, -180, 180);
        var y = Mathf.Clamp(eulerAngles.y > 180 ? eulerAngles.y - 360 : eulerAngles.y, -45, 45);

        return new Vector3(x, y, 0);
    }

}

Настройка объектов:

enter image description here

Вращение объекта с именем "parent" составляет 90 градусов по оси Y, все остальное не поворачивается.

Зажим по оси y работает хорошо - вращение остается в диапазоне от -45 до 45 градусов. Однако вращение не работает на оси х (с зажимом или без него). Итак, цель в том, чтобы при перемещении куба влево или вправо красный вращался между [-45,45] градусами вокруг оси Y, а когда я перемещал его сверху или снизу, красный вращался между [-180,180] градусами. вокруг оси X У меня был некоторый успех, используя метод LookAt класса Transform, но по какой-то причине, если я попытаюсь вручную изменить eulerAngles из localRotation, он внезапно теряет возможность вращаться назад по оси X, даже если я я только что-то делаю со значениями Y ...

enter image description here

1 Ответ

0 голосов
/ 11 ноября 2018

После долгих часов проб и ошибок и безумного просмотра интернета мне удалось найти ответ, который я мог бы адаптировать к своим потребностям. Слово комментария к первому оператору if метода Clamp - это полезно, если я хочу, чтобы объект также был зажат в перевернутом положении (если цель находится позади него):

void Update() {
    transform.LookAt(_target.transform);

    var rotation = transform.localRotation;
    var eulers = ClampRotation(rotation.eulerAngles);

    transform.localEulerAngles = eulers;
}

private static Vector3 ClampRotation(Vector3 eulerAngles) {
    var x = Clamp(eulerAngles.x, -60, 60);
    var y = Clamp(eulerAngles.y, -45, 45);

    return new Vector3(x, y, 0);
}

private static float Clamp(float angle, float min, float max) {
    if ((angle <= 180 && angle >= 180 - Mathf.Abs(min)) || (angle >= 180 && angle <= 180 + max)) {
        return Mathf.Clamp(angle, 180 - Mathf.Abs(min), 180 + max);
    }

    if (angle > 180f) {
        angle -= 360f;
    }

    angle = Mathf.Clamp(angle, min, max);

    if (angle < 0f) {
        angle += 360f;
    }

    return angle;
}

EDIT:

Как выясняется, иногда лучше создать собственное детализированное решение, вы можете изменить его проще, поэтому любой желающий может сделать то же, что и я, с приведенным ниже кодом:

void Update() {
    var actualTarget = _enemy.transform.position;
    var targetDir = actualTarget - transform.position;
    var target = Quaternion.LookRotation(targetDir.normalized, transform.up);
    var actual = Quaternion.Inverse(transform.parent.rotation) * target;

    actual.eulerAngles = ClampRotation(actual.eulerAngles);

    var targetRotation = Quaternion.Slerp(transform.localRotation, actual, 8 * Time.deltaTime);

    transform.localRotation = targetRotation;
}

private static Vector3 ClampRotation(Vector3 newRotation) {
    var x = Clamp(newRotation.x, -179, 179);
    var y = Clamp(newRotation.y, -45, 45);

    return new Vector3(x, y, 0);
}

private static float Clamp(float angle, float min, float max) {
    if ((angle <= 180 && angle >= 180 - Mathf.Abs(min)) || (angle >= 180 && angle <= 180 + max)) {
        return Mathf.Clamp(angle, 180 - Mathf.Abs(min), 180 + max);
    }

    if (angle > 180f) {
        angle -= 360f;
    }

    angle = Mathf.Clamp(angle, min, max);

    if (angle < 0f) {
        angle += 360f;
    }

    if (Mathf.Abs(angle) == 360) {
        angle = 0;
    }

    return angle;
}
...