一、功能说明
我们知道,3D情况下给定一个位置,我们可以令游戏体朝向该位置,也就是
transform.LookAt(target);
结果就是游戏体的z轴正方向朝向目标。但是在2D中不能直接用这个方法,因为2D情况下游戏体的z轴是永远垂直于屏幕的,并不代表游戏体的前方。此时游戏体的前方可以是y轴正方向,也可以是x轴正方向,这取决于你的游戏体的形状。
综上,需要一个2D的LookAt函数。
二、功能实现
首先定义函数,如下
// transform表示需要旋转的游戏体,forward表示游戏体需要朝向目标的方向,targetPos表示目标位置
void LookAt2D(Transform transform, Vector3 forward, Vector3 targetPos)
{
Vector3 targetDir = targetPos - transform.position;
...
}
效果为forward方向旋转到目标方向。
显然我们要计算两个方向的夹角,不妨再写一个函数,如下
//forwardDir、targetDir均为单位向量
float GetAngle(Vector3 forwardDir, Vector3 targetDir)
{
float cosTheta = Mathf.Clamp(Vector3.Dot(forwardDir,targetDir),-1,1);//限制范围,否则可能会超出范围导致下一步报错
float Theta = Mathf.Acos(cosTheta) * 180 / Mathf.PI;//弧度制转角度制
return Theta;
}
函数就是先做个内积得到余弦值,再取反函数。因为这个角度永远都是小于180°的,我们还需要决定旋转的方向。如果目标方向在forward轴的左半边则为正(顺时针转),如果在右半边则为负(逆时针转)。那么只需计算目标方向在右方向上的投影的正负即可(这里的右指的是正交于forward方向的轴的正方向),如下示意图
所以更改上面代码为
float GetAngle(Vector3 forward, Vector3 targetDir)
{
float cosTheta = Mathf.Clamp(Vector3.Dot(forward,targetDir),-1,1);//限制范围,否则可能会超出范围导致下一步报错
float Theta = Mathf.Acos(cosTheta) * 180 / Mathf.PI;//弧度制转角度制
Vector3 right = new Vector3(forward.y,-forward.x,0);//得到右方向
float rotDir = Mathf.Sign(Vector3.Dot(targetDir,-right));//得到旋转方向
return Theta * rotDir;
}
其中为了得到右方向只需对forward方向逆时针旋转90°。
现在可以对LookAt2D函数进行补充
void LookAt2D(Transform transform, Vector3 forward, Vector3 targetPos)
{
Vector3 targetDir = (targetPos - transform.position).normalized;//获得目标方向单位向量
Vector3 forwardDir = forward.normalized;//获得forward方向的单位向量
float theta = GetAngle(forwardDir,targetDir);//计算旋转角度和方向
transform.rotation *= Quaternion.Euler(0, 0, theta);//绕着z轴旋转theta角度
}
最后,讲一下forward方向怎么获得。情况一,如果已知forward方向的向量表示,那就没问题。情况二,没有向量表示但是有相对transform.right的角度,可以用下面的方法得到
或者从transform.right出发旋转theta角度(即乘一个旋转矩阵)得到。