思路:只考虑xz平面,摄像机前向与敌人的方向夹角即目标为屏幕y轴夹角α,然后通过屏幕宽高计算边缘坐标。
代码如下:
public class Arrow : MonoBehaviour
{
public Transform target;
public Image ui;
public Transform player;
void Start()
{
player = transform;
}
void Update()
{
//获取目标的向量
var tForward = target.position - player.position;
tForward.y = 0;
//获取玩家前向向量 即为屏幕的y轴
var pForward = Quaternion.AngleAxis(player.eulerAngles.y, Vector3.up) * Vector3.forward;
//获取玩家前向向量到目标向量夹角
var rotAngle = Vector3.SignedAngle(pForward, tForward, Vector3.up);
var tRot = Quaternion.AngleAxis(rotAngle, Vector3.up) * Vector3.forward;
tRot.Set(tRot.x, tRot.z, 0);
//ugui是在uicamera前面 图片的旋转
ui.transform.localEulerAngles = new Vector3(0, 0, -rotAngle);
//位置方案1 绕着屏幕中点转圈
//ui.transform.localPosition = tRot.normalized * 50;
//位置方案2 在屏幕边缘显示
float halfWidth = Screen.width / 2 - (ui.transform as RectTransform).rect.width / 2;
float halfHeight = Screen.height / 2 - (ui.transform as RectTransform).rect.width / 2;
//屏幕中点到中上和右上的夹角
var screenRightTopRad = Mathf.Atan(halfWidth / halfHeight);
//转成0-PI的弧度
var rotRad = Mathf.Deg2Rad * Mathf.Abs(rotAngle);
//区分屏幕上面和下面
bool isTop = rotRad < Mathf.PI / 2;
//下半使用-y轴计算
rotRad = isTop ? rotRad : Mathf.PI - rotRad;
float x = 0, y = 0;
//弧度比屏幕右上的夹角小,需要计算x坐标 否则计算y坐标
if (rotRad < screenRightTopRad)
{
x = halfHeight * Mathf.Tan(rotRad) * Math.Sign(rotAngle);
y = halfHeight;
}
else
{
x = halfWidth * Math.Sign(rotAngle);
y = halfWidth * Mathf.Tan(Mathf.PI / 2 - rotRad);
}
y = isTop ? y : -y;
ui.transform.localPosition = new Vector2(x, y);
}
}
效果图如下,圆球为角色,方块为敌人,摄像机跟随在角色身后
摄像机没有旋转:
摄像机旋转: