二,3D角色摄像机控制方案

前言

这段时间刚好看悠游视频,学习MMORPG的游戏制作,开这个篇章的主要是为了记录下自己的学习历程,以及自己的一些理解和思考,主要会把学习到的一些比较重要的东西记录下。

使用的环境

  1. Unity版本 2020.2.3f1c1
  2. 使用到的插件 FingerGestures(手势识别)
  3. 课程下载地址 http://www.u3dol.com/index_CourseOne.html

代码工程在文末

摄像机控制方案

通常情况下,我们控制摄像机都是直接去操作MainCamera位置旋转来达到摄像机跟随目标,旋转,拉近视角,但是这样跟随,旋转和拉近视角可能会相互影响,导致操作会变得很复杂。

刚好在视频中看到这种3d角色的摄像机控制方案,顿时感觉豁然开朗。

总体思想:既然多种操作之间会相互影响,那么自然而然就想到通过将跟随目标,旋转视角,拉近视角这些控制分离开来,每个属性控制单独的操作,使得相互之间没有影响。从而达到更方便简单的去实现摄像机的控制。

摄像机结构

摄像机的大致结构如下图所示,主要分5层,每层职责清晰
在这里插入图片描述

  1. CameraFolowAndRotate 这层主要是负责
    • 摄像机跟随目标
    • 摄像机左右旋转
  2. CameraUpAndDown 这层主要负责
    • 摄像机上下旋转
  3. CameraZoomContainer 这层主要负责
    • 初始化摄像机位置
    • 初始化摄像方向
  4. CameraContainer 这层主要负责
    • 摄像机拉远拉近
  5. Main Camera 这个就是场景摄像机

注:temp 主要是用来方便在scene场景中查看
在这里插入图片描述

摄像机控制

1. 摄像机跟随目标

通过修改 CameraFolowAndRotate.transform.position 坐标值,来实现摄像机跟随目标。
因为会设置好摄像机的初始角度,初始位置,所以直接设置 CameraFolowAndRotate.transform.position = Target.transform.position即可实现跟随目标。

2. 摄像机左右旋转

通过修改 CameraFolowAndRotate.transform.rotation.y 值,来实现摄像机左右旋转。

3. 摄像机上下旋转

通过修改 CameraUpAndDown.transform.rotation.z 值,来实现摄像机上下旋转。

4. 摄像机拉远拉近

通过修改 CameraContainer.transform.position.z 值,来实现摄像机拉近拉远。

具体代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 摄像机控制
/// </summary>
public class CameraCtrl : MonoBehaviour
{
    /// <summary>
    /// 单例
    /// </summary>
    public static CameraCtrl Instance;

    /// <summary>
    /// 摄像机 上下
    /// </summary>
    [SerializeField]
    private Transform m_CameraUpAndDown;

    /// <summary>
    /// 摄像机 放大缩小
    /// </summary>
    [SerializeField]
    private Transform m_CameraZoomContainer;

    /// <summary>
    /// 摄像机 容器
    /// </summary>
    [SerializeField]
    private Transform m_CameraContainer;

    private void Awake()
    {
        Instance = this;
    }

    /// <summary>
    /// 初始化摄像机
    /// </summary>
    public void Init()
    {
        m_CameraUpAndDown.transform.localEulerAngles = new Vector3(0, 0, Mathf.Clamp(m_CameraUpAndDown.transform.localEulerAngles.z, 30, 80));
    }
    
    /// <summary>
    /// 设置摄像机左右旋转
    /// </summary>
    /// <param name="type">type = 0 表示向左  type =1 表示向右</param>
    public void SetCameraRotate(int type)
    {
        transform.Rotate(0, 40f * Time.deltaTime * (type == 0 ? -1:1), 0);
    }

    /// <summary>
    /// 设置摄像机上下移动
    /// </summary>
    /// <param name="type">type = 0 表示向上  type =1 表示向下</param>
    public  void SetCameraUp(int type)
    {
        m_CameraUpAndDown.Rotate(0, 0, 30f * Time.deltaTime * (type == 0 ? 1 : -1));
        //-15,40
        m_CameraUpAndDown.transform.localEulerAngles = new Vector3(0, 0, Mathf.Clamp(m_CameraUpAndDown.transform.localEulerAngles.z, 30, 80));
    }

    /// <summary>
    /// 设置摄像机前后 移动
    /// </summary>
    /// <param name="type">type = 0 表示向前  type =1 表示向后</param>
    public void SetCameraZoom(int type)
    {
        m_CameraContainer.Translate(Vector3.forward * 20 * (type == 0 ? 1 : -1) * Time.deltaTime);


        m_CameraContainer.transform.localPosition = new Vector3(0, 0, Mathf.Clamp(m_CameraContainer.transform.localPosition.z, -5, 5));
    }

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(transform.position, 15);

        Gizmos.color = Color.blue;
        Gizmos.DrawWireSphere(transform.position, 14);

        Gizmos.color = Color.green;
        Gizmos.DrawWireSphere(transform.position, 12);
    }
}

摄像机方案使用

摄像机设置

1. CameraFolowAndRotate 设置
  • 坐标归零
  • 挂载 CameraCtrl 控制脚本,并设置好属性值

如下图所示
在这里插入图片描述

2. CameraUpAndDown 设置
  • 设置初始角度(可根据需要设置)

如下图所示
在这里插入图片描述

3. CameraZoomContainer 设置
  • 设置初始位置
  • 设置初始摄像机方向(只配置Rotation.y)

如下图所示
在这里插入图片描述

4. CameraContainer 设置
  • 坐标归零

如下图所示
在这里插入图片描述

5. Main Camera 设置
  • 坐标归零 (方便后期做一些震屏等摄像机特效)

如下图所示
在这里插入图片描述

摄像机控制

跟随控制

前面也提到了,主要是跟随直接设置坐标就好了,这边直接在目标Update中设置摄像机与目标坐标相等即可

CameraCtrl.Instance.transform.position = transform.position;
旋转控制

这边根据收拾手势拖动方向来调用对应摄像机上下旋转,左右旋转控制方法

具体代码如下:

   /// <summary>
    /// 手指拖动处理
    /// </summary>
    /// <param name="dragDir"></param>
    private void OnFigerDrag(FingerEvent.DragDir dragDir)
    {
        switch (dragDir)
        {
            case FingerEvent.DragDir.Up:
                CameraCtrl.Instance.SetCameraUp(1);
                break;
            case FingerEvent.DragDir.Down:
                CameraCtrl.Instance.SetCameraUp(0);
                break;
            case FingerEvent.DragDir.Left:
                CameraCtrl.Instance.SetCameraRotate(0);
                break;
            case FingerEvent.DragDir.Right:
                CameraCtrl.Instance.SetCameraRotate(1);
                break;
            default:
                break;
        }
    }
近远控制

这边根据收拾手势来调用对应摄像机远近控制方法,具体电脑使用鼠标滑轮控制,移动设备使用两手指缩小放大来控制。

具体代码如下

   /// <summary>
    /// 缩放摄像机
    /// 缩放摄像机
    /// </summary>
    /// <param name="zoomType"></param>
    private void OnZoom(FingerEvent.ZoomType zoomType)
    {
        switch (zoomType)
        {
            case FingerEvent.ZoomType.In:
               CameraCtrl.Instance.SetCameraZoom(0);
                break;
            case FingerEvent.ZoomType.Out:
               CameraCtrl.Instance.SetCameraZoom(1);
                break;
            default:
                break;
        }
    }
手势触发

这边使用了插件FingerGestures 手势插件,主要是判断鼠标或者触控来识别对应控制 这边就不多说了 有兴趣的可以自己去看看源代码。

下面是FingerEvent代码

using UnityEngine;
using System.Collections;
public class FingerEvent :  MonoBehaviour {

    public static FingerEvent Instance;
    /// <summary>
    /// 手指拖动方向
    /// </summary>
    public enum DragDir
    {
        Up,
        Down,
        Left,
        Right
    }
    /// <summary>
    /// 放大类型
    /// </summary>
    public enum ZoomType
    {
        /// <summary>
        /// 拉近
        /// </summary>
        In,
        /// <summary>
        /// 拉远
        /// </summary>
        Out
    }
    /// <summary>
    /// 前一拖动坐标点
    /// </summary>
    private Vector2 oldDragPos;
    /// <summary>
    /// 点击类型
    /// </summary>
    private int porClickType =-1;
    /// <summary>
    /// 前一双指距离
    /// </summary>
    private float proTouchDis;
    /// <summary>
    /// 手指拖动委托
    /// </summary>
    public System.Action<DragDir> OnFigerDrag;
    /// <summary>
    /// 点击委托
    /// </summary>
    public System.Action OnPlayerClick;
    /// <summary>
    ///  拉近拉远委托
    /// </summary>
    public System.Action<ZoomType> OnZoom;

    private void Awake()
    {
        Instance = this;
    }
    void OnEnable()
    {
        //启动时调用,这里开始注册手势操作的事件。
        
        //按下事件: OnFingerDown就是按下事件监听的方法,这个名子可以由你来自定义。方法只能在本类中监听。下面所有的事件都一样!!!
        FingerGestures.OnFingerDown += OnFingerDown;
        //抬起事件
        FingerGestures.OnFingerUp += OnFingerUp;
        //开始拖动事件
        FingerGestures.OnFingerDragBegin += OnFingerDragBegin;
        //拖动中事件...
        FingerGestures.OnFingerDragMove += OnFingerDragMove;
        //拖动结束事件
        FingerGestures.OnFingerDragEnd += OnFingerDragEnd;
        //长按事件
        FingerGestures.OnFingerLongPress += OnFingerLongPress; 
    }
    private void Update()
    {
#if UNITY_EDITOR || UNITY_STANDALONE_WIN
        if (Input.GetAxis("Mouse ScrollWheel") < 0)
        {
            if (OnZoom != null)
            {
                OnZoom(ZoomType.In);
            }
        }
        else if (Input.GetAxis("Mouse ScrollWheel") > 0)
        {
            if (OnZoom != null)
            {
                OnZoom(ZoomType.Out);
            }
        }
#elif UNITY_ANDROID || UNITY_IPHONE
         if (Input.touchCount > 1)
        {
            if(Input.GetTouch(0).phase == TouchPhase.Moved || Input.GetTouch(1).phase == TouchPhase.Moved)
            {
                float touchDis = Vector2.Distance(Input.GetTouch(0).position, Input.GetTouch(0).position);
                if (proTouchDis > touchDis)
                {
                    if (OnZoom != null)
                    {
                        OnZoom(ZoomType.In);
                    }
                }
                else
                {
                    if (OnZoom != null)
                    {
                        OnZoom(ZoomType.Out);
                    }
                }
            }
        }
#endif
    }
   void OnDisable()
    {
        //关闭时调用,这里销毁手势操作的事件
        //和上面一样
        FingerGestures.OnFingerDown -= OnFingerDown;
        FingerGestures.OnFingerUp -= OnFingerUp;
        FingerGestures.OnFingerDragBegin -= OnFingerDragBegin;
        FingerGestures.OnFingerDragMove -= OnFingerDragMove;
        FingerGestures.OnFingerDragEnd -= OnFingerDragEnd;
        FingerGestures.OnFingerLongPress -= OnFingerLongPress;
    }
    //开始滑动
    void OnFingerDragBegin( int fingerIndex, Vector2 fingerPos, Vector2 startPos )
    {
        porClickType = 2;
        oldDragPos = fingerPos;
    }
    //滑动中
    void OnFingerDragMove( int fingerIndex, Vector2 fingerPos, Vector2 delta )
    {
        porClickType = 3;
        Vector2 dir = fingerPos - oldDragPos;

        DragDir dragDir;
        if (dir.x > dir.y) {
            if(dir.x > -dir.y) {
                //往右
                dragDir = DragDir.Right;
            }
            else {
                //往下
                dragDir = DragDir.Down;
            }
        }
        else{
            if (dir.x > -dir.y) {
                //往左
                dragDir = DragDir.Up;
            }
            else {
                //往上
                dragDir = DragDir.Left;
            }
        }
        if (OnFigerDrag != null)
        {
            OnFigerDrag(dragDir);
        }
    }
}

结语

单独一篇讲这个,主要是第一次看到这种控制方案觉得挺新奇的,2d游戏,大部分3d游戏也不会用到这种方案,只是这种方案,将多种控制相互关联的属性拆开来,每种单独控制,复杂问题简单化倒是以后处理问题的一种很好的思路。


代码工程下载

具体的脚本这边就不单独给出来了,主要就是CameraCtrl脚本,FingerGestures这个插件只是更方便的提供多平台同一套控制方法。

整个Unity项目Demo下载

Github工程地址 https://github.com/Wsxiaojian/MMORPG

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值