Unity学习记录-摄像机控制

摄像机控制:

  1. WASD或方向键控制
  2. 鼠标屏幕边缘滚动控制(如英雄联盟)
  3. 鼠标右键拖拽控制移动
  4. 鼠标中键控制摄像机焦距

关于移动转向的问题

  • 如果单纯地按键盘输入来控制移动的话,会出现摄像机转向后移动混乱的情况(转向后摄像机自身的坐标系也跟着旋转)
  • 使用三元数InputDir接收键盘输入,再用三元数MoveDir计算摄像机的移动

WASD控制摄像机移动

  1. 创建一个空物体,摆正其坐标系(方便接收键盘输入)
  2. 创建cinemachineVirtualCamera,使其看向空物体
  3. 为空物体编写脚本,键盘控制空物体移动,相当于摄像机移动
1
2
3
private Vector3 inputDir=new Vector3(0,0,0);//键盘输入方向,默认是0
private Vector3 moveDir;//摄像机移动方向
[SerializeField] private float moveSpeed = 50f;//移动速度

inputDir记录键盘输入,moveDir计算实际移动

1
2
3
4
5
6
7
8
9
10
11
12
void KeybroadControl()
{
//键盘控制摄像机移动
inputDir.Set(0, 0, 0);//每次调用记得清空,否则会累加

if (Input.GetKey(KeyCode.W)) inputDir.z += 1f;
if (Input.GetKey(KeyCode.A)) inputDir.x += -1f;
if (Input.GetKey(KeyCode.S)) inputDir.z += -1f;
if (Input.GetKey(KeyCode.D)) inputDir.x += 1f;
//前后左右,对应Z轴和X轴

}

每帧调用该函数,获取键盘输入

1
2
3
4
moveDir = this.transform.forward * inputDir.z + this.transform.right * inputDir.x;
//计算moveDir
this.transform.position += moveDir * moveSpeed * Time.deltaTime;
//应用移动

在Update中应用移动:moveDir = 当前朝向 X 前后移动量+当前的右方 X 左右的移动量,并应用到position中

QE控制摄像机转向

1
2
private float rotateDir;//摄像机旋转角度
[SerializeField] private float rotateSpeed = 50f;//旋转速度(角度)
1
2
3
4
5
6
7
8
void KeybroadControl()
{
//键盘记录
rotateDir = 0f;//每次记录前记得清空

if (Input.GetKey(KeyCode.Q)) rotateDir += -1f;
if (Input.GetKey(KeyCode.E)) rotateDir += +1f;
}
1
this.transform.eulerAngles += new Vector3(0, rotateDir * rotateSpeed * Time.deltaTime, 0);

在Update中应用移动,沿Y轴转动

鼠标屏幕边缘控制摄像机移动(如英雄联盟)

  • 和键盘控制思路类似,只不过inputDir改为接收鼠标位置
1
2
[SerializeField] private int edgeScrollSize = 20;//屏幕边缘判定长度(像素)
[SerializeField] private float moveSpeed = 50f;//移动速度(鼠标操控)
1
2
3
4
5
6
7
8
9
void MouseControlScroll()
{
//鼠标在边界控制摄像头的移动
//屏幕边缘滚动
if (Input.mousePosition.x < edgeScrollSize) inputDir.x = -1f;
if (Input.mousePosition.x > Screen.width - edgeScrollSize) inputDir.x = 1f;
if (Input.mousePosition.y < edgeScrollSize) inputDir.z = -1f;
if (Input.mousePosition.y > Screen.height - edgeScrollSize) inputDir.z = 1f;
}

每帧调用该函数

1
2
3
4
moveDir = this.transform.forward * inputDir.z + this.transform.right * inputDir.x;
//计算moveDir
this.transform.position += moveDir * moveSpeed * Time.deltaTime;
//应用移动

同样地在Update中应用移动

鼠标右键拖拽控制摄像机移动

1
2
private Vector3 mouseLastPosition;//鼠标上一次的的位置
private bool isDrag = false;//判断鼠标是否在拖拽状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void MouseControlDrag()
{
//鼠标拖拽控制摄像头移动
//如果鼠标右键按下,则说明处于拖拽状态
if (Input.GetMouseButtonDown(1))
{
isDrag = true;
mouseLastPosition = Input.mousePosition;
//记录此时的鼠标位置
}
//如果鼠标右键弹起,则说明离开拖拽状态
if (Input.GetMouseButtonUp(1))
{
isDrag = false;
}
//在拖拽状态下
if (isDrag)
{
inputDir.x = (mouseLastPosition.x - Input.mousePosition.x) * dragSpeed * Time.deltaTime;
//x=(鼠标之前的X位置-鼠标此时的X位置)*拖拽速度*Time.deltaTime;
inputDir.z = (mouseLastPosition.y - Input.mousePosition.y) * dragSpeed * Time.deltaTime;
//z=(鼠标之前的z位置-鼠标此时的z位置)*拖拽速度*Time.deltaTime;
mouseLastPosition = Input.mousePosition;
//更新记录的鼠标位置,否则会越拖越快
//用户习惯 拖动场景 而不是 拖动摄像机,所以要之前-此时
}
}

每帧调用该函数,记录鼠标拖拽

1
2
3
4
moveDir = this.transform.forward * inputDir.z + this.transform.right * inputDir.x;
//计算moveDir
this.transform.position += moveDir * moveSpeed * Time.deltaTime;
//应用移动

同样地在Update中应用移动

鼠标滚轮控制摄像机焦距

1
2
[SerializeField] private float ScrollSpeed = 5f;//缩放速度
[SerializeField] private CinemachineVirtualCamera cinemachineVirtualCamera;//相机组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void CameraZoomControl()
{
if(Input.mouseScrollDelta.y>0)
{
cinemachineVirtualCamera.m_Lens.FieldOfView -= ScrollSpeed;
}
if(Input.mouseScrollDelta.y<0)
{
cinemachineVirtualCamera.m_Lens.FieldOfView += ScrollSpeed;
}
if(Input.GetMouseButtonDown(2))
{
cinemachineVirtualCamera.m_Lens.FieldOfView = 60;
//单击滚轮恢复
}
}