【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所擅長的。
本文以系列文章中的精靈鼠標移動和序列幀動畫爲基礎,沒有基礎的先參考下傳統實現方式一下兩篇文章
時光煮雨 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(上一篇的一個異步加載資源的函數),採用聲明式編程的方式,看看函數名就知道是幹什麼的了吧,還用看文檔或者解釋什麼嗎?
文章內容比較簡單,實現的功能也簡單,函數也簡單,但願大家喜歡。