在 Camera Controller 组件上有一些参数,决定了摄像机与角色之间的相对关系。这些点可以理解成由角色指向摄像机的向量。
其中 Relate Camera Pos 指的是,摄像机下一帧所应该在的位置(该位置用角色指向摄像机的向量表示)
其中Start Pos 指的是,当摄像机距离角色最远时,从角色指向摄像机的向量
其中End Pos 指的是,当摄像机距离角色最近时,从角色指向摄像机的向量
而 Start T Pos 和 End T Pos 是在对应位置上,摄像机旋转的 欧拉角。
当左右移动时,摄像机应该 以 人物为圆心,围绕人物的 y 轴,进行圆周运动
其中,标 R 的向量表示Relate Camera Pos,就是摄像机下一帧需要移动到的位置,与另一个向量之间有一个 夹角α。我们现在需要做的,就是将向量Relate Camera Pos 旋转 α 角度,得到新的 Relate Camera Pos 向量。
其中的 α = Time.deltaTime * RotateSpeed; (时间 * 角速度)
这个旋转的 四元数 就是 Quaternion.AngleAxis(Time.deltaTime * RotateSpeed, Vector3.up)
再用 这个 四元数 乘以 原来的Relate Camera Pos 就可以得到新的 旋转过 α角度后的 Relate Camera Pos
此时 只改变了 摄像机的位置,但是其观察角度并没有跟着改变,此时对于其观察角度来说,只有y轴旋转了α度,x 和 z 沿用之前的值
最后,需要根据这个 Quaternion 修改Start Pos、End Pos、Strat T Pos 、End T Pos的值。其中Start Pos、End Pos直接 用 四元数旋转,Strat T Pos 、End T Pos 只修改 对应的 y 欧拉角。代码如下:
完整代码:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public enum eCameraState
{
//无变化
Normal,
//视角大小
CameraView,
//位置和角度
CameraMove,
//锁定其他目标
LockOtherTarget
}
public class CameraController : MonoBehaviour
{
//3D视角
public Vector3 RelateCameraPos;
public Vector3 StartPos;
public Vector3 EndPos;
public Vector3 StartTPos;
public Vector3 EndTPos;
public Vector3 AutoRoEndPos;
public Vector3 AutoRoEndTPos;
//2.5D视角
public Vector3 twoPoFivePos;
public Vector3 twoPoFiveAngle;
//镜头旋转速度
private float RotateSpeed = 0.1f;
public Vector3 CameraAngle
{
get
{
if (TheTrans == null) return Vector3.zero;
return TheTrans.eulerAngles;
}
}
//相机姿态参数
private Vector3 initialRelateCameraPos;
private Vector3 initialCameraAngle;
private Vector3[] initialCameraRotatePrama;
private Vector3 defultCameraPos;
private Vector3 defultCameraAngle;
public Camera theCamera;
float InitFieldView;
public float SmoothMoveSpeed = 1.5f;
public float SmoothRotateSpeed = 1.5f;
public float camAutoEnlargeSpeed1 = 0.1f;
public float camAutoEnlargeSpeed2 = 0.2f;
//角色
public Transform TargetTransform;
public static CameraController Instance;
//锁定角色
public bool LockSight = true;
//当前相机状态
private eCameraState CurrentState = eCameraState.Normal;
//自动缩放
private bool isCamAutoEnlarge;
private bool isRecovingCam;
Transform TheTrans;
//是否2.5D视角
public bool is2po5DView {
set {
if (value)
{
_is2po5DViewState = 1;
}
else
{
_is2po5DViewState = 2;
}
}
}
private int _is2po5DViewState = 0;
public bool editromode = false;
[ContextMenu("editor camera param")]
void viewparam()
{
RelateCameraPos = TheTrans.position - TargetTransform.position;
}
/// <summary>
/// 初始化相机位置,本脚本唯一入口,需要在角色模型加载后、每次模型刷新时调用
/// </summary>
public void InitCameraRotatePrama()
{
StartPos = initialCameraRotatePrama[0];
EndPos = initialCameraRotatePrama[1];
StartTPos = initialCameraRotatePrama[2];
EndTPos = initialCameraRotatePrama[3];
AutoRoEndPos = initialCameraRotatePrama[4];
AutoRoEndTPos = initialCameraRotatePrama[5];
defultCameraPos = initialCameraRotatePrama[6];
if (_is2po5DViewState == 1)
{
RelateCameraPos = initialCameraRotatePrama[8];
}
else
{
RelateCameraPos = initialRelateCameraPos;
}
twoPoFivePos = initialCameraRotatePrama[8];
twoPoFiveAngle = initialCameraRotatePrama[9];
}
void Awake()
{
Instance = this;
if (!theCamera)
{
theCamera = Camera.main;
}
TheTrans = transform;
// 3D 视角设置
InitFieldView = theCamera.fieldOfView;
initialRelateCameraPos = RelateCameraPos;
initialCameraAngle = TheTrans.eulerAngles;
defultCameraPos = RelateCameraPos;
defultCameraAngle = TheTrans.eulerAngles;
initialCameraRotatePrama = new Vector3[10];
initialCameraRotatePrama[0] = StartPos;
initialCameraRotatePrama[1] = EndPos;
initialCameraRotatePrama[2] = StartTPos;
initialCameraRotatePrama[3] = EndTPos;
initialCameraRotatePrama[4] = AutoRoEndPos;
initialCameraRotatePrama[5] = AutoRoEndTPos;
initialCameraRotatePrama[6] = defultCameraPos;
initialCameraRotatePrama[7] = defultCameraAngle;
//2D视角设置
initialCameraRotatePrama[8] = twoPoFivePos;
initialCameraRotatePrama[9] = twoPoFiveAngle;
}
//控制信号发出
void Update()
{
//判断手机操作信号
TouchController();
if (_is2po5DViewState == 0)
{
if (GUIManager.MainGUIPanel.autoEffectMove.activeInHierarchy)
{
var RotateSpeed = LoadConfigManager.clientGeneralConfig.GetCommonConf("camAutoRotateSpeed");
if (RotateSpeed == null)
{
return;
}
if (isAutoRotateCamera() == 1)
{
canScale = true;
distance = 3f;
moveVec = new Vector2(RotateSpeed, 0);
}
else if (isAutoRotateCamera() == 2)
{
canScale = true;
distance = -3f;
moveVec = new Vector2(RotateSpeed, 0);
}
else
{
canScale = false;
moveVec = new Vector2(0, 0);
}
isRecovingCam = true;
}
else //停止寻路时恢复视角
{
if (isRecovingCam)
{
if (Vector3.Distance(RelateCameraPos, defultCameraPos) > 0.001f || Vector3.Distance(TheTrans.eulerAngles, defultCameraAngle) > 0.001f)
{
canScale = true;
distance = 0f;
}
else
{
canScale = false;
isRecovingCam = false;
}
}
}
}
//2.5D
if (_is2po5DViewState != 0)
{
if (_is2po5DViewState == 1)
{
if (Vector3.Distance(RelateCameraPos,twoPoFivePos)>0.001f || Vector3.Distance(TheTrans.eulerAngles,twoPoFiveAngle)>0.001f)
{
canScale = true;
distance = 2.5f;
}
else
{
canScale = false;
}
}else if (_is2po5DViewState == 2)
{
if (Vector3.Distance(RelateCameraPos, defultCameraPos) > 0.001f || Vector3.Distance(TheTrans.eulerAngles,defultCameraAngle) > 0.001f)
{
canScale = true;
distance = 0f;
}
else
{
_is2po5DViewState = 0;
canScale = false;
}
}
}
//调试快捷键
if (Input.GetKey("o"))
{
canScale = true;
distance = -1;
}
if (Input.GetKey("p"))
{
canScale = true;
distance = 1;
}
if (Input.GetKey("k"))
{
canScale = true;
distance = -2f;
moveVec = new Vector2(20f , 0);
}
if (Input.GetKey("l"))
{
canScale = true;
distance = 2f;
moveVec = new Vector2(-20 , 0);
}
}
private Vector2 oldPosition1;
private Vector2 oldPosition2;
private float distance = 10.0f;
private bool canScale;
//判断手机操作信号
public void TouchController()
{
//判断触摸数量为多点触摸
if (Input.touchCount > 1)
{
//前两只手指触摸类型都为移动触摸
if (Input.GetTouch(0).phase == TouchPhase.Moved && Input.GetTouch(1).phase == TouchPhase.Moved &&
PlayController.Instance != null && PlayController.Instance.ObState == ObjectState.Idel)
{
//计算出当前两点触摸点的位置
var tempPosition1 = Input.GetTouch(0).position;
var tempPosition2 = Input.GetTouch(1).position;
//函数返回真为放大,返回假为缩小
if (oldPosition1 != Vector2.zero && oldPosition2 != Vector2.zero)
{
//函数返回真为放大,返回假为缩小
if (isEnlarge(oldPosition1, oldPosition2, tempPosition1, tempPosition2))
{
canScale = true;
distance = 1f;
}
if (!isEnlarge(oldPosition1, oldPosition2, tempPosition1, tempPosition2))
{
canScale = true;
distance = -1f;
}
}
//备份上一次触摸点的位置,用于对比
oldPosition1 = tempPosition1;
oldPosition2 = tempPosition2;
}
}
else if (Input.touchCount == 1)
{
if (Input.GetTouch(0).phase == TouchPhase.Moved)
{
var tempPosition1 = Input.GetTouch(0).position;
if (oldPosition1 != Vector2.zero)
{
//单点触摸判定方向
if (isMoveCamera(oldPosition1, tempPosition1) == 1)
{
canScale = true;
distance = 2f; //远
}
if (isMoveCamera(oldPosition1, tempPosition1) == 2)
{
canScale = true;
distance = -2f; //近
}
}
oldPosition1 = tempPosition1;
}
}
else if (Input.touchCount == 0)
{
oldPosition1 = Vector2.zero;
oldPosition2 = Vector2.zero;
canScale = false;
}
}
//函数返回真为放大,返回假为缩小
bool isEnlarge(Vector2 oP1 ,Vector2 oP2 ,Vector2 nP1 ,Vector2 nP2)
{
//函数传入上一次触摸两点的位置与本次触摸两点的位置计算出用户的手势
var leng1 =Mathf.Sqrt((oP1.x-oP2.x)*(oP1.x-oP2.x)+(oP1.y-oP2.y)*(oP1.y-oP2.y));
var leng2 =Mathf.Sqrt((nP1.x-nP2.x)*(nP1.x-nP2.x)+(nP1.y-nP2.y)*(nP1.y-nP2.y));
if (leng2 - leng1 > 1)
{
//放大手势
return true;
}else
{
//缩小手势
return false;
}
}
/// <summary>
/// 是否双指滑动屏幕,旋转摄像机
/// </summary>
/// <param name="oP1"></param>
/// <param name="oP2"></param>
/// <param name="nP1"></param>
/// <param name="nP2"></param>
/// <returns>0,没旋转 1,右划 2,左划</returns>
private Vector2 moveVec = new Vector2();
int isMoveCamera(Vector2 oP1, Vector2 nP1) {
if (GUIManager.Instance.controlmanager.transform.localPosition == new Vector3(-9999, 0, 0) || PlayController.IsJoyStickMoving)
{
return 0;
}
if (Screen.width/3 > oP1.x || oP1.x > Screen.width/3 * 2)
{
return 0;
}
moveVec = nP1 - oP1;
//右划手势
if (moveVec.x > 0)
{
return 1;
}
//左划手势
if (moveVec.x < 0)
{
return 2;
}
return 0;
}
private bool canAutoRotate = false;
//根据玩家行走姿态自动旋转镜头,返回旋转方向
int isAutoRotateCamera()
{
var RotateStartAngle = LoadConfigManager.clientGeneralConfig.GetCommonConf("camAutoRotateStartAngle");
var RotateStopAngle = LoadConfigManager.clientGeneralConfig.GetCommonConf("camAutoRotateStopAngle");
Vector3 camVec = TheTrans.transform.position - TargetTransform.position;
Vector3 playerDir = -TargetTransform.forward;
camVec.y = 0;
playerDir.y = 0;
float angle = Vector3.Angle(camVec, playerDir);
if (angle > RotateStartAngle)
{
canAutoRotate = true;
}
Vector3 ans = Vector3.Cross(camVec, playerDir);
if (ans.y < 0 && angle > RotateStopAngle && canAutoRotate) // 逆时针
{
return 1;
}
else if (ans.y > 0 && angle > RotateStopAngle && canAutoRotate) //顺时针
{
return 2;
}
else if (RelateCameraPos != AutoRoEndPos) //停止旋转是继续缩放
{
return 1;
}
else
{
if (canAutoRotate)
{
canAutoRotate = false;
}
return 0;
}
}
Vector3 targetPos;
//执行相机移动操作
void LateUpdate()
{
if (canScale)
{
if (distance == 0f)
{
EnlargeCamera(defultCameraPos, defultCameraAngle, camAutoEnlargeSpeed1, camAutoEnlargeSpeed2);
}
else if (distance == 2.5f)
{
EnlargeCamera(twoPoFivePos, twoPoFiveAngle, 0.1f, 0.2f);
}
else if (distance == -1f)
{
EnlargeCamera(StartPos, StartTPos, 0.1f, 0.2f);
}
else if (distance == 1f)
{
EnlargeCamera(EndPos, EndTPos, 0.1f, 0.2f);
}
else if (distance == -2f)
{
Quaternion tempQuater = Quaternion.AngleAxis(moveVec.magnitude * RotateSpeed, Vector3.up);
RotateCamera(tempQuater);
}
else if (distance == 2f)
{
Quaternion tempQuater = Quaternion.AngleAxis(-moveVec.magnitude * RotateSpeed, Vector3.up);
RotateCamera(tempQuater);
}
else if (distance == -3f)
{
Quaternion tempQuater = Quaternion.AngleAxis(moveVec.magnitude * RotateSpeed, Vector3.up);
RotateCamera(tempQuater);
if (isCamAutoEnlarge)
{
//EnlargeCamera(AutoRoEndPos, AutoRoEndTPos, camAutoEnlargeSpeed1, camAutoEnlargeSpeed2);
if (RelateCameraPos != AutoRoEndPos)
{
RelateCameraPos = Vector3.MoveTowards(RelateCameraPos, AutoRoEndPos, camAutoEnlargeSpeed1);
}
if (transform.localEulerAngles != AutoRoEndTPos)
{
transform.localEulerAngles = Vector3.MoveTowards(transform.localEulerAngles, AutoRoEndTPos, camAutoEnlargeSpeed2);
}
}
}
else if (distance == 3f)
{
Quaternion tempQuater = Quaternion.AngleAxis(-moveVec.magnitude * RotateSpeed, Vector3.up);
RotateCamera(tempQuater);
if (isCamAutoEnlarge)
{
//EnlargeCamera(AutoRoEndPos, AutoRoEndPos, camAutoEnlargeSpeed1, camAutoEnlargeSpeed2);
if (RelateCameraPos != AutoRoEndPos)
{
RelateCameraPos = Vector3.MoveTowards(RelateCameraPos, AutoRoEndPos, camAutoEnlargeSpeed1);
}
if (transform.localEulerAngles != AutoRoEndTPos)
{
transform.localEulerAngles = Vector3.MoveTowards(transform.localEulerAngles, AutoRoEndTPos, camAutoEnlargeSpeed2);
}
}
}
}
if (editromode) return;
if (CurrentState == eCameraState.CameraView)
{
UpdateCameraViewProcess();
}
if (CurrentState == eCameraState.CameraMove)
{
UpdateCameraMove();
}
if (CurrentState == eCameraState.LockOtherTarget)
{
UpdateLockOtherTarget();
}
if (LockSight)
{
targetPos = TargetTransform.position + RelateCameraPos;
TheTrans.position = targetPos;
}
}
//执行旋转相机,输入四元数
private void RotateCamera(Quaternion tempQuater) {
RelateCameraPos = tempQuater * RelateCameraPos;
TheTrans.localEulerAngles = new Vector3(TheTrans.localEulerAngles.x, TheTrans.localEulerAngles.y + tempQuater.eulerAngles.y, TheTrans.localEulerAngles.z);
StartPos = tempQuater * StartPos;
EndPos = tempQuater * EndPos;
defultCameraPos = tempQuater * defultCameraPos;
twoPoFivePos = tempQuater * twoPoFivePos;
twoPoFiveAngle = new Vector3(twoPoFiveAngle.x, TheTrans.localEulerAngles.y, 0);
StartTPos = new Vector3(StartTPos.x, TheTrans.localEulerAngles.y, 0);
EndTPos = new Vector3(EndTPos.x, TheTrans.localEulerAngles.y, 0);
defultCameraAngle = new Vector3(defultCameraAngle.x, TheTrans.localEulerAngles.y, 0);
AutoRoEndPos = tempQuater * AutoRoEndPos;
AutoRoEndTPos = new Vector3(AutoRoEndTPos.x, TheTrans.localEulerAngles.y, 0);
}
//执行相机缩放
private void EnlargeCamera(Vector3 targetPos,Vector3 targetAngle,float enlargeSpeed,float angleSpeed) {
if (RelateCameraPos != targetPos)
{
RelateCameraPos = Vector3.MoveTowards(RelateCameraPos, targetPos, enlargeSpeed);
}
if (transform.localEulerAngles != targetAngle)
{
transform.localEulerAngles = Vector3.MoveTowards(transform.localEulerAngles, targetAngle, angleSpeed);
}
}
}