## 自动转向炮台（武器）的实现

### 前言

pitch是围绕X轴旋转，也叫做俯仰角（武器上下转动），如图：

yaw是围绕Y轴旋转，也叫偏航角（武器左右转动），如图：

roll是围绕Z轴旋转，也叫翻滚角，如图：

### 实现

``````using UnityEngine;

public class AutoRotationGun : MonoBehaviour
{
//最大旋转角度限制
public int MaxPitchAngle = 60;
public int MaxYawAngle = 60;

//旋转速度
public int Speed = 20;

//pitch转动的物体
public Transform PitchTransform;
//yaw转动的物体
public Transform YawTransform;
//瞄准物
public Transform TargetTransform;

Vector3 mPitchTarget, mYawTarget;
float mCurrentPitchAngle, mCurrentYawAngle;

//精度
float mPrecision = 0.5f;

Transform mTransform;

float mPitchAngleOffset, mYawAngleOffset, mAimAngleOffset;
Vector3 mPitchCross, mYawCross;

void Start()
{
mTransform = transform;
}

void Update()
{
if (TargetTransform != null && IsCanAim())
{
//旋转不使用PitchTransform.Rotate(x,y,z)，防止z轴的变化
if (PitchTransform == YawTransform)
{
PitchTransform.localEulerAngles = new Vector3(PitchRotation(), YawRotation(), 0);
}
else
{
PitchTransform.localEulerAngles = new Vector3(PitchRotation(), 0, 0);
YawTransform.localEulerAngles = new Vector3(0, YawRotation(), 0);
}
}
#if UNITY_EDITOR
Debug.DrawRay(PitchTransform.position, PitchTransform.forward * 100, Color.red);
#endif
}

bool IsCanAim()
{
//武器到瞄准物的向量与武器的正前面的夹角在90度以内才可瞄准
if (PitchTransform != null && YawTransform != null)
{
//mAimAngleOffset = Mathf.Acos(Vector3.Dot(transform.forward, (TargetTransform.position - PitchTransform.position).normalized)) * Mathf.Rad2Deg;
mAimAngleOffset = Vector3.Angle(mTransform.forward, TargetTransform.position - PitchTransform.position);
if (mAimAngleOffset < 90)
{
return true;
}
}
return false;
}

float PitchRotation()
{
//当前旋转角度
mCurrentPitchAngle = PitchTransform.localEulerAngles.x;
if (mCurrentPitchAngle > 180)
{
mCurrentPitchAngle = -(360 - mCurrentPitchAngle);
}

//武器到瞄准物的向量，映射到武器的yz屏幕上的法向量
mPitchTarget = Vector3.ProjectOnPlane(TargetTransform.position - PitchTransform.position, PitchTransform.right).normalized;

//计算当前瞄准方向与预期方向的夹角。大于精度则需要pitch转动来调整
mPitchAngleOffset = Vector3.Angle(PitchTransform.forward, mPitchTarget);

if (mPitchAngleOffset > mPrecision)
{
//计算两个向量的叉乘，用于判断向量的左右关系
mPitchCross = Vector3.Cross(mPitchTarget, PitchTransform.forward).normalized;
if (mCurrentPitchAngle > -MaxPitchAngle && mPitchCross == PitchTransform.right)
{
return mCurrentPitchAngle - Speed * Time.deltaTime;
}
else if (mCurrentPitchAngle < MaxPitchAngle && mPitchCross != PitchTransform.right)
{
return mCurrentPitchAngle + Speed * Time.deltaTime;
}
}
return mCurrentPitchAngle;
}

float YawRotation()
{
mCurrentYawAngle = YawTransform.localEulerAngles.y;
if (mCurrentYawAngle > 180)
{
mCurrentYawAngle = -(360 - mCurrentYawAngle);
}

mYawTarget = Vector3.ProjectOnPlane(TargetTransform.position - YawTransform.position, YawTransform.up).normalized;
mYawAngleOffset = Vector3.Angle(YawTransform.forward, mYawTarget);

if (mYawAngleOffset > mPrecision)
{
mYawCross = Vector3.Cross(mYawTarget, YawTransform.forward).normalized;
if (mCurrentYawAngle > -MaxYawAngle && mYawCross == YawTransform.up)
{
return mCurrentYawAngle - Speed * Time.deltaTime;
}
else if (mCurrentYawAngle < MaxYawAngle && mYawCross != YawTransform.up)
{
return mCurrentYawAngle + Speed * Time.deltaTime;
}
}
return mCurrentYawAngle;
}
}``````