Unity填坑之俯视角相机水平面方向移动
前言
碰到一个需求,就是摄像机是俯视角,即沿X轴旋转了一定角度,同时摄像机还能左右旋转,要求:滑动屏幕时,要让摄像机总是以平行于地面某个高度进行移动。
一、需求分析
一开始以为很简单的需求,如下图俯视角然后移动摄像机,要求移动的平面是地面,相当于地面上有个小人,然后摄像机跟随这个小人,关键就是这里没有小人。玩家在滑屏的时候实现平面移动,且以摄像机视角为参考坐标系。
补充一下,就是当玩家向上滑动屏幕时,向黑色水平面方向移动,向下则向红色方向移动。左右滑动,则是紫色,橙色方向移动。
二、解决方案有两种
1.模拟一个小人
以屏幕发射射线的方式,射向地面,通过每次移动都发射两条射线,一条是上一次鼠标所在屏幕位置发射,Camera.main.ScreenPointToRay(gesture.Position - gesture.DeltaMove),一条是本次鼠标所在屏幕发射Camera.main.ScreenPointToRay(gesture.Position)。然后可以用高度与射线向量,或者碰撞的方式,得到两个地面上的位置Position,这两个position相减得出一个向量,就是屏幕在世界空间中要移动的向量,简易代码如下,本例是利用射线向量与高度y值,得到世界坐标下的平面位置。
void OnDragMove(DragGesture gesture)
{//拖拽移动
if (gesture.Phase == ContinuousGesturePhase.Started)
if (gesture.Phase != ContinuousGesturePhase.Updated) return;
Ray ray1 = Camera.main.ScreenPointToRay(gesture.Position - gesture.DeltaMove);
Ray ray2 = Camera.main.ScreenPointToRay(gesture.Position);
Vector3 pos1 = GetPlanePoint(ray1);
Vector3 pos2 = GetPlanePoint(ray2);
transform.position -= pos2 - pos1;
}
public static Vector3 GetPlanePoint(Ray ray, float planeY = 0)
{
Vector3 dir = ray.direction;
if (dir.y.Equals(0)) return Vector3.zero;
float num = (planeY - ray.origin.y) / dir.y;
return ray.origin + ray.direction * num;
}
2.通过四元数旋转的方式
上面第一种方式,有一个最大的弊端,就是当摄像机不是朝向地面时,射线摄入到天空与地面之间,这样移动距离就很大,移动就快到飞起。于是产生了第二种方法。直接通过玩家手指移动,当作摄像机在摄像机平面的移动。以摄像机本地坐标系为准,让其移动X,Z轴,这样摄像机会朝向地面移动,明显不符合需求,于是想到利用俯视角,将以摄像机本地坐标系得到的将要移动的坐标,通过四元数的方式,绕X轴旋转回来,这样就是在摄像机平面移动了。
代码如下:
void OnDragMove(DragGesture gesture)
{//拖拽移动
if (gesture.Phase == ContinuousGesturePhase.Started)
if (gesture.Phase != ContinuousGesturePhase.Updated) return;
Vector.x = -gesture.DeltaMove.x * moveRatio;
Vector.y = 0;
Vector.z = -gesture.DeltaMove.y * moveRatio;
Vector3 temp2 = transform.TransformPoint(Vector);
Quaternion quaTemp = Quaternion.AngleAxis(-transform.eulerAngles.x,transform.right);
temp2= quaTemp * (temp2 - transform.position)+transform.position;
transform.position = temp2;
}
先创建一个鼠标移动向量(Vector),这就是以摄像机为本地坐标系将要移动的坐标,即在摄像机本地的XZ轴面移动,然后获取到其在世界坐标系中的坐标,然后获取以摄像机本身X轴,X轴旋转摄像机本身X的反向欧拉角度的四元数:Quaternion.AngleAxis(-transform.eulerAngles.x,transform.right);这将是在世界坐标系中,要围绕旋转的四元数,然后用转换后的世界坐标-摄像机的世界坐标得到相当于归原点的向量,用这个坐标围绕摄像机X轴的四元数进行旋转,得到相对向量,加上摄像机本身的坐标即为摄像机将要到的坐标。
不知道上面讲清楚没有,反正把上面这段代码挂在摄像机下即可。
总结
用了两种方法,平移摄像机,第二种方法没有什么太大弊端,仅此记录。