//此版本是在摄像机跟随物体的基础上改的,自己用了感觉挺好的,一点不足是因为相机跟随物体,所以相机旋转是以目标物体为中心旋转的,而并不是绕相机自身。(我的做法是把Target设置成空物体,下面不挂任何mesh,办法可能比较老土,哈哈。。欢迎指正!!)
using UnityEngine;
using System.Collections;
//[ExecuteInEditMode]
public class MyDungeonCamera : MonoBehaviour
{
/// [摄像机控制参数]
public GameObject Target = null;
public float RotateX = 0f; // 俯仰角度(垂直)
public float RotateY = 180.0f; // 偏航角度(水平)
public float Distance = 10f; // 摄像机远近
public float MoveSmoothTime = 0.3f; // 位置平滑时间
public float RotateSmoothTime = 0.3f; // 旋转平滑时间
public float ScrollWheelSpeed = 1000f; // 中轴调整速度
public Vector3 TargetOffset = Vector3.zero; // 目标偏移量
private Vector3 Velocity = Vector3.zero; // 平滑初速度
private Vector3 Offset = Vector3.zero; // 根据上面三个变量计算出
private const float MinDistance = 2f; // 镜头最近距离
private const float MaxDistance = 100f; // 镜头最远距离
private Quaternion tmpRotation = Quaternion.identity;
private Vector3 tmpPosition = Vector3.zero;
/// [Target参数]
private float m_Horizontal;
private float m_Vertical;
private float m_UpDown;
private float RotateDamping
{
get
{
if (RotateSmoothTime <= 0f) RotateSmoothTime = 0.001f;
return 1f / RotateSmoothTime;
}
}
void Update()
{
TargetCtrl();
}
void LateUpdate()
{
if (Target == null) return;
tmpRotation = transform.rotation;
tmpPosition = transform.position;
UpdateDistance();
UpdateRotation();
UpdatePosition();
transform.rotation = tmpRotation;
transform.position = tmpPosition;
}
private void UpdateRotation()
{
if (!NeedRotate()) return;
Quaternion wantedRotation = Quaternion.Euler(RotateX, RotateY, 0f);
// 旋转采用球形插值
tmpRotation = Quaternion.Slerp(tmpRotation, wantedRotation, Time.deltaTime * RotateDamping);
// 实现实时方向改变
Target.transform.forward = transform.forward;
}
private void UpdatePosition()
{
// 如果有旋转插值,则位置根据旋转变换;否则,位置自己进行插值过渡
if (!NeedRotate())
{
Offset = Quaternion.Euler(RotateX, RotateY, 0f) * Vector3.forward * Distance;
Vector3 wantedPos = Target.transform.position - Offset + TargetOffset;
// 位置采用平滑阻尼过渡
tmpPosition = Vector3.SmoothDamp(tmpPosition, wantedPos, ref Velocity, MoveSmoothTime);
}
else
{
Offset = tmpRotation * Vector3.forward * Distance;
tmpPosition = Target.transform.position - Offset + TargetOffset;
}
}
private void UpdateDistance()
{
float horizontal = Input.GetAxis("Mouse ScrollWheel") * ScrollWheelSpeed * Time.deltaTime;
Distance -= horizontal;
Distance = Mathf.Clamp(Distance, MinDistance, MaxDistance);
}
private bool NeedRotate()
{
Vector3 eulerAngles = transform.rotation.eulerAngles;
return !(FloatEqual(eulerAngles.x, RotateX) && FloatEqual(eulerAngles.y, RotateY));
}
public static bool FloatEqual(float value1, float value2)
{
float ret = value1 - value2;
return ret > -0.0005f && ret < 0.0005f;
}
/// <summary>
/// Target移动控制
/// </summary>
private void TargetCtrl()
{
m_Horizontal = Input.GetAxis("Horizontal") * Time.deltaTime * 4;
m_Vertical = Input.GetAxis("Vertical") * Time.deltaTime * 4;
if (Input.GetKey(KeyCode.Mouse1))
{
RotateX -=Input.GetAxis("Mouse Y") * Time.deltaTime * 50;
RotateX = Mathf.Clamp(RotateX, -50, 88);
RotateY -= -Input.GetAxis("Mouse X") * Time.deltaTime * 50;
RotateY = Mathf.Clamp(RotateY, -110, 110);
}
if (Input.GetKey(KeyCode.Q))
{
m_UpDown -= 0.3f * Time.deltaTime;
}
else if (Input.GetKey(KeyCode.E))
{
m_UpDown += 0.3f * Time.deltaTime;
}
else
{
m_UpDown = 0;
}
Target.transform.Translate(m_Horizontal, m_UpDown, m_Vertical);
//Target.transform.forward = transform.forward;
}
}