實(shí)現(xiàn)Unity中簡(jiǎn)單的相機(jī)系統(tǒng)

風(fēng)景.jpeg

曾幾何時(shí)一直癡迷著手游,特別是3D的游戲,繼而在學(xué)過(guò)游戲開發(fā)之后對(duì)其中的技術(shù)實(shí)現(xiàn)也是充滿了好奇。今天帶來(lái)的就是對(duì)游戲角色的至關(guān)重要的相機(jī)系統(tǒng)的基礎(chǔ)實(shí)現(xiàn),當(dāng)然基于目前技術(shù)水平所限制,我分享的其實(shí)也不是很難的東西哈。

相機(jī)系統(tǒng)的大概了解

所謂的相機(jī)系統(tǒng)其實(shí)也就是對(duì)相機(jī)的管理,想想當(dāng)初玩游戲通過(guò)屏幕手勢(shì)對(duì)游戲角色的各方面觀察,這也就是我們普遍意義上的相機(jī)系統(tǒng),當(dāng)然這算是比較簡(jiǎn)單的一種,我們所要實(shí)現(xiàn)的就是通過(guò)屏幕終端手勢(shì)來(lái)控制游戲中的相機(jī)。

效果圖

Demo.gif

有童鞋可能不明白我在屏幕上干了啥,這里說(shuō)明下,其實(shí)也就是上下左右滑動(dòng)和鼠標(biāo)滾輪滾動(dòng)而已,然后實(shí)現(xiàn)的結(jié)果就是圖中所示。

實(shí)現(xiàn)原理概括

其實(shí)從效果圖中就能看出,我所做的操作無(wú)非手勢(shì)的不同,所對(duì)應(yīng)的就是相機(jī)的狀態(tài)改變,換作代碼邏輯也很好分析。

首先我需要有監(jiān)聽屏幕上下左右滑動(dòng)的事件,鼠標(biāo)滾輪的觸發(fā)事件,事件有了之后就需要對(duì)相機(jī)進(jìn)行控制,相機(jī)的控制類型無(wú)非旋轉(zhuǎn)和位移兩種。(上下左右滑動(dòng)對(duì)應(yīng)相機(jī)的旋轉(zhuǎn),位移對(duì)應(yīng)著鼠標(biāo)滾輪滾動(dòng))做完這些其實(shí)也就剩不了什么了,這里需要提的一點(diǎn)是,相機(jī)結(jié)構(gòu)的構(gòu)造比較重要,僅僅只依靠一個(gè)相機(jī)我感覺(jué)不好操作,所以這里提供了一個(gè)相機(jī)結(jié)構(gòu)參考,如下圖

捕獲.JPG

相機(jī)結(jié)構(gòu)介紹完了,那么整體的相機(jī)系統(tǒng)的原理也差不多了,接下來(lái)可以看看代碼實(shí)現(xiàn)。

關(guān)鍵代碼演示

首先可以看下整個(gè)系統(tǒng)的事件監(jiān)聽,這里導(dǎo)入了手勢(shì)插件FingerGestures


public class FingerEvent : MonoBehaviour
{

    public static FingerEvent Instance;

    public enum FingerDir
    {
        Up,
        Down,
        Left,
        Right
    }

    /// <summary>
    /// 滑動(dòng)委托
    /// </summary>
    public Action<FingerDir> OnFingerDrag;

    public enum ZoomType
    {
        In,
        Out
    }
    /// <summary>
    /// 縮放的委托
    /// </summary>
    public Action<ZoomType> OnZoom;
    private Vector2 m_TempFinger1Pos;
    private Vector2 m_TempFinger2Pos;

    private Vector2 m_OldFinger1Pos;
    private Vector2 m_OldFinger2Pos;

    /// <summary>
    /// 玩家點(diǎn)擊地面的委托
    /// </summary>
    public Action OnPlayerClickGround;
    /// <summary>
    /// 拖動(dòng)的舊位置
    /// </summary>
    private Vector2 m_OldDragPos;
    /// <summary>
    /// 拖動(dòng)的方向
    /// </summary>
    private Vector2 m_DragDir;
    /// <summary>
    /// 交互參數(shù)
    /// </summary>
    private int m_PreFinger = -1;

    void Awake()
    {
        Instance = this;
    }

    void OnEnable()
    {
        //啟動(dòng)時(shí)調(diào)用,這里開始注冊(cè)手勢(shì)操作的事件。

        //按下事件: OnFingerDown就是按下事件監(jiān)聽的方法,這個(gè)名子可以由你來(lái)自定義。方法只能在本類中監(jiān)聽。下面所有的事件都一樣?。?!
        FingerGestures.OnFingerDown += OnFingerDown;
        //開始拖動(dòng)事件
        FingerGestures.OnFingerDragBegin += OnFingerDragBegin;
        //拖動(dòng)中事件...
        FingerGestures.OnFingerDragMove += OnFingerDragMove;
        //拖動(dòng)結(jié)束事件
        FingerGestures.OnFingerDragEnd += OnFingerDragEnd;
    }

    private void Update()
    {
        if (Input.GetAxis("Mouse ScrollWheel") < 0)
        {
            if (OnZoom != null)
            {
                OnZoom(ZoomType.Out);
            }
        }
        else if (Input.GetAxis("Mouse ScrollWheel") > 0)
        {
            if (OnZoom != null)
            {
                OnZoom(ZoomType.In);
            }
        }
    }

    void OnDisable()
    {
        //關(guān)閉時(shí)調(diào)用,這里銷毀手勢(shì)操作的事件
        //和上面一樣
        FingerGestures.OnFingerDown -= OnFingerDown;
        FingerGestures.OnFingerDragBegin -= OnFingerDragBegin;
        FingerGestures.OnFingerDragMove -= OnFingerDragMove;
        FingerGestures.OnFingerDragEnd -= OnFingerDragEnd;
    }

    //按下時(shí)調(diào)用
    void OnFingerDown(int fingerIndex, Vector2 fingerPos)
    {
        m_PreFinger = 1;
    }

    //開始滑動(dòng)
    void OnFingerDragBegin(int fingerIndex, Vector2 fingerPos, Vector2 startPos)
    {
        m_PreFinger = 2;
        m_OldDragPos = fingerPos;
    }
    //滑動(dòng)結(jié)束
    void OnFingerDragEnd(int fingerIndex, Vector2 fingerPos)
    {
        m_PreFinger = 4;
    }
    //滑動(dòng)中
    void OnFingerDragMove(int fingerIndex, Vector2 fingerPos, Vector2 delta)
    {
        m_PreFinger = 3;
        m_DragDir = fingerPos - m_OldDragPos;
        if (m_DragDir.y < m_DragDir.x && m_DragDir.y > -m_DragDir.x)
        {
            //向右
            if (OnFingerDrag != null)
            {
                OnFingerDrag(FingerDir.Right);
            }
        }
        else if (m_DragDir.y > m_DragDir.x && m_DragDir.y < -m_DragDir.x)
        {
            //向左
            if (OnFingerDrag != null)
            {
                OnFingerDrag(FingerDir.Left);
            }
        }
        else if (m_DragDir.y > m_DragDir.x && m_DragDir.y > -m_DragDir.x)
        {
            //向上
            if (OnFingerDrag != null)
            {
                OnFingerDrag(FingerDir.Up);
            }
        }
        else
        {
            //向下
            if (OnFingerDrag != null)
            {
                OnFingerDrag(FingerDir.Down);
            }
        }
    }
}

然后就是我們的相機(jī)控制邏輯了,其實(shí)也就是針對(duì)我們之前的相機(jī)結(jié)構(gòu)來(lái)的,運(yùn)用的也就是transform里頭的東西


public class CameraCtr : MonoBehaviour {

    public static CameraCtr Instance;
    /// <summary>
    /// 控制攝像機(jī)上下
    /// </summary>
    [SerializeField]
    private Transform m_CameraUpAndDown;
    /// <summary>
    /// 控制攝像機(jī)縮放父物體
    /// </summary>
    [SerializeField]
    private Transform m_CameraZoomContainer;
    /// <summary>
    /// 攝像機(jī)容器
    /// </summary>
    [SerializeField]
    private Transform m_CameraContainer;

    void Awake()
    {
        Instance = this;
    }

    /// <summary>
    /// 初始化
    /// </summary>
    public void Init()
    {
        m_CameraUpAndDown.localEulerAngles = new Vector3(0, 0, Mathf.Clamp(m_CameraUpAndDown.localEulerAngles.z, 10f, 80f));
    }

    /// <summary>
    /// 設(shè)置攝像機(jī)旋轉(zhuǎn) 
    /// </summary>
    /// <param name="type">0=左 1=右</param>
    public void SetCameraRotate(int type)
    {
        transform.Rotate(0,80*Time.deltaTime * (type == 1?-1:1),0);
    }

    /// <summary>
    /// 設(shè)置攝像機(jī)上下 
    /// </summary>
    /// <param name="type">0=上 1=下</param>
    public void SetCameraUpAndDown(int type)
    {
        m_CameraUpAndDown.Rotate(0,0, 30 * Time.deltaTime * (type == 1 ? -1 : 1));
        m_CameraUpAndDown.localEulerAngles = new Vector3(0,0,Mathf.Clamp(m_CameraUpAndDown.localEulerAngles.z,10f,80f));
    }

    /// <summary>
    /// 設(shè)置攝像機(jī)縮放 
    /// </summary>
    /// <param name="type">0=拉近 1=拉遠(yuǎn)</param>
    public void SetCameraZoom(int type)
    {
        m_CameraContainer.Translate(Vector3.forward * 20 * Time.deltaTime * (type == 1 ? -1 : 1));
        m_CameraContainer.localPosition = new Vector3(0, 0, Mathf.Clamp(m_CameraContainer.localPosition.z, -5f, 8f));
    }

    public void AutoLookAt(Vector3 pos)
    {
        m_CameraZoomContainer.LookAt(pos);
    }
}

然后就沒(méi)有然后了,代碼中有注釋,看不懂可以多看幾遍哈!

代碼地址

具體代碼我已經(jīng)上傳Github,看這里

代碼上可能還存在些問(wèn)題,歡迎提出自己的意見(jiàn)。

總結(jié)

以前其實(shí)對(duì)相機(jī)系統(tǒng)不是很了解,而這篇分享也是基于目前我所學(xué)習(xí)的課程的筆記而已,也希望后面能更多得學(xué)習(xí)新的東西能拿出來(lái)分享。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,940評(píng)論 25 709
  • 果子還沒(méi)有采摘 正在收集最后的陽(yáng)光 披上一件件紅袍 準(zhǔn)備過(guò)一個(gè)溫暖的冬天 籬笆上的牽?;?像一個(gè)驕傲的麥田守望者 ...
    一團(tuán)菌閱讀 288評(píng)論 3 8
  • 成長(zhǎng)路上的苦,誰(shuí)經(jīng)歷誰(shuí)知道。 很多家長(zhǎng)在教育孩子時(shí),總會(huì)說(shuō),你看誰(shuí)家的孩子多厲害,考試考了多少分,你看誰(shuí)誰(shuí)多懂事,...
    月媽育兒閱讀 429評(píng)論 0 0
  • 「不是勵(lì)志故事,是每個(gè)人都可以做到的事情」 12.12,去了浙大,和一些大二大三學(xué)生交流。 誒,說(shuō)個(gè)題外話,我比較...
    tree閱讀 301評(píng)論 2 2
  • 空調(diào)的聯(lián)想 新家裝修 安裝空調(diào)是父親的意見(jiàn) 盡管早有此意 還是驚訝 80歲的父親如此周全 如...
    邊驥秋生閱讀 519評(píng)論 1 3

友情鏈接更多精彩內(nèi)容