前言

相机控制是游戏开发中至关重要的一部分,直接影响玩家在游戏中的视觉体验。在Unity中,我们可以通过自定义相机控制脚本来实现灵活、流畅的相机操作。本文将深入介绍一个基于Unity的相机控制脚本的设计与实现,包括鼠标旋转、键盘移动、滚轮缩放等功能。


一、概述

首先,让我们来了解一下相机控制脚本的主要目标。我们希望实现以下功能:

使用鼠标右键控制相机的旋转。
使用键盘控制相机的移动。
支持鼠标滚轮进行缩放。
平滑插值过渡相机状态,以确保相机移动的流畅性。

二、代码结构

using UnityEngine;

public class CameraCtrl : MonoBehaviour
{
    private class CameraState
    {
        // 成员变量和方法在后文详细解释
    }

    // 主要成员变量和方法在后文详细解释
}

三、CameraState 类

3.1 相机状态信息

CameraState类负责保存相机的状态信息,包括旋转角度(yaw、pitch、roll)和位置(x、y、z)。

private class CameraState
{
    public float yaw;
    public float pitch;
    public float roll;
    public float x;
    public float y;
    public float z;

}

3.2 SetFromTransform 方法

SetFromTransform方法用于从一个Transform对象中提取旋转角度和位置,并将其应用到当前相机状态。

public void SetFromTransform(Transform t)
{
    pitch = t.eulerAngles.x;
    yaw = t.eulerAngles.y;
    roll = t.eulerAngles.z;
    x = t.position.x;
    y = t.position.y;
    z = t.position.z;
}

3.3 Translate 方法

Translate方法用于根据输入的位移向量进行相机状态的平移。这里使用了旋转矩阵来转换位移向量,以考虑相机的旋转状态。

public void Translate(Vector3 translation)
{
    Vector3 rotatedTranslation = Quaternion.Euler(pitch, yaw, roll) * translation;

    x += rotatedTranslation.x;
    y += rotatedTranslation.y;
    z += rotatedTranslation.z;
}

3.4 LerpTowards 方法

LerpTowards方法实现了相机状态在两个状态之间的插值平滑过渡,包括旋转角度和位置。

public void LerpTowards(CameraState target, float positionLerpPct, float rotationLerpPct)
{
    yaw = Mathf.Lerp(yaw, target.yaw, rotationLerpPct);
    pitch = Mathf.Lerp(pitch, target.pitch, rotationLerpPct);
    roll = Mathf.Lerp(roll, target.roll, rotationLerpPct);

    x = Mathf.Lerp(x, target.x, positionLerpPct);
    y = Mathf.Lerp(y, target.y, positionLerpPct);
    z = Mathf.Lerp(z, target.z, positionLerpPct);
}

3.5 UpdateTransform 方法

UpdateTransform方法用于将相机状态应用到一个Transform对象,更新相机的旋转和位置信息。

public void UpdateTransform(Transform t)
{
    t.eulerAngles = new Vector3(pitch, yaw, roll);
    t.position = new Vector3(x, y, z);
}

3.6 主类 CameraCtrl

在主类CameraCtrl中,我们声明了相机状态的目标和插值状态,以及一些参数如加速度、插值时间等。在OnEnable方法中,我们初始化这两个状态,将其设置为当前相机的初始状态。

private CameraState m_TargetCameraState = new CameraState();
private CameraState m_InterpolatingCameraState = new CameraState();
public float boost = 3.5f;
public float positionLerpTime = 0.2f;
public AnimationCurve mouseSensitivityCurve = new AnimationCurve(new Keyframe(0f, 0.5f, 0f, 5f), new Keyframe(1f, 2.5f, 0f, 0f));
public float rotationLerpTime = 0.01f;
public bool invertY = false;

void OnEnable()
{
    m_TargetCameraState.SetFromTransform(transform);
    m_InterpolatingCameraState.SetFromTransform(transform);
}


3.7 输入处理

在Update方法中,我们处理用户的输入。通过检测鼠标右键的按下和松开,我们可以控制光标的锁定和显示。当右键按下时,光标被锁定,当右键松开时,光标解锁并显示。

if (Input.GetMouseButtonDown(1))
{
    Cursor.lockState = CursorLockMode.Locked;
}

if (Input.GetMouseButtonUp(1))
{
    Cursor.visible = true;
    Cursor.lockState = CursorLockMode.None;
}


3.8 鼠标旋转与键盘移动

当鼠标右键按下时,通过鼠标的移动来控制相机的旋转。使用mouseSensitivityCurve曲线调整鼠标灵敏度,并根据用户设置的invertY来控制是否反转垂直方向。

if (Input.GetMouseButton(1))
{
    var mouseMovement = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y") * (invertY ? 1 : -1));

    var mouseSensitivityFactor = mouseSensitivityCurve.Evaluate(mouseMovement.magnitude);

    m_TargetCameraState.yaw += mouseMovement.x * mouseSensitivityFactor;
    m_TargetCameraState.pitch += mouseMovement.y * mouseSensitivityFactor;
}



键盘输入控制相机的移动,通过GetInputTranslationDirection方法获取输入方向,然后根据输入方向和时间步长计算位移。按住Shift键时,移动速度加倍。

Vector3 translation = GetInputTranslationDirection() * Time.deltaTime;

if (Input.GetKey(KeyCode.LeftShift))
{
    translation *= 10.0f;
}

m_TargetCameraState.Translate(translation);

3.9 缩放和插值平滑过渡

通过鼠标滚轮实现相机的缩放功能。滚轮滚动时,修改boost参数,然后通过插值计算得到新的移动增强因子,从而影响相机的位移。

boost += Input.mouseScrollDelta.y * 0.2f;
translation *= Mathf.Pow(2.0f, boost);


为了使相机运动更加平滑,使用帧率无关的插值方法,将当前相机状态平滑过渡到目标状态。通过计算插值的百分比,可以在指定的时间内到达目标的99%。

var positionLerpPct = 1f - Mathf.Exp((Mathf.Log(1f - 0.99f) / positionLerpTime) * Time.deltaTime);
var rotationLerpPct = 1f - Mathf.Exp((Mathf.Log(1f - 0.99f) / rotationLerpTime) * Time.deltaTime);
m_InterpolatingCameraState.LerpTowards(m_TargetCameraState, positionLerpPct, rotationLerpPct);

m_InterpolatingCameraState.UpdateTransform(transform);


总结

通过这个相机控制脚本的设计与实现,我们实现了一个功能强大而灵活的相机控制器。通过细致的输入处理、插值平滑过渡以及参数调整,我们为游戏开发者提供了一个优雅的相机控制解决方案。希望这篇文章对Unity相机控制的学习和应用有所帮助。在实际项目中,可以根据需求进行修改和扩展,以适应不同类型的游戏场景。

Logo

分享前沿Unity技术干货和开发经验,精彩的Unity活动和社区相关信息

更多推荐