IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> unity的摄像机处理--第三人称摄像机 -> 正文阅读

[游戏开发]unity的摄像机处理--第三人称摄像机

摄像机应该具有平移跟随,旋转,缩放视野的功能,之前使用的是将摄像机放置到角色下节点,作为角色的子物体跟随,但是这样就写死了:
网上查了一下,发现还是利用摄像机根据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,给我一个赞我会很开心哒

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-07-29 11:59:42  更:2021-07-29 12:00:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 16:06:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码