摄像机应该具有平移跟随,旋转,缩放视野的功能,之前使用的是将摄像机放置到角色下节点,作为角色的子物体跟随,但是这样就写死了:
网上查了一下,发现还是利用摄像机根据target进行处理还是挺方便的:
首先必须要有一个看向的目标角色:
/// <summary>
/// 目标角色
/// </summary>
public Transform target;
也必须限制相机的上下仰角角度:
/// <summary>
/// 最小仰角度
/// </summary>
//public float minAngle = 35;
/// <summary>
/// 最大仰角度
/// </summary>
public float maxAngle = 85;
/// <summary>
/// 初始化角度
/// </summary>
private float initAngle;
然后就是摄像机的视角缩放:也必须有一个范围:不能超过和接近0:
ps:请忽视minAngle ,这个最后还是用了minZAngle 作为最小角度,本来想做一个功能的,但最后还是没做
/// <summary>
/// 最近的摄像机与角色距离
/// </summary>
public float minZ = 5f;
/// <summary>
/// 最远的摄像机与角色距离
/// </summary>
public float maxZ = 20f;
/// <summary>
/// 最短距离的角度
/// </summary>
public float minZAngle = 10;
/// <summary>
/// 摄像机的当前距离
/// </summary>
public float CurCameraDis;
然后就是记录一下水平偏移值和当前的垂直角度:
/// 当前角度
/// </summary>
public float CurCameraAngle;
public float CurCameraAngleH;
首先初始化摄像机坐标:因为我是利用摄像机在角色后面为基准的
transform.position = target.position - target.forward;
首先得到摄像机距离角色的距离值:
private void ZoomCamera()
{
//手机端---应该是手势缩放--还没写
if (Application.platform == RuntimePlatform.Android ||
Application.platform == RuntimePlatform.IPhonePlayer)
{
}
else
{
//电脑端使用鼠标中建作为缩放触发
if (Input.GetAxis("Mouse ScrollWheel") != 0)
{
zoomValue = Input.GetAxis("Mouse ScrollWheel");
targDis = CurCameraDis - zoomValue * zoomSpeed;
}
}
//添加一个差值计算,这样可以平滑缩放---从当前距离变成目标距离
CurCameraDis = Mathf.Lerp(CurCameraDis, targDis, Time.deltaTime * 10);
//限制范围
CurCameraDis = Mathf.Clamp(CurCameraDis, minZ, maxZ);
}
然后就是根据滑动屏幕得到垂直和水平的偏移值和角度:
记录移动前坐标和移动后坐标,然后进行相减,得到是相对应的x和y,对应了水平偏移值和垂直角度:最后进行限制范围(只有垂直范围需要限制)
private void SwipeScreen()
{
CurCameraAngleH = 0;
if (Input.GetMouseButtonDown(0))
{
oldMousePos = Vector2.zero;
isMousePress = true;
}
else if (Input.GetMouseButtonUp(0))
{
mousePosOffset = Vector2.zero;
isMousePress = false;
}
if (!isMousePress)
return;
newMousePos = Input.mousePosition;
if (oldMousePos != Vector2.zero)
{
mousePosOffset = newMousePos - oldMousePos;
//垂直
CurCameraAngle -= mousePosOffset.y * v_speed;
//水平
CurCameraAngleH = mousePosOffset.x* h_speed;
}
oldMousePos = newMousePos;
CurCameraAngle = Mathf.Clamp(CurCameraAngle, minZAngle, maxAngle);
}
有了数据就可以计算相机的坐标了:
private void FollowPlayer()
{
if (target == null) return;
//使用target.position.y,是将相机的高度一直以角色高度为基准,这样角色跳跃,相机也会跟随
Vector3 camPosY = new Vector3(transform.position.x, target.position.y, transform.position.z);
Vector3 targPos2D = new Vector3(target.position.x, target.position.y, target.position.z);
//重建坐标系
Vector3 m_forwld = (camPosY - targPos2D).normalized;
Vector3 m_up = target.up;
m_right = Vector3.Cross(m_forwld, m_up);
//第一根向量--垂直角度:绕m_right轴旋转角度
Quaternion rot = Quaternion.AngleAxis(CurCameraAngle, m_right);
Vector3 dir = camPosY - target.position;
//向量矩阵乘以向量
dir = rot * dir;
//基于第一根向量的第二根向量--绕角色的上方向轴进行旋转
Quaternion rotH = Quaternion.AngleAxis(CurCameraAngleH, m_up);
Vector3 dirH = rotH * dir;
//起始点+向量=终点:相机坐标
transform.position = target.position + dirH;
//Debug.Log(rot + "---" + rotH);
}
最后就是让相机lookat角色:本来想直接lookat角色的,出了点bug,然后直接自己计算吧。
然后加上相机的距离参数:起始点+向量*长度=终点
transform.rotation = rota;中的rota计算的就是lookat旋转
private void RotateCamera()
{
if (target == null) return;
//lookat旋转
Vector3 m_forwld = (target.position - transform.position).normalized;
Vector3 m_up = Vector3.Cross(m_forwld, m_right);
//Debug.Log(target.position + "---------" + transform.position);
Quaternion rota = Quaternion.LookRotation(m_forwld, m_up);
Debug.DrawRay(transform.position, m_forwld * 10, Color.red);
Debug.DrawRay(transform.position, m_up * 10, Color.green);
transform.position = target.position + (transform.position - target.position).normalized * CurCameraDis;
transform.rotation = rota;
}
完整代码:手机端的可以先注释掉,因为我还没在手机端测试,电脑端没发现问题,就是在场景进入的时候,初始化摄像机赋值target,需要手动调用一下InitCamera()对摄像机进行初始化。
手机端处理:我是使用的触控点进行储存判断,最多两个触控点控制相机。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCamera : MonoBehaviour
{
/// <summary>
/// 目标角色
/// </summary>
public Transform target;
/// <summary>
/// 最小仰角度
/// </summary>
//public float minAngle = 35;
/// <summary>
/// 最大仰角度
/// </summary>
public float maxAngle = 85;
/// <summary>
/// 初始化角度
/// </summary>
private float initAngle;
/// <summary>
/// 最近的摄像机与角色距离
/// </summary>
public float minZ = 5f;
/// <summary>
/// 最远的摄像机与角色距离
/// </summary>
public float maxZ = 20f;
/// <summary>
/// 最短距离的角度
/// </summary>
public float minZAngle = 10;
/// <summary>
/// 摄像机的当前距离
/// </summary>
public float CurCameraDis;
/// <summary>
/// 当前角度
/// </summary>
public float CurCameraAngle;
public float CurCameraAngleH;
/// <summary>
/// 相机的偏移值
/// </summary>
public Vector2 mousePosOffset;
public float initDic = 10;
void Start()
{
InitCamera();
}
// Update is called once per frame
void Update()
{
//手机端
if (Application.platform == RuntimePlatform.Android ||
Application.platform == RuntimePlatform.IPhonePlayer)
{
//获取除了特殊点之外的触控点,最多两个,如果是1个就是调整相机的上下左右视角范围
//如果是两个,则是调整相机缩放z值,冻结相机旋转和移动
touchMap = CameraMode.Instance.getContrlCamToch();
if (touchMap.Count == 1)
{
isDown = false;
}
}
else
{
if (Input.GetMouseButtonDown(0))
{
//这个检测是否在遥感范围内的触控,直接可以改为isDown =false
isDown = App.Instance.chickInRect(Input.mousePosition);
}
}
ZoomCamera();
if (!isDown /*|| Input.touches.Length >= 2*/)
{
SwipeScreen();
}
}
bool isDown = false;
private void LateUpdate()
{
FollowPlayer();
RotateCamera();
}
/// <summary>
/// 初始化摄像机
/// </summary>
public void InitCamera()
{
if (target == null) return;
initAngle = 40;
CurCameraAngle = initAngle;
mousePosOffset = Vector2.zero;
transform.position = target.position - target.forward;
targDis = CurCameraDis;
isDown = false;
isEnd = false;
touchDis = 0;
}
bool isMousePress = false;
Vector2 oldMousePos;
Vector2 newMousePos;
public float v_speed = 0.2f;
public float h_speed = 0.1f;
private void SwipeScreen()
{
CurCameraAngleH = 0;
//手机端
if (Application.platform == RuntimePlatform.Android ||
Application.platform == RuntimePlatform.IPhonePlayer)
{
if (touchMap.Count == 1 && touchMap[0].phase == TouchPhase.Began)
{
isEnd = false;
oldMousePos = Vector2.zero;
isMousePress = true;
}
else if (touchMap.Count == 1 && touchMap[0].phase == TouchPhase.Ended)
{
mousePosOffset = Vector2.zero;
isMousePress = false;
}
if (!isMousePress)
return;
if (touchMap.Count == 1 && isEnd == false)
{
newMousePos = touchMap[0].position;
}
}
else
{
if (Input.GetMouseButtonDown(0))
{
oldMousePos = Vector2.zero;
isMousePress = true;
}
else if (Input.GetMouseButtonUp(0))
{
mousePosOffset = Vector2.zero;
isMousePress = false;
}
if (!isMousePress)
return;
newMousePos = Input.mousePosition;
}
if (oldMousePos != Vector2.zero)
{
mousePosOffset = newMousePos - oldMousePos;
//垂直
CurCameraAngle -= mousePosOffset.y * v_speed;
//水平
CurCameraAngleH = mousePosOffset.x* h_speed;
}
oldMousePos = newMousePos;
CurCameraAngle = Mathf.Clamp(CurCameraAngle, minZAngle, maxAngle);
}
private float zoomValue;
public float zoomSpeed = 20;
float targDis;
private List<Touch> touchMap;
private float touchDis;
private bool isEnd;
private void ZoomCamera()
{
if (Application.platform == RuntimePlatform.Android ||
Application.platform == RuntimePlatform.IPhonePlayer)
{
zoomSpeed = 1;
//touchMap = CameraMode.Instance.getContrlCamToch();
if (touchMap.Count >= 2)
{
Vector2 pos1 = touchMap[0].position;
Vector2 pos2 = touchMap[1].position;
if (touchDis == 0)
{
touchDis = Vector3.Distance(pos1, pos2);
}
else
{
targDis = CurCameraDis - (Vector2.Distance(pos1, pos2) - touchDis) * zoomSpeed;
touchDis = Vector3.Distance(pos1, pos2);
}
}
for (int k = 0; k < touchMap.Count; k++)
{
//如果又触控点离开屏幕
if (touchMap[k].phase == TouchPhase.Ended)
{
touchDis = 0;
isEnd = true;
}
}
}
else
{
zoomSpeed = 20;
if (Input.GetAxis("Mouse ScrollWheel") != 0)
{
zoomValue = Input.GetAxis("Mouse ScrollWheel");
targDis = CurCameraDis - zoomValue * zoomSpeed;
}
}
CurCameraDis = Mathf.Lerp(CurCameraDis, targDis, Time.deltaTime * 10);
CurCameraDis = Mathf.Clamp(CurCameraDis, minZ, maxZ);
}
Vector3 m_right;
public float v_sa;
private void FollowPlayer()
{
if (target == null) return;
Vector3 camPosY = new Vector3(transform.position.x, target.position.y, transform.position.z);
Vector3 targPos2D = new Vector3(target.position.x, target.position.y, target.position.z);
//重建坐标系
Vector3 m_forwld = (camPosY - targPos2D).normalized;
Vector3 m_up = target.up;
m_right = Vector3.Cross(m_forwld, m_up);
//第一根向量
Quaternion rot = Quaternion.AngleAxis(CurCameraAngle, m_right);
Vector3 dir = camPosY - target.position;
dir = rot * dir;
//基于第一根向量的第二根向量
Quaternion rotH = Quaternion.AngleAxis(CurCameraAngleH, m_up);
Vector3 dirH = rotH * dir;
transform.position = target.position + dirH;
//Debug.Log(rot + "---" + rotH);
}
private void RotateCamera()
{
if (target == null) return;
//lookat旋转
Vector3 m_forwld = (target.position - transform.position).normalized;
Vector3 m_up = Vector3.Cross(m_forwld, m_right);
//Debug.Log(target.position + "---------" + transform.position);
Quaternion rota = Quaternion.LookRotation(m_forwld, m_up);
Debug.DrawRay(transform.position, m_forwld * 10, Color.red);
Debug.DrawRay(transform.position, m_up * 10, Color.green);
transform.rotation = rota;
}
}
参考:制作大型MMO项目中的相机视角操作
每天学一点QAQ,给我一个赞我会很开心哒