Единство - глядя через прицел

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

Вот визуал для лучшего понимания:

Теперь, если бы я просто включил вторую камеру и выключил основную камеру, это работало бы замечательно, но это не очень идеально. Вы должны иметь только 1 камеру на сцену.

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

[RequireComponent(typeof(Camera))]
public class Zoom : MonoBehaviour {

    private Transform CameraTransform = null;
    public Transform ZoomedTransform;

    private bool zoomed = false;

    void Start () {
        CameraTransform = Camera.main.transform;
    }

    // Update is called once per frame
    void Update () {
        if (Input.GetKey (KeyCode.LeftShift)) 
        {
            CameraTransform.position = Vector3.Lerp (
                CameraTransform.position, 
                CameraTransform.position + ZoomedTransform.position, 
                5f * Time.deltaTime
            );
            CameraTransform.Rotate(ZoomedTransform.rotation.eulerAngles);
        }
    }
}

Проблема в том, что это не работает: когда я нажимаю кнопку зума, камера перемещается по сцене со скоростью света, и трудно точно сказать, что происходит.

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

Иерархия:

3 ответа

Решение

(Этот ответ работает при условии, что ZoomedTransform является относительным преобразованием, а не абсолютным положением камеры, как подозревает ответ 31eee384.)

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

CameraTransform.position = Vector3.Lerp (CameraTransform.position, CameraTransform.position + ZoomedTransform.position, 5f * Time.deltaTime);

Во-первых, давайте посмотрим, как вы используете Vector3.Lerp(), Для третьего аргумента Vector3.Lerp()поставляешь 5f * Time.deltaTime, Что именно это значение работает? Ну, стандартная частота кадров составляет около 60 кадров в секунду, так Time.deltaTime = ~1/60. Следовательно, 5f * Time.deltaTime = 5/60 = ~0,0833.

Что такое Vector3.Lerp() ожидая третьего аргумента? Согласно документации, этот третий аргумент должен быть между 0 и 1 и определяет, будет ли возвращен Vector3 должно быть ближе к первому или второму Vector3, Так да, 5f * Time.deltaTime попадает в этот диапазон, но интерполяция не произойдет - потому что она всегда будет около ~0,0833, а не прогрессирует от 0 до 1 (или от 1 до 0). Каждый кадр, ты в основном всегда возвращаешься cameraPos + zoomTransform * 0.0833,

Другая заметная проблема заключается в том, как вы обновляете значение CameraTransform.position каждый кадр, но затем использовать это новое (увеличенное) значение в качестве аргумента для Vector3.Lerp() следующий кадр. (Это немного похоже на int i = i + 1; в цикле.) Это причина, почему ваша камера летит по карте так быстро. Вот что происходит в каждом кадре, используя гипотетический результат вашего Vector3.Lerp() что я рассчитал ранее (псевдокод):

// Frame 1
cameraPosFrame_1 = cameraPosFrame_0 + zoomTransform * 0.0833;
// Frame 2
cameraPosFrame_2 = cameraPosFrame_1 + zoomTransform * 0.0833;
// Frame 3
cameraPosFrame_3 = cameraPosFrame_2 + zoomTransform * 0.0833;
// etc...

Каждый кадр, zoomTransform * 0.0833 добавляется к позиции камеры. Что в итоге приводит к очень, очень быстрому и безостановочному увеличению стоимости - так ваша камера летит по карте.

Один из способов решения этих проблем состоит в том, чтобы иметь переменные, в которых хранится исходное локальное положение вашей камеры, прогресс увеличения и скорость увеличения. Таким образом, мы никогда не теряем исходное положение камеры, и мы можем отслеживать, как далеко продвинулся зум, и когда остановить его.

[RequireComponent(typeof(Camera))]
public class Zoom : MonoBehaviour {

    private Transform CameraTransform = null;
    public Transform ZoomedTransform;
    private Vector3 startLocalPos;
    private float zoomProgress = 0;
    private float zoomLength = 2; // Number of seconds zoom will take

    private bool zoomed = false;

    void Start () {
        CameraTransform = Camera.main.transform;
        startLocalPos = CameraTransform.localPosition;
    }

    // Update is called once per frame
    void Update () {
        if (Input.GetKey (KeyCode.LeftShift)) 
        {
            zoomProgress += Time.deltaTime;

            CameraTransform.localPosition = Vector3.Lerp (startLocalPos, startLocalPos + ZoomedTransform.position, zoomProgress / zoomLength);
            CameraTransform.Rotate(ZoomedTransform.rotation.eulerAngles);
        }
    }
}

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

Ваша цель lerp относительно текущей позиции камеры, поэтому она постоянно движется. Это ваша цель:

CameraTransform.position + ZoomedTransform.position

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

Ваш пункт назначения должен быть ZoomedTransform.position, Нет необходимости в добавлении, потому что position находится в мировых координатах. (И когда вам действительно нужно конвертировать между пробелами, проверьте TransformPoint и аналогичные методы.)

Прошло много времени с тех пор, как я что-то делал в Unity, но я думаю, что он обрабатывает функцию Lerp во время кадра, а не в реальном времени. Вам нужно будет вызвать его в другой функции, которая не обрабатывается во время кадра.

Другие вопросы по тегам