Хорошо, я собираюсь сделать три вещи здесь. Прежде всего, я порекомендую вам, если вы работаете с расширенным поведением камеры, вы, вероятно, захотите хотя бы рассмотреть возможность использования Cinemachine . Я бы сам провел вас через это, но, учитывая отсутствие у меня личного опыта, я, вероятно, даже оказал бы вам медвежью услугу. Есть много хороших уроков. Youtube и Google должны предоставить.
Второе, что я сделаю, это решу вашу проблему самым непосредственным образом, каким я смогу управлять, и после этого мы посмотрим, не сможем ли мы придумать лучший метод для решения вашей проблемы.
Итак, ключевой момент здесь заключается в том, что входное колесо прокрутки Unity является довольно двоичным. Когда вы проверяете ось колеса прокрутки, результат напрямую зависит от того, сколько «щелчков» прошло вашим колесом с момента последнего обновления кадра, но то, что вы действительно хотите, - это что-то с небольшим преимуществом. По умолчанию Unity может делать это с большинством входов оси: вы можете заметить, что если вы используете WASD в проекте Unity по умолчанию, это своего рода «отставание», когда вы убираете пальцы с клавиш, но вы все равно будете получать положительные значения от Input.GetAxis()
в течение нескольких кадров. Это связано со значением Gravity
в ваших настройках ввода, и Input.GetAxisRaw()
фактически используется, чтобы полностью обойти это. По какой-то причине оси гравитации не зависят от силы тяжести оси, поэтому нам, по сути, приходится реализовывать нечто подобное самим.
// Add this property to your class definition (so it persists between updates):
private float wheelAxis = 0;
// Delete this line:
var zoomDelta = Input.GetAxis("Mouse ScrollWheel") * zoomSpeed * Time.deltaTime;
// And put these three new lines in its place:
wheelAxis += Input.GetAxis("Mouse ScrollWheel");
wheelAxis = Mathf.MoveTowards(wheelTotal, 0f, Time.deltaTime);
var zoomDelta = Mathf.Clamp(wheelAxis, -0.05f, 0.05f) * zoomSpeed * Time.deltaTime;
Правильно, поэтому мы делаем несколько вещей здесь. При каждом обновлении мы добавляем текущие значения колесика прокрутки к нашему wheelAxis
. Затем мы применяем ток Time.deltatime
как «гравитацию» через функцию Mathf.MoveTowards()
. Наконец, мы называем то, что в основном является вашим старым zoomDelta
кодом, с простой модификацией: мы ограничиваем wheelAxis
с помощью Mathf.Clamp
, чтобы попытаться отрегулировать, как быстро может происходить увеличение.
Вы можете изменить этот код несколькими способами. Если вы умножите параметр Time.deltaTime
, вы можете указать, как долго ваш ввод будет «сохраняться». Если вы возитесь со значениями Mathf.Clamp()
, вы будете увеличивать или уменьшать масштаб. В общем, если вам нужно плавное масштабирование с минимальными изменениями в коде, это, вероятно, ваш лучший выбор.
Так!
Теперь, когда мы это сделали, давайте поговорим о вашем коде и о том, как вы подходите к проблеме, и посмотрим, не найдем ли мы более чистого решения.
Получение хорошей работы камеры на удивление нетривиально. Ваш код выглядит как большой код, который, как я вижу, пытается решить сложную проблему: похоже, вы добавили какую-то функцию, а затем протестировали ее, нашли некоторые крайние случаи, когда она развалилась, а затем исправили эти случаи, а затем попытались реализовать новую функцию поверх старого кода, но она вроде как сломалась различными другими способами, и т. д., и т. д., и то, что мы получили в этот момент, немного грязно.
Самая большая проблема с вашим кодом заключается в том, что положение камеры и вращение камеры тесно связаны между собой. При работе с персонажами это обычно нормально, но при работе с камерой вы хотите разбить это на части. Подумайте, где камера и что камера смотрит на как на отдельные вещи, которые нужно отслеживать.
Итак, вот рабочий скрипт камеры, который вы можете подключить и просто запустить:
using UnityEngine;
public class RTSCamera : MonoBehaviour
{
public float zoomSpeed = 100f;
public float zoomTime = 0.1f;
public float maxHeight = 100f;
public float minHeight = 20f;
public float focusHeight = 10f;
public float focusDistance = 20f;
public int panBorder = 25;
public float dragPanSpeed = 25f;
public float edgePanSpeed = 25f;
public float keyPanSpeed = 25f;
private float zoomVelocity = 0f;
private float targetHeight;
void Start()
{
// Start zoomed out
targetHeight = maxHeight;
}
void Update()
{
var newPosition = transform.position;
// First, calculate the height we want the camera to be at
targetHeight += Input.GetAxis("Mouse ScrollWheel") * zoomSpeed * -1f;
targetHeight = Mathf.Clamp(targetHeight, minHeight, maxHeight);
// Then, interpolate smoothly towards that height
newPosition.y = Mathf.SmoothDamp(transform.position.y, targetHeight, ref zoomVelocity, zoomTime);
// Always pan the camera using the keys
var pan = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")) * keyPanSpeed * Time.deltaTime;
// Optionally pan the camera by either dragging with middle mouse or when the cursor touches the screen border
if (Input.GetMouseButton(2)) {
pan -= new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")) * dragPanSpeed * Time.deltaTime;
} else {
var border = Vector2.zero;
if (Input.mousePosition.x < panBorder) border.x -= 1f;
if (Input.mousePosition.x >= Screen.width - panBorder) border.x += 1f;
if (Input.mousePosition.y < panBorder) border.y -= 1f;
if (Input.mousePosition.y > Screen.height - panBorder) border.y += 1f;
pan += border * edgePanSpeed * Time.deltaTime;
}
newPosition.x += pan.x;
newPosition.z += pan.y;
var focusPosition = new Vector3(newPosition.x, focusHeight, newPosition.z + focusDistance);
transform.position = newPosition;
transform.LookAt(focusPosition);
}
}
Хотя я призываю вас пройти через это в свое время, я не собираюсь тащить вас через каждый дюйм этого. Вместо этого я просто перейду к основному смыслу.
Ключевая идея здесь заключается в том, что вместо непосредственного управления высотой и ориентацией камеры мы просто позволяем колесу прокрутки определять, где хочет высота камеры, а затем мы используем Mathf.SmoothDamp()
для перемещения камеры. плавно в эту позицию в течение нескольких кадров. (У Unity есть много полезных функций, подобных этой. Рассмотрим Mathf.MoveTowards()
для альтернативного метода интерполяции.) В самом конце, вместо того, чтобы пытаться напрямую поиграть со значениями вращения камеры, мы просто выбираем точку перед нами возле земли и направьте камеру прямо на это место.
Сохраняя положение камеры и ее ориентацию полностью независимыми друг от друга, а также выделяя «анимацию» высоты камеры, мы избегаем многих головных болей и устраняем большой потенциал для беспорядочных переплетенных ошибок.
Надеюсь, это поможет.