Задача
Ваш скрипт в основном работает!Проблема в
private void Update()
{
if (target != null && timer < 0.99f)
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation((target.position - transform.position).normalized), timer);
timer += Time.deltaTime * RotationSpeed;
}
}
. Есть две проблемы с этим:
Вы добавляете Time.deltaTime * RotationSpeed
, поэтому время, необходимое для достижения 1
иливаш случай 0.99
просто занимает 1/RotationSpeed = 100
раз дольше, чем обычно.Таким образом, ваша камера будет оставаться в состоянии Rotating
около 100 секунд - после этого она будет двигаться очень хорошо!
(Эта может быть преднамеренной, но см. Нижедля лучшего решения ) Quaternion.Slerp
интерполирует между первым и вторым поворотом.Но вы всегда используете текущее вращение в качестве начальной точки, поэтому, поскольку timer
никогда не достигает 1
, вы получаете очень быстрое вращение в начале, но очень медленное (фактически никогда не заканчивающееся) вращение в конце, так как расстояние между текущим вращениеми целевое вращение со временем уменьшается.
Быстрые исправления
Эти исправления исправляют ваше текущее решение, но вы должны проверить раздел Лучшее решение ниже;)
Обычно для сравнения обоих значений float
лучше использовать Mathf.Approximately , а не использовать фактическое целевое значение 1
.
if (target != null && !Mathf.Approximately(timer, 1.0f))
{
//...
timer += Time.deltaTime * RotationSpeed;
// clamps the value between 0 and 1
timer = Mathf.Clamp01(timer);
}
и
public bool IsRotationFinished
{
get { return Mathf.Approximately(timer, 1.0f); }
}
Вы должны использовать Quaternion.Slerp
для сохранения исходного поворота и использовать его в качестве первого параметра (чем вы увидите, что вынужно больше RotationSpeed
)
private Quaternion lastRotation;
private void Update()
{
if (target != null && !Mathf.Approximately(timer, 1.0f))
{
transform.rotation = Quaternion.Slerp(lastRotation, Quaternion.LookRotation((target.position - transform.position).normalized), timer);
timer += Time.deltaTime * RotationSpeed;
}
else
{
lastRotation = transform.rotation;
}
}
Или вместо Quaternion.Slerp
используйте Quaternion.RotateTowards
как
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.LookRotation((target.position - transform.position).normalized), RotationSpeed * Time.deltaTime);
Лучшее решение
Я настоятельно рекомендую использоватьe Сопрограммы для всего, вместо обработки такого рода вещей в Update
.Они намного проще в управлении и делают ваш код очень чистым.
Посмотрите, как уменьшатся ваши сценарии, и вам больше не понадобятся все свойства, поля и сравнение floats
.Вы можете сделать большинство вещей, которые вы получаете в настоящее время, и настроить ожидание того, что определенная вещь произойдет, всего за несколько строк.
В случае, если вы не знали: на самом деле вы можете просто yield return
другой IEnumerator
чтобы дождаться его окончания:
Маршрутные точки
public class Waypoints : MonoBehaviour
{
private GameObject[] waypoints;
public GameObject player;
public float speed = 5;
public float WPradius = 1;
public LookAtCamera lookAtCam;
private Transform currentWaypoint;
private void Start()
{
// maybe refresh here?
//RefreshWaypoints();
StartCoroutine(RunWaypoints());
}
private IEnumerator RunWaypoints()
{
// Sanity check in case the waypoint array has length == 0
if (waypoints.Length == 0)
{
Debug.Log("No Waypoints!", this);
yield break;
}
// this looks dnagerous but as long as you yield somewhere it's fine ;)
while (true)
{
// maybe refresh here?
//RefreshWaypoints();
// Sanity check in case the waypoint array was set to length == 0 between states
if (waypoints.Length == 0)
{
Debug.Log("No Waypoints!", this);
yield break;
}
// first select the next waypoint
// Note that you might get the exact same waypoint again you currently had
// this will throw two errors in Unity:
// - Look rotation viewing vector is zero
// - and transform.position assign attempt for 'Main Camera' is not valid. Input position is { NaN, NaN, NaN }.
//
// so to avoid that rather use this (not optimal) while loop
// ofcourse while is never good but the odds that you will
// always get the same value over a longer time are quite low
//
// in case of doubt you could still add a yield return null
// than your camera just waits some frames longer until it gets a new waypoint
Transform newWaypoint = waypoints[Random.Range(0, waypoints.Length)].transform;
while(newWaypoint == currentWaypoint)
{
newWaypoint = waypoints[Random.Range(0, waypoints.Length)].transform;
}
currentWaypoint = newWaypoint;
// tell camera to rotate and wait until it is finished in one line!
yield return lookAtCam.RotateToTarget(currentWaypoint);
// move and wait until in correct position in one line!
yield return MoveToTarget(currentWaypoint);
//once waypoint reached wait 3 seconds than start over
yield return new WaitForSeconds(3);
}
}
private IEnumerator MoveToTarget(Transform currentWaypoint)
{
var currentPosition = transform.position;
var duration = Vector3.Distance(currentWaypoint.position, transform.position) / speed;
var passedTime = 0.0f;
do
{
// for easing see last section below
var lerpFactor = passedTime / duration;
transform.position = Vector3.Lerp(currentPosition, currentWaypoint.position, lerpFactor);
passedTime += Time.deltaTime;
yield return null;
} while (passedTime <= duration);
// to be sure to have the exact position in the end set it fixed
transform.position = currentWaypoint.position;
}
public void RefreshWaypoints()
{
waypoints = GameObject.FindGameObjectsWithTag("Target");
}
}
LookAtCamera
public class LookAtCamera : MonoBehaviour
{
// Values that will be set in the Inspector
public float RotationSpeed;
public IEnumerator RotateToTarget(Transform target)
{
var timePassed = 0f;
var targetDirection = (target.position - transform.position).normalized;
var targetRotation = Quaternion.LookRotation(targetDirection);
var currentRotation = transform.rotation;
var duration = Vector3.Angle(targetDirection, transform.forward) / RotationSpeed;
do
{
// for easing see last section below
var lerpFactor = timePassed / duration;
transform.rotation = Quaternion.Slerp(currentRotation, targetRotation, lerpFactor);
timePassed += Time.deltaTime;
yield return null;
} while (timePassed <= duration);
// to be sure you have the corrcet rotation in the end set it fixed
transform.rotation = targetRotation;
}
}

Примечание
Снова вместо Quaternion.Slerp
и currentRotation
вы также можете просто использовать Quaternion.RotateTowards
как
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, RotationSpeed * Time.deltaTime);
И для движения вы также можете по-прежнемуиспользуйте Vector3.MoveTowards
, если хотите
while (Vector3.Distance(currentWaypoint.position, transform.position) < WPradius)
{
transform.position = Vector3.MoveTowards(transform.position, currentWaypoint.position, Time.deltaTime * speed);
yield return null;
}
, но Я бы предпочел использовать решения Lerp
.Почему я предпочитаю использовать Lerp
?
Теперь вы можете очень легко контролировать, хотите ли вы двигаться / вращаться с определенной скоростью или, вернее, установите фиксированное значение duration
, при которомперемещение / вращение должны быть закончены независимо от того, насколько велика разница - или даже иметь некоторые дополнительные проверки, чтобы выбрать один из этих вариантов!
Вы можете облегчить и отменитьдвижение / вращение!См. Ниже;)
Подсказка для облегчения движений Лерпа
Для того, чтобы все еще поддерживать ослабленное движение и / или ослабленное движение и вращение, я нашел этот блок Как Лерп как профессионал очень полезно!(адаптировано к моим примерам)
Например, мы могли бы "ослабить" с помощью sinerp:
var lerpFactor = Mathf.Sin(passedTime / duration * Mathf.PI * 0.5f);
Или мы могли бы "облегчить" с помощью coserp:
var lerpFactor = 1f - Mathf.Cos(passedTime / duration * Mathf.PI * 0.5f);
Мы могли бы даже создать экспоненциальное движение:
var lerpFactor = Mathf.Pow(passedTime / duration, 2);
Упомянутое выше свойство умножения является основной концепцией некоторых методов интерполяции, которые упрощаются и упрощаются, например, известной формулой «smoothstep»:
var lerpFactor = Mathf.Pow(passedTime / duration, 2) * (3f - 2f * passedTime / duration);
Или мой личный фаворит, "Smootherstep":
var lerpFactor = Mathf.Pow(passedTime / duration, 3) * (6f * (passedTime / duration) - 15f) + 10f);