unity Camera控制(上帝视角)
解决问题
- 解决移动速度不变带来的操作迟缓;
- 实现在摄像机不同角度下,始终针对地面的水平移动(而非视口的上下左右移动);
- 实现绕点旋转。
正文
在针对大场景(如智慧城市),需要一种行之有效的Camera控制移动方法。为了提高漫游的效率,Camera的移动速度不应该是不变的。在实际的操作过程中,水平的鸟瞰移动,比视口发上下移动更为快捷。针对某地某建筑绕点旋转比自身旋转观测更具效率。
- 动态改变速度
这里引入了射线的,在特定时间用射线和地面或建筑发生碰撞,得到碰撞点,通过计算距离,调整移动速度。
- 始终水平地面移动
解决问题要得到一个始终平行于地面的“移动向量”。首先,我们定义Input.GetAxis(“Mouse X”)和Input.GetAxis(“Mouse Y”)为Camera的“移动增量”。unity Camera的x轴是水平地面的,可以用“X移动增量”作为“移动向量的X分量”。y,z两轴是根据图中a角度改变而改变,当控制Camera向前移动时,使用Camera的x欧拉角(如图a角)和Y移动增量,分别用sin和cos计算出“移动向量”的Y、Z分量。这样所构成的向量始终平行于地面。
源代码
using UnityEngine;
using System;
public class CameraControl : MonoBehaviour
{
enum RayType { Mouse, Camera }
Transform m_Camera;
Vector3 m_RayHitPoint;
void Start()
{
m_Camera = this.transform;
m_RayHitPoint = Vector3.zero;
}
void LateUpdate()
{
if (Input.GetMouseButtonDown(1))
{
RayPoint(RayType.Camera);
}
if (Input.GetMouseButton(0))
{
Translation(0.05f);
}
if (Input.GetMouseButton(1))
{
RotatePoint();
}
if (Input.GetAxis("Mouse ScrollWheel") < 0)
{
RayPoint(RayType.Camera);
FrontMove(-1);
}
if (Input.GetAxis("Mouse ScrollWheel") >0)
{
RayPoint(RayType.Camera);
FrontMove(1);
}
}
Vector3 VecOffet=Vector3.zero;
/// <summary>
/// 平移控制
/// </summary>
/// <param name="sheep"></param>
void Translation(float sheep)
{
VecOffet = m_RayHitPoint- m_Camera.position;
float ftCamerDis = Vector3.Distance(m_Camera.position, m_RayHitPoint);
if (ftCamerDis==0)
{
ftCamerDis = 1;
}
float moveX = Input.GetAxis("Mouse X");
float moveY = Input.GetAxis("Mouse Y");
float tranY = moveY * (float)Math.Sin(Math.Round(m_Camera.localRotation.eulerAngles.x, 2) * Math.PI / 180.0);
float tranZ = moveY * (float)Math.Cos(Math.Round(m_Camera.localRotation.eulerAngles.x, 2) * Math.PI / 180.0);
m_Camera.Translate(new Vector3(-moveX, -tranY, -tranZ) * ftCamerDis * sheep, Space.Self);
m_RayHitPoint = m_Camera.position + VecOffet;
}
/// <summary>
/// 绕点旋转
/// </summary>
void RotatePoint()
{
Vector3 eulerAngles = m_Camera.eulerAngles;
float eulerAngles_x = eulerAngles.y;
float eulerAngles_y = eulerAngles.x;
float ftCamerDis = Vector3.Distance(transform.position, m_RayHitPoint);
eulerAngles_x += (Input.GetAxis("Mouse X"));
eulerAngles_y -= (Input.GetAxis("Mouse Y"));
if (eulerAngles_y > 80)
{
eulerAngles_y = 80;
}
else if (eulerAngles_y < 1)
{
eulerAngles_y = 1;
}
Quaternion quaternion = Quaternion.Euler(eulerAngles_y, eulerAngles_x, (float)0);
Vector3 vector = ((Vector3)(quaternion * new Vector3((float)0, (float)0, -ftCamerDis))) + m_RayHitPoint;
m_Camera.rotation = quaternion;
m_Camera.position = vector;
}
/// <summary>
/// 得到射线碰撞点
/// </summary>
void RayPoint(RayType rayType)
{
Ray ray;
switch (rayType)
{
case RayType.Mouse:
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
break;
case RayType.Camera:
ray = new Ray(m_Camera.position, m_Camera.forward);
break;
default:
return;
}
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
m_RayHitPoint = hit.point;
}
else
{
m_RayHitPoint = transform.forward * 800 + transform.position;//摄像机前方 800 点
}
}
/// <summary>
/// 键盘控制
/// </summary>
void KeyTranslation()
{
VecOffet = m_RayHitPoint - m_Camera.position;
float ftCamerDis = Vector3.Distance(m_Camera.position, m_RayHitPoint);
float moveX = Input.GetAxis("Horizontal") * Time.deltaTime * 10;
float moveY = Input.GetAxis("Vertical") * Time.deltaTime * 10;
if (moveX == 0 && moveY == 0)
{
return;
}
float tranY = moveY * (float)Math.Sin(Math.Round(m_Camera.localRotation.eulerAngles.x, 2) * Math.PI / 180.0);
float tranZ = moveY * (float)Math.Cos(Math.Round(m_Camera.localRotation.eulerAngles.x, 2) * Math.PI / 180.0);
m_Camera.Translate(new Vector3(moveX, tranY, tranZ) * ftCamerDis * 0.1f, Space.Self);
m_RayHitPoint = m_Camera.position + VecOffet;
}
/// <summary>
/// 向前移动
/// Direction[方向]
/// </summary>
/// <param name="intDirection">填写正反,1向前移动,2向后移动</param>
void FrontMove(int intDirection)
{
float ftCamerDis = Vector3.Distance(m_Camera.position, m_RayHitPoint);
if (ftCamerDis < 1)
{
ftCamerDis = 1;
}
m_Camera.Translate(Vector3.forward * ftCamerDis * 0.1f* intDirection);
}
}