Unity基於響應式編程(Reactive programming)入門

系列目錄

【Unity3D基礎】讓物體動起來①--基於UGUI的鼠標點擊移動html

【Unity3D基礎】讓物體動起來②--UGUI鼠標點擊逐幀移動編程

時光煮雨 Unity3D讓物體動起來③—UGUI DoTween&Unity Native2D實現架構

時光煮雨 Unity3D實現2D人物動畫① UGUI&Native2D序列幀動畫框架

時光煮雨 Unity3D實現2D人物動畫② Unity2D 動畫系統&資源效率異步

背景

前有慕容小匹夫的一篇《解構C#遊戲框架uFrame兼談遊戲架構設計》,引用文中內容異步編程

uFrame是提供給3D開發者使用的一個框架插件,它自己模仿了MVVM這種架構模式(事實上並不包含Model部分,且多出了Controller部分)。由於用於Unity3D,因此它向開發者提供了一套基於Editor的可視化編輯工具,能夠用來管理代碼結構等。須要指出的是它的一個重要的理念,同時也是軟件工程中的一個重要理念就是關注分離(Separation of concern,SoC)。uFrame藉助控制反轉(IoC)/依賴注入(DI)實現了這種分離,從而進一步實現了MVVM這種模式。且在1.5版本以後,引入了UniRx庫,引進了響應式編程的思想。函數

讀起來高大上,本文主要想從實際出發,着手最後一句「且在1.5版本以後,引入了UniRx庫,引進了響應式編程的思想。」,在Unity中如何使用響應式編程,如何使用UniRx庫。工具

固然一下列出這麼多新概念性的東西,做爲新手必然理解起來有困難的,固然我也但願你是天賦迥異的人。這裏列出幾點,若是你不瞭解,請自行去學習或者複習,回來在看也不遲。學習

一、Linq基礎,Linq的本質及與傳統命令式編程的區別和優勢動畫

二、聲明式編程和命令式編程的概念和區別

三、什麼是響應式編程

四、什麼是觀察者模式

五、軟件編程中Stream的概念

好了裝b時間過去了,讓咱們簡單的說下什麼是響應式編程。這裏也不廢話,引用一段,看的懂得天然明白,不懂得仍是不明白

什麼是反應式編程:反應式編程(Reactive programming)簡稱Rx,他是一個使用LINQ風格編寫基於觀察者模式的異步編程模型。簡單點說Rx = Observables + LINQ + Schedulers。

這裏爲何要在遊戲開發中引入響應式編程Rx,答案是遊戲特別適合RX編程,由於在遊戲中普遍應用了時間(幀)和事件(UI)的概念,時間自己是一種流,而事件也是基於時間的一種信號(並非特別準確,意會),而這正是RX所擅長的。

600216-20151118151734936-2055036453

實現

本文以系列文章中的精靈鼠標移動和序列幀動畫爲基礎,沒有基礎的先參考下傳統實現方式一下兩篇文章

時光煮雨 Unity3D實現2D人物動畫① UGUI&Native2D序列幀動畫

時光煮雨 Unity3D實現2D人物動畫② Unity2D 動畫系統&資源效率

這裏引入了UniRx庫,來實現基於響應式編程及聲明式編程代碼重構,代碼以下:

using UnityEngine;
using UniRx;

public class PlayerController : MonoBehaviour
{
    public float speed;
    private Vector3 moveDirection;

    private int currentTexture = 0;
    public Sprite[] textureArray;
    // Use this for initialization
    void Start()
    {
        //鼠標控制移動,每幀更新
        Observable.EveryUpdate()
         .Subscribe(_ =>
         {
             //一、得到當前位置
             Vector3 curenPosition = this.transform.position;
             //二、得到方向
             if (Input.GetButton("Fire1"))
             {
                 Vector3 moveToward = Camera.main.ScreenToWorldPoint(Input.mousePosition);

                 moveDirection = moveToward - curenPosition;
                 moveDirection.z = 0;
                 moveDirection.Normalize();
             }
             //三、插值移動
             Vector3 target = moveDirection * speed + curenPosition;
             transform.position = Vector3.Lerp(curenPosition, target, Time.deltaTime);
         });

        //幀動畫
        SpriteRenderer spriteRenderer = GetComponent<SpriteRenderer>();
        //定時器每隔5幀
        Observable.IntervalFrame(5).Subscribe(_ =>
        {
            currentTexture++;
            if (currentTexture >= textureArray.Length)
            {
                currentTexture = 0;
            }
            spriteRenderer.sprite = textureArray[currentTexture];
        });
    }

}
是的沒有看錯,你沒有發現熟悉的Update函數,若是說以上函數讓你看到就是把全部代碼就放在了Start裏面而已,咱們再重構一下代碼,使用提取方法,看看效果,這就是聲明式編程的魅力,程序可讀性加強,更適合人類的思惟方式

using UnityEngine;
using UniRx;

public class PlayerController : MonoBehaviour
{
    public float speed;
    private Vector3 moveDirection;

    private int currentTexture = 0;
    public Sprite[] textureArray;
    // Use this for initialization
    void Start()
    {
        //鼠標控制移動,每幀更新
        PlayerMove();

        //角色 幀動畫
        PlayerAnimation();
    }

    /// <summary>
    /// 角色 幀動畫控制
    /// </summary>
    private void PlayerAnimation()
    {
        SpriteRenderer spriteRenderer = GetComponent<SpriteRenderer>();
        //定時器每隔5幀
        Observable.IntervalFrame(5).Subscribe(_ =>
        {
            currentTexture++;
            if (currentTexture >= textureArray.Length)
            {
                currentTexture = 0;
            }
            spriteRenderer.sprite = textureArray[currentTexture];
        });
    }

    /// <summary>
    /// 鼠標控制移動,每幀更新
    /// </summary>
    private void PlayerMove()
    {
        Observable.EveryUpdate()
            .Subscribe(_ =>
            {
                //一、得到當前位置
                Vector3 curenPosition = this.transform.position;
                //二、得到方向
                if (Input.GetButton("Fire1"))
                {
                    Vector3 moveToward = Camera.main.ScreenToWorldPoint(Input.mousePosition);

                    moveDirection = moveToward - curenPosition;
                    moveDirection.z = 0;
                    moveDirection.Normalize();
                }
                //三、插值移動
                Vector3 target = moveDirection*speed + curenPosition;
                transform.position = Vector3.Lerp(curenPosition, target, Time.deltaTime);
            });
    }
}

總結

這裏記住UniRx兩個方法 Observable.EveryUpdate,Observable.IntervalFrame(這裏還記得之前文章裏提的定時器嗎,這個定時器怎麼樣簡單吧),還有ObservableWWW.GetWWW(上一篇的一個異步加載資源的函數),採用聲明式編程的方式,看看函數名就知道是幹什麼的了吧,還用看文檔或者解釋什麼嗎?

文章內容比較簡單,實現的功能也簡單,函數也簡單,但願大家喜歡。

相關文章
相關標籤/搜索