Стрелка, вращающаяся к лицу курсора, должна делать это только в пределах угла, заданного двумя указанными направлениями. - PullRequest
4 голосов
/ 01 июля 2019

У меня есть 2d стрелка, вращающаяся так, что она всегда направлена ​​против цели (цель в данном случае - курсор), стержень - мой персонаж игрока. Мне нужно ограничить эту стрелку, чтобы она следовала только за целью, если она находится под углом игрока, например, 90 градусов, поэтому она будет следовать, только если курсор находится в верхней правой части экрана.

Я работал с векторными направлениями и методами, такими как Vector2D.angle, но все они, кажется, имеют некоторые ограничения, которые я не могу обойти, ограничение Vector2D.angles состоит в том, что третья позиция, которую он использует для вычисления угла, является мировым центром. (0, 0), мой плеер мобильный, так что не работает. Поэтому я думаю, что я спрашиваю, есть ли способ сохранить угол, а затем проверить, что-то в этом есть.

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

    public float speed;
    public Transform target;

 void Update()
    {
        Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 direction = target.position - transform.position;
            target.position = mousePosition;
        float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
Quaternion rotation = Quaternion.AngleAxis(angle, Vector3.forward);
        transform.rotation = Quaternion.Slerp(transform.rotation, rotation, speed * Time.deltaTime);

Извините, если это плохо отформатировано, это мой первый пост здесь, спасибо миллион раз, если вы можете мне помочь, я застрял на этом в течение нескольких дней.

Попросили уточнить вопрос, так что попытка:

Example

На этом рисунке показан пример того, что я имею в виду, когда стрелка вращается вокруг центра круга (она вращается, поэтому всегда указывает на мой курсор). Что мне нужно, так это способ ограничить его, чтобы он указывал только на курсор, если он находится в пределах определенного угла (красные линии на рисунке), вот с чем у меня проблемы. Я не могу найти способ сохранить этот порог и не могу найти способ сравнить направление курсоров с ним.

Я думаю, что было бы возможно, если бы можно было выбрать собственный центр для Vector2D.angle, но, похоже, это не так.

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

1 Ответ

2 голосов
/ 02 июля 2019

Важные поля / входы

Во-первых, нам нужно знать направления границы в мировом пространстве:

public Vector2 boundaryDirectionLeft;
public Vector2 boundaryDirectionRight;

Важным моментом является то, что угол по часовой стрелке составляет от boundaryDirectionLeft до boundaryDirectionRight - это область, внутри которой должна оставаться стрелка.В случае вашего изображения boundaryDirectionLeft может быть Vector2.up, а boundaryDirectionRight может быть что-то вроде new Vector2(1f,-1f).

Нам также нужно знать, в каком локальном направлении направлена ​​стрелка, прежде чем будет применено любое вращение.,Например, если стрелка всегда указывает на локальную ось красной стрелки (локальное направление вправо), это будет Vector2.right:

public Vector2 localArrowDirection;

И, наконец, нам нужна максимальная скорость для нашего вращения, поэтомумы не деформируем стрелу, когда пришло время вращаться.Хорошее значение для начала может быть 360f, 360 градусов в секунду.Попробуйте поэкспериментировать с различными значениями, чтобы найти то, что вам нравится:

public float maxRotationSpeed;

Процедура обновления

В Update, определите направление к цели:

Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 direction = mousePosition - transform.position;

Затем нам нужно знать, куда в данный момент указывает стрелка.Мы можем использовать Transform.TransformVector, чтобы найти, куда локальный localArrowDirection указывает в мировом пространстве:

Vector2 currentDirection = transform.TransformVector(localArrowDirection);

Затем определить угол со знаком из boundaryDirectionLeft к целевому направлению, то же самое от boundaryDirectionLeft до boundaryDirectionRight и то же самое от boundaryDirectionLeft к текущему направлению лицом:

float directionAngle = Vector2.SignedAngle(boundaryDirectionLeft, direction);
float boundaryAngle = Vector2.SignedAngle(boundaryDirectionLeft, boundaryDirectionRight);
float currentAngle = Vector2.SignedAngle(boundaryDirectionLeft, currentDirection);

Эти значения варьируются от [-180,180], но мымы хотим, чтобы они выражались в диапазоне [0,360), чтобы потом было легче вычислять, поэтому мы можем добавить 360f и использовать Mathf.Repeat на эту сумму:

directionAngle = Mathf.Repeat(directionAngle+360f, 360f);
boundaryAngle = Mathf.Repeat(boundaryAngle+360f, 360f);
currentAngle = Mathf.Repeat(currentAngle+360f, 360f);

На этом этапеdirectionAngle - это количество градусов по часовой стрелке от boundaryDirectionLeft, равное target, boundaryAngle - это число boundaryDirectionRight, и currentAngle - то же, что и в каком направлении мы сейчас находимся.

Итак, теперь нам нужно знать, как правильно зажать угол между 0 и boundaryAngle.Все, что находится слишком высоко над boundaryAngle, на самом деле ближе к левой границе и должно быть прикреплено к левой границе.Фактически, поскольку все находится между 0 и 360, все, что выше boundaryAngle+(360f-boundaryAngle)/2f, находится ближе к левому краю.Итак, мы просто установили что-либо выше, чем это на 0 градусов от boundaryDirectionLeft:

if (directionAngle > boundaryAngle + (360f - boundaryAngle)/2f)
{
    directionAngle = 0f;
}

Итак, теперь мы можем зажать directionAngle с максимумом boundaryAngle (оно уже зафиксировано снизув 0f, поэтому мы можем использовать Mathf.Min здесь):

directionAngle = Mathf.Min(directionAngle, boundaryAngle);

Теперь мы можем ограничить угловую разницу между directionAngle и currentAngle, используя наши maxRotationSpeed:

float deltaAngle = Mathf.Clamp(directionAngle-currentAngle,
                               -maxRotationSpeed * Time.deltaTime, 
                               maxRotationSpeed * Time.deltaTime);

Теперь мы можем повернуть преобразование deltaAngle градусов по часовой стрелке (в мировом пространстве):

transform.Rotate(0f,0f,deltaAngle,Space.World);
...