2023.03.22 - [게임을 만들자/Unity] - 2. UniRx, Observable 메소드
2023.08.14 - [게임을 만들자/Unity] - 3. UniRx, 메시지 제어(Select, Where..)
2023.08.25 - [게임을 만들자/Unity] - 4. UniRx, 메시지 병합(Merge, CombineLastest, Concat)
- UniRx를 이용해 Unity에서 Rx기반의 개발이 가능하다. c#에 Official Rx가 있지만, Unity에서 IL2CPP 호환문제로 제대로 동작하고 있지 않다. UniRx에는 다양한 비동기 함수와 Unity Ui 개발에 편리한 기능들이 있어, 조금 익숙해지면 매우 편리하다는 것을 느낄 것이다. 또한 Linq문법과 비슷해, 코드가 깔끔해보이는 것도 있다.
- UniRx는 https://github.com/neuecc/UniRx 에서 다운받아 설치하면 된다.
- UniRx는 Unity에 최적화된 모델이라, Official Rx보다 빠르다는 소리도 있다.(뇌피셜)
- UniRx를 효율적으로 사용하기에는 MVP 또는 MVVM 개발 모형이 어울린다는 것을 느꼈다.
- Unity에서 MVP또는 MVVM 모델로 개발하는 경우,
- 유저가 View(Button, Image, Text)를 통해, 이벤트를 주고,
- Presenter에서 이벤트를 받아, 처리 후, Model값을 변경한다(서버에 전달 후, 응답 포함).
- Model의 멤버 변수들과 View가 연결(Binding)돼 있어, Model의 변경이 View에 반영된다.
- 여기서 Model과 View의 Binding을 Preserter(or Model-View)에서 정의한다.
- Model의 변화 값을 알기 위해서, Observer패턴을 이용해 구현해야 하는데, Unirx를 이용해 해결한다.
- Unity에서 MVP또는 MVVM 모델로 개발하는 경우,
- 구조 예시
- 위 코드는 영웅(Hero)의 Hp를 Text 뷰를 통해 보여주고 있다. 이때 Hp값 변경되면, 연결된 Text뷰의 문자열 역시 변경된다. 기존 방식으로 코딩 했다면, SetHpText라는 함수를 만들고 세팅했겠지만, Rx에서는 모델과 뷰를 연결해, 변경 즉시 뷰에 반영되도록 한다.
public class HeroModel { private readonly ReactiveProperty<int> level = new(); private readonly ReactiveProperty<int> hp = new(); private readonly string nickName; //닉네임은 한번정하면 안바뀌므로, Reactive일 필요없음 public IReadOnlyReactiveProperty<int> Level => level; public IReadOnlyReactiveProperty<int> Hp => hp; public string NickName => nickName; public void AddHp(int _addHp) { hp.Value += _addHp; } } public HeroInfoPresenter : MonoBehaviour { [SerializeField] TextMeshProUGUI textName; [SerializeField] TextMeshProUGUI textLevel; [SerializeField] TextMeshProUGUI textHp; [SerializeField] Button buttonHpAdd; private HeroModel heroModel; void Start() { //모델의 닉네임를 뷰에 세팅. 옵저버 패턴 아님. textName.text = heroModel.NickName; //모델의 Level과 text뷰를 연결 //AddTo. GameObject가 Destroy되면 해당 구독도 완료됨(Dispose) heroModel .Level .SubscribeToText(textLevel) .AddTo(this); //모델의 hp와 text뷰를 연결 heroModel .Hp .SubscribeToText(textHp) .AddTo(this); //버튼 클릭 시, HeroModel의 AddHp호출 //모델의 hp값이 바뀌고, 위에 연결한 textHp에 반영됨 buttonHpAdd .OnClickAsObservable() .Subscribe(heroModel.AddHp(100)) .AddTo(this) } }
- 위 코드는 영웅(Hero)의 Hp를 Text 뷰를 통해 보여주고 있다. 이때 Hp값 변경되면, 연결된 Text뷰의 문자열 역시 변경된다. 기존 방식으로 코딩 했다면, SetHpText라는 함수를 만들고 세팅했겠지만, Rx에서는 모델과 뷰를 연결해, 변경 즉시 뷰에 반영되도록 한다.
- Subject
- ISubject인터페이스를 살펴보면, IObserver<TSource>, IObservable<TResult> 가 Implemet 돼 있다. Subject객체는 구독 대상이며(IObservable), 구독자들에게 메시지를 발행하는 함수를 가지고 있다(IObserver).
- IOservable를 Subscribe함수를 통해 구독한다.
- IObserver의 OnNext/OnComplete/OnError의 함수로 메시지를 발행한다.
- OnComplete/OnError을 호출하면, 해당 구독은 종료된다. 내부적으로 Dispose()가 호출됨.
- Subscribe 함수는 몇 가지 버전으로 오버로딩 돼 있다. 즉 반드시 OnComplete/OnError 액션을 넣어야 하는 것은 아니다.
Subject<Unit> subject = new(); var dispose = subject.Subscribe(_ => { //OnNext호출시 실행 Debug("OnNext"); }, _ => { //Exception발생 시 실행됨. 생략 가능 Debug("OnError"); }, () => { //OnComplete호출 시 실행. 생략 가능 Debug("OnComplete"); }).AddTo(this);
- Subscribe시, 구독이 시작되며(스트림이 생성됨), IDisposable 객체 반환한다. Dispose()호출 시 또는 OnComplete될 때, 구독이 종료된다.
- Subscribe시 반환한 객체의 Dispose()호출과 Subject의 Dispose()호출은 다르다
- Subscribe시 반환한 객체의 Dispose()호출 시 해당 구독만 종료된다.
- Subject의 Dispose()호출 시, 해당 객체의 모든 구독이 종료된다.
- 주의! OnComplete()호출 되지 않는다.
- Subscribe시 반환한 객체(IDisposal)의 Dispose()호출과 OnComplete 차이
- 반환된 IDisposal의 Dispose시 해당 Subscribe만 종료
- OnComplete는 모든 Subscribe종료
- Subscribe시 반환한 객체의 Dispose()호출과 Subject의 Dispose()호출은 다르다
- AddTo 함수
- Subject객체가 살아있는 동안, Subscribe의 액션에서 어떤 GameObject를 참조하고 있는 경우가 있다. 만약 GameObject가 Destory되고 나서, 내부에서 해당 GameObject를 참조하면 Null이셉션이 발생하게 된다. 만약 어떤 구독이 GameObject가 Destroy될 때 Dispose되게 하고 싶은 경우 AddTo함수를 사용하면 된다. AddTo에 등록된 GameObject가 Destroy되면, 해당 구독은 종료된다.
- ISubject인터페이스를 살펴보면, IObserver<TSource>, IObservable<TResult> 가 Implemet 돼 있다. Subject객체는 구독 대상이며(IObservable), 구독자들에게 메시지를 발행하는 함수를 가지고 있다(IObserver).
- ReactiveProperty
- Model의 멤버 변수를 ReactiveProperty로 만들 경우, 값이 변할 때마다, 메시지를 받을 수 있다. Subject와의 차이점은 구독 시 현재 값을 받을 수 있으며, 초기 값을 설정 할 수 있다.
- ReactiveProperty는 값이 변경 되었을 때만 값이 전달된다. 보통 Model의 멤버를 ReactiveProperty로 하고, 이벤트로 사용할 경우에는 Subject의 OnNext()를 이용하고 있다.
- 예시
ReactiveProperty<int> Level = new(); Level.Subscribe(_level => { //레벨이 변경 될 때마다 호출됨. }) .AddTo(gameObject); //gameObject가 Destroy되면, 이 구독은 종료됨.
'게임을 만들자 > Unity' 카테고리의 다른 글
Unity Android Pip (0) | 2023.07.12 |
---|---|
2. UniRx, Observable 메소드 (0) | 2023.03.22 |
C#, 기기 시간과 인터넷 표준시간 비교하기 (0) | 2022.05.25 |
Unity, Tilemap 검은 선 없애기. (0) | 2021.04.19 |
Unity Simulator 사용하기 (0) | 2021.04.03 |