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


系列目錄

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

【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庫。

當然一下列出這么多新概念性的東西,作為新手必然理解起來有困難的,當然我也希望你是天賦迥異的人。這里列出幾點,如果你不了解,請自行去學習或者復習,回來在看也不遲。

1、Linq基礎,Linq的本質及與傳統命令式編程的區別和優點

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

3、什么是響應式編程

4、什么是觀察者模式

5、軟件編程中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(_ =>
         {
             //1、獲得當前位置
             Vector3 curenPosition = this.transform.position;
             //2、獲得方向
             if (Input.GetButton("Fire1"))
             {
                 Vector3 moveToward = Camera.main.ScreenToWorldPoint(Input.mousePosition);

                 moveDirection = moveToward - curenPosition;
                 moveDirection.z = 0;
                 moveDirection.Normalize();
             }
             //3、插值移動
             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(_ =>
            {
                //1、獲得當前位置
                Vector3 curenPosition = this.transform.position;
                //2、獲得方向
                if (Input.GetButton("Fire1"))
                {
                    Vector3 moveToward = Camera.main.ScreenToWorldPoint(Input.mousePosition);

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

總結

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

文章內容比較簡單,實現的功能也簡單,函數也簡單,希望你們喜歡。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM