Unity 第一人称视角实现
项目目标:
完成第一人称视角的相机
项目要求:
1.可以通过键盘对操作物品进行移动操作
2.可以通过鼠标来对屏幕进行移动,从而达到旋转视角的效果
3.鼠标可以被隐藏并且在移动时始终保持在界面中心
项目分析:
在对(1)进行完成时,可以使用虚拟轴来实现,也可以通过直接检测键盘的输入来进行移动,然后在通过相关的transform组件来完成对物体位置的移动。
在使用虚拟轴时,在输入键盘时,其并不是从0立刻变成1或者-1,它是慢慢的变成1和-1的(大约0.3s),因此我们获取到的是一个变化,每当我们按下键盘一次它就会变化一下,所以当我们需要移动时,我们需要去将这些值累加起来才能对position进行计算,及我们需要对position进行一个累加,如果只是对其单纯的赋值,那么物体就只能在原地移动后又再次回到原位,并且在移动时也会有一个范围的最大值。
在我们进行移动是,是使用Update()来进行的,及我们是每一帧移动一次,当帧数不同时,我们的移动速度也会不一样,这是我们不希望看见的,使用我们可以在对移动量进行计算时,在加以乘入Time.deltaTime,这样我们就可以以时间来进行移动,当帧数低时,Time.deltaTime也会变大,因此在乘上Time.deltaTime后,输入帧数会跳动变化,但是移动速度会保持基本不变
例如
//虚拟轴输入
float hor = Input.GetAxis("Horizontal");
float ver = Input.GetAxis("Vertical");
//检测输入
if(Input.GetKey(KeyCode.S)){
transform.position += View.transform.forward * -1 * Time.deltaTime * Moverate;
}
当对(2)进行完成时,也可以有两种思想,但大体的不同在对旋转摄像机进行完成时有差异,一是可以通过欧拉角来进行完成旋转,二是可以通过四元数来进行旋转。我们这边使用四元数进行对摄像机的操作,虽然四元数比欧拉角更加难以理解,但是,四元数不会像欧拉角一样的万向锁的问题(在静态欧拉角时不会出现这种问题)
- 静态:即绕世界坐标系三个轴的旋转,由于物体旋转过程中坐标轴保持静止,所以称为静态。
- 动态:即绕物体坐标系三个轴的旋转,由于物体旋转过程中坐标轴随着物体做相同的转动,所以称为动态。
Quaternion Yaw = Quaternion.AngleAxis(RotationTo.x,upAxis);
Quaternion.AngleAxis用法 public static Quaternion AngleAxis(float angle, Vector3 axis);
既绕着以Vector3 axis旋转float angle为角度旋转
那么如何拿到需要旋转的角度呢
我们可以使用一个二维数来进行对鼠标移动的存储,在我们使用虚拟轴时,当我们移动鼠标时,都会记下鼠标的x,y的变化,我们可以通过**Input.GetAxis(“Mouse X”)来获取到鼠标的x的变化,再将其记录到变量例如rh中,但因为Input.GetAxis(“Mouse X”)**得到的是变化的指,所以我们还需要有一个二维数来存储鼠标的x,y的变化
rh = Input.GetAxis("Mouse X")*Time.deltaTime*RotationRate;
rv = Input.GetAxis("Mouse Y") * Time.deltaTime * RotationRate ;
//当鼠标不移动时,直接结束,使电脑的负担减少
if (rv == 0 && rh == 0) return;
RotationTo.x += rh;
RotationTo.y += rv
之后我们就可以通过角度和轴来进行对摄像机的旋转了。当鼠标左右移动时,x发生变化,在游戏中摄像机左右旋转,所以应该绕世界的y轴旋转,及绕重力轴旋转
Quaternion Yaw = Quaternion.AngleAxis(RotationTo.x,upAxis);
在Unity 中,时常会有在游戏运行时,出现鼠标移动相反的情况,这时候我们会需要一个Y轴翻转来抵消这个情况带来的影响。
public bool pitch;
Quaternion Pitch =
Quaternion.AngleAxis(RotationTo.y * (pitch ? 1 : -1), pitcAxis);
这时我们就可以在Unity面板上选择是否要开启Y轴翻转
在之后是对(3)进行完成,在Unity中已经为我们准备好了这一功能,我们只需要调用即可
public void MouseHide()
{
//当按下左Alt时,鼠标就会出现
if (!Input.GetKey(KeyCode.LeftAlt))
{
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
}
else
{
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
}
}
项目代码:
对此因为对物体的输入有两种方式,当然在使用非虚拟轴来进行输入键盘时,就会出现无法改建的情况,只有在代码中修改相应的键盘输入,才可以修改相应的键位,但是当使用虚拟轴时,可以在Unity中的预算修改这些键位。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FirstView : MonoBehaviour
{
public float Moverate = 0.1f;
public float RotationRate = 0.1f;
public GameObject View;
float rh;
float rv;
Vector3 dir;
Vector3 upAxis;
Vector3 pitcAxis;
[Header("pitch反转")]
public bool pitch;
Vector2 RotationTo;
// Start is called before the first frame update
void Start()
{
//对两个选择轴进行初始化
upAxis = -Physics.gravity.normalized;
pitcAxis = Vector3.Cross(upAxis,Vector3.ProjectOnPlane(transform.forward,upAxis));
}
// Update is called once per frame
void Update()
{
MouseHide();
//实现移动
float hor = Input.GetAxis("Horizontal");
float ver = Input.GetAxis("Vertical");
//对position进行更新,使其移动
transform.position +=
View.transform.right * Time.deltaTime * Moverate * hor + View.transform.forward * Time.deltaTime * Moverate * ver;
//上升
float jump = Input.GetAxis("Jump");
transform.position +=
View.transform.up * Time.deltaTime * Moverate * jump;
//获得鼠标的位置变化
rh = Input.GetAxis("Mouse X")*Time.deltaTime*RotationRate;
rv = Input.GetAxis("Mouse Y") * Time.deltaTime * RotationRate ;
//在不使用鼠标时节省性能
//if (rv == 0 && rh == 0) return;
//对选择的角度进行累加
RotationTo.x += rh;
RotationTo.y += rv;
Quaternion Yaw = Quaternion.AngleAxis(RotationTo.x,upAxis);
Quaternion Pitch =
Quaternion.AngleAxis(RotationTo.y * (pitch ? 1 : -1), pitcAxis);
//计算方向
dir = Yaw * Pitch * Vector3.forward;
Quaternion RotationAngle = Quaternion.LookRotation(dir);
//对相机进行选择
View.transform.rotation = RotationAngle;
//下面是使用非虚拟轴的完成代码
/*
if (Input.GetKey(KeyCode.W)&&Input.GetKey(KeyCode.D))
{
//transform.position += new Vector3(1 * Moverate*Time.deltaTime, 0, -1 * Moverate * Time.deltaTime);
transform.position += (View.transform.forward*Time.deltaTime * Moverate) +(View.transform.right * Time.deltaTime * Moverate);
}
else if (Input.GetKey(KeyCode.W)&&Input.GetKey(KeyCode.A))
{
//transform.position += new Vector3(-1 * Moverate * Time.deltaTime, 0, -1 * Moverate * Time.deltaTime);
transform.position += (View.transform.forward * 1 * Time.deltaTime * Moverate) + (View.transform.right * -1*Time.deltaTime * Moverate);
}
else if (Input.GetKey(KeyCode.S)&&Input.GetKey(KeyCode.D))
{
//transform.position += new Vector3(1 * Moverate * Time.deltaTime, 0, -1 * Moverate * Time.deltaTime);
transform.position += (View.transform.forward * -1 * Time.deltaTime * Moverate) + (View.transform.right * 1 * Time.deltaTime * Moverate);
}
else if (Input.GetKey(KeyCode.S)&&Input.GetKey(KeyCode.A))
{
//transform.position += new Vector3(-1 * Moverate * Time.deltaTime, 0, -1 * Moverate * Time.deltaTime);
transform.position += (View.transform.forward * -1 * Time.deltaTime * Moverate) + (View.transform.right * -1 * Time.deltaTime * Moverate);
}
else if (Input.GetKey(KeyCode.W))
{
//transform.position += new Vector3(0, 0, 1 * Moverate * Time.deltaTime);
transform.position += View.transform.forward*Time.deltaTime * Moverate;
}else if (Input.GetKey(KeyCode.A))
{
//transform.position += new Vector3(-1* Moverate * Time.deltaTime, 0, 0);
transform.position += View.transform.right*-1 * Time.deltaTime * Moverate;
}
else if (Input.GetKey(KeyCode.D))
{
//transform.position += new Vector3(1 * Moverate * Time.deltaTime, 0, 0);
transform.position += View.transform.right * Time.deltaTime * Moverate;
}
else if (Input.GetKey(KeyCode.S))
{
//transform.position += new Vector3(0, 0, -1 * Moverate * Time.deltaTime);
transform.position += View.transform.forward * -1 * Time.deltaTime * Moverate;
}
*/
}
public void MouseHide()
{
if (!Input.GetKey(KeyCode.LeftAlt))
{
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
}
else
{
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
}
}
}
项目小结:
通过代码我们可以看到,使用虚拟轴比不使用虚拟轴的代码要精简许多。
在实现非虚拟轴的移动时,我们需要去输入所有需要的可能,并且在需要同时输入A和S或者其他情况时,需要将其放在 if 语句的较为前端,不然就会直接执行单个字母的输入,即便你输入了两个键值。
使用虚拟轴时,要对position和旋转角度进行累加
当前代码依然会有因为角度旋转超过了360度而出现的Bug,因此我们可以将代码中的角度进行修改从0度到360度改至从 -180度到180度,这样可以避免Bug的产生。
|