Praneat Blog

Wongsathorn Phaisarnrungpana
Game Developer
20 Apr 2020

มาใช้ Reactive Extensions for Unity (UniRx) กันเถอะ !!! Part1


UniRx คืออะไร


UniRx คือการ Re-Implement Reactive Extensions (ReactiveX) ของ .Net Frameswork เพื่อไว้สำหรับใช้งานใน Unity โดยคนที่นำมา Re-Implement เพื่อให้เราได้ใช้งานกันนั้นคือ Yoshifumi Kawai โปรแกรมเมอร์ชาวญี่ปุ่นซึ่งเป็น Microsoft MVP ของ Visual C#. โดยในบทความฉบับนี้ ทางผู้เขียนจะพาท่านผู้อ่านไปเรียนรู้ทำความเข้าใจถึงพื้นฐานและสิ่งที่ควรรู้เกี่ยวกับ UniRx เช่น Reactive Extensions คืออะไร ข้อจำกัดของ Coroutine ที่ทำให้เราควรเปลี่ยนมาใช้ UniRx เพื่อให้ผู้อ่านมีความเข้าใจที่ถูกต้องก่อนที่จะลงลึกไปถึงวิธีการใช้งาน UniRx ใช้งานในแบบต่างๆ


Reactive Extensions คืออะไร


Reactive Extensions หรือ Rx คือ library สำหรับการเขียน asynchronous และ event-based programs โดยใช้ observable sequences และ LINQ-style query operators. เพื่อจัดการกับ asynchronous data ต่างๆ โดย reactive extensions จะ represents Data ในรูปแบบของ observable เพื่อให้ตัว program , game หรือ application ของเรา สามารถ subscribe กับ observable นั้นๆ เพื่อรับ Notification เมื่อมี Data ใหม่ๆเข้ามา


ข้อจำกัดต่างๆของการใช้งาน Coroutine


โดยปกติแล้วเวลาที่เราเขียนโปรแกรมที่เป็น asynchronous เช่น การรับส่งข้อมูลของผู้เล่นกับ Server เรามักจะใช้ Class ที่ชื่อ WWW ควบคู่กับ Coroutine กัน แต่การใช้ Coroutine นั้นจริงๆแล้วไม่ใช่ practice ที่ดีสำหรับการเขียนโปรแกรมที่เป็น asynchronous อันเนื่องมาจากปัญหาต่างๆ
เช่น

  • ไม่สามารถ return result value ได้ เพราะ return type ต้องเป็น IEnumerator
  • ไม่สามารถ handle exception ต่างๆ ได้ เพราะ yield return ไม่สามารถใช้ try-catch ครอบได้


ทำให้การที่เราจะเขียนโปรแกรมที่เป็น asynchronous ภายใน Unity ค่อนข้างลำบากอันเนื่องมาจากเราไม่สามารถหลีกเหลี่ยงการใช้ Coroutine เพราะ Unity เองนั้นเป็น singlethread.


ตัวอย่างการใช้งาน UniRx เบื้องต้น


Ex1. ตัวอย่างการใช้ UniRx แทน Class WWW ของ Unity ในการยิง API

ObservableWWW.Get("http://google.co.jp/")
    .Subscribe(
        x => Debug.Log(x.Substring(0, 100)),
        ex => Debug.LogException(ex));


Ex2. ตัวอย่างการใช้ UniRx แทน Class WWW ของ Unity ในการยิง Chain API แบบ Concatinate

var query = from google in ObservableWWW.Get("http://google.com/")
                            from bing in ObservableWWW.Get("http://bing.com/")
                            select new { google, bing };

var cancel = query.Subscribe(x => Debug.Log(x.google.Substring(0, 100) + ":" + x.bing.Substring(0, 100)));
cancel.Dispose();


Ex3. ตัวอย่างการใช้ UniRx แทน Class WWW ของ Unity ในการยิง API แบบ parallel

var parallel = Observable.WhenAll(
                    ObservableWWW.Get("http://google.com/"),
                    ObservableWWW.Get("http://bing.com/"),
                    ObservableWWW.Get("http://unity3d.com/"));

parallel.Subscribe(xs =>
{
    Debug.Log(xs[0].Substring(0, 100)); // google
    Debug.Log(xs[1].Substring(0, 100)); // bing
    Debug.Log(xs[2].Substring(0, 100)); // unity
});


Ex4. ตัวอย่างการ handler download progress ของ UniRx

var progressNotifier = new ScheduledNotifier<float>();
                progressNotifier.Subscribe(x => Debug.Log(x)); 

ObservableWWW.Get("http://google.com/", progress: progressNotifier).Subscribe();


Ex5. ตัวอย่างการ handle error ของ UniRx

ObservableWWW.Get("http://www.google.com/404")
                    .CatchIgnore((WWWErrorException ex) =>
                    {
                        Debug.Log(ex.RawErrorMessage);
                        if (ex.HasResponse)
                        {
                            Debug.Log(ex.StatusCode);
                        }
                        foreach (var item in ex.ResponseHeaders)
                        {
                            Debug.Log(item.Key + ":" + item.Value);
                        }
                    }).Subscribe();