Важные поля / входы
Во-первых, нам нужно знать направления границы в мировом пространстве:
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);