• 热门专题

Unity基于响应式编程(Reactive programming)入门

作者:伊利丹·怒风??发布日期:2016-11-30 20:23:49
  • ?

    背景

    前有慕容小匹夫的一篇《解构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();
    ??????? //定时器每隔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();
    ??? }

    ??? ///


    ??? /// 角色 帧动画控制
    ??? ///

    ??? private void PlayerAnimation()
    ??? {
    ??????? SpriteRenderer spriteRenderer = GetComponent();
    ??????? //定时器每隔5帧
    ??????? Observable.IntervalFrame(5).Subscribe(_ =>
    ??????? {
    ??????????? currentTexture++;
    ??????????? if (currentTexture >= textureArray.Length)
    ??????????? {
    ??????????????? currentTexture = 0;
    ??????????? }
    ??????????? spriteRenderer.sprite = textureArray[currentTexture];
    ??????? });
    ??? }

    ??? ///


    ??? /// 鼠标控制移动,每帧更新
    ??? ///

    ??? 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(上一篇的一个异步加载资源的函数),采用声明式编程的方式,看看函数名就知道是干什么的了吧,还用看文档或者解释什么吗?

    文章内容比较简单,实现的功能也简单,函数也简单,希望你们喜欢。

延伸阅读:

About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规