摄像机应该具有平移跟随,旋转,缩放视野的功能,之前使用的是将摄像机放置到角色下节点,作为角色的子物体跟随,但是这样就写死了: 网上查了一下,发现还是利用摄像机根据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)
{
if (Input.touches[0].phase == TouchPhase.Began)
{
isDown = App.Instance.chickInRect(Input.touches[0].position);
}
}
else
{
if (Input.GetMouseButtonDown(0))
{
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;
}
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 (Input.touches.Length == 1 && Input.touches[0].phase == TouchPhase.Began||
Input.touches.Length >= 2 && Input.touches[1].phase == TouchPhase.Began)
{
oldMousePos = Vector2.zero;
isMousePress = true;
}
else if (Input.touches.Length == 1 && Input.touches[0].phase == TouchPhase.Ended||
Input.touches.Length >= 2 && Input.touches[1].phase == TouchPhase.Ended)
{
mousePosOffset = Vector2.zero;
isMousePress = false;
}
if (!isMousePress)
return;
if (Input.touches.Length == 1)
{
newMousePos = Input.touches[0].position;
}
else
{
newMousePos = Input.touches[1].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 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);
}
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);
//距离测算
//if (transform.position == Vector3.zero && CurCameraAngle == 90)
//{
// rota = Quaternion.LookRotation(-target.up, target.forward);
// transform.position = target.position + target.up * CurCameraDis;
//}
//else
//{
transform.position = target.position + (transform.position - target.position).normalized * CurCameraDis;
//}
transform.rotation = rota;
}
}
参考:制作大型MMO项目中的相机视角操作 每天学一点QAQ,给我一个赞我会很开心哒
|