UniRx 예제

DonghoShin·2022년 1월 7일
0

삽질후기

목록 보기
2/4

사내 Unity 프로젝트 리팩토링 중에 UniRx를 사용하면 좋을 상황을 발견했다.
어떤 상황이었냐면...

  1. 특정 Value들을 여러 곳에서 참조하고 그에 따른 행동이 다르다.
  2. 근데 그런 특징때문에 Value의 State 관리가 Update, Trigger, FixedUpdate 등 자주 호출되는 곳에서 if else, switch로 해결을 본 상황이다.
  3. 이 때 Getter와 Setter로 관리하기도 참 애매한 것이 동적으로 관리하기 힘들뿐더러 Get, Set 하는데 너무 큰 오버헤드가 발생할 것이다.

-> 이때 생각난 것이 Observer 패턴이였다.

출처 : 아까 링크한 위키백과

Subject를 옵저버가 구독하고 값의 변화를 통지하면 그에 따른 notify는 옵저버 별로 다르게 세팅할 수 있다. 자 그럼 상황 2번으로 올라가서 값에 변화할 때만 발생시키는 게 코드 관리나 가독성, 퍼포먼스 측면에서 더 좋다는 것을 알 수 있다.(Unity Update, Trigger, FixedUpdate 등은 호출빈도가 굉장히 높기 때문에)

그래서 코드 설명을 조금 하고 나도 참고했던 링크를 달면서 글을 끝맺어볼까 한다.
코드 이름을 보면 굉장히 불편할 수 있는데 예제 만들다보니 그런 것이니 양해부탁드립니다.

크게 2가지 CSharp 파일을 작성했다.

  1. TestUniRX.cs
  2. ObserveScript.cs

우선 TestUniRX를 보자

using UnityEngine;
using UniRx;
public class TestUniRX : MonoBehaviour
{
    public ReactiveProperty<bool> isPopUp = new ReactiveProperty<bool>(false);
    public ReactiveProperty<Language> subLanguage = new ReactiveProperty<Language>(Language.NONE);

    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("Set Obeservation Event");
        
        isPopUp.Subscribe(isOn =>
        {
            Debug.Log($"is {isOn} Do Something");
        });

        subLanguage.Subscribe(language =>
        {
            switch(language)
            {
                case Language.KOREAN:
                case Language.ENGLISH:
                case Language.JAPANESE:
                default:
                    Debug.Log($"Language : {language}");
                    break;
            }
        });

        
    }

    public void SetBool(bool isOn)
    {
        isPopUp.Value = isOn;
    }

    public void SetLanguage(int language)
    {
        subLanguage.Value = (Language)language;
    }    
}

public enum Language
{
    NONE,
    KOREAN,
    ENGLISH,
    JAPANESE,
    
    CountLanguage,
}

UniRx를 보면 Subject와 ReactiveProperty 둘 다 구독할 수 있는 형태지만 Property의 경우는 .Value를 통해 값을 가져올 수 있다는 차이점이 있다. 사용성측면에서 더 낫기도 하고 UniRx를 모르는 사람이 보더라도 사용하기 좀 더 좋을 것 같다는 생각이 들었다. Value에 값을 넣어주면 알아서 통지를 해준다.

그래서 Start에서 bool 타입과 Language(enum)타입의 구독할 수 있는 Reactive Property를 만들었다.
그리고 Subscribe에 통지에 따라 어떤 이벤트를 넣어줄지 작성했다. 예제라 간단하게 Debug 로그를 심었지만 실제는 더 복잡할 것이다.

그리고 다른 곳에서 해당 값에 따른 이벤트를 추가하고 싶다고 하면
ObserveScript.cs를 보면 된다.

using UniRx;
using UnityEngine;

public class ObserveScript : MonoBehaviour
{
    TestUniRX testObject;

    // Start is called before the first frame update
    void Start()
    {
        testObject = FindObjectOfType<TestUniRX>();

        testObject.isPopUp.Subscribe(isOn =>
        {
            Debug.Log($"Another Object {isOn}");
        });

        testObject.subLanguage.Subscribe(language =>
        {
            switch (language)
            {
                case Language.KOREAN:
                case Language.ENGLISH:
                case Language.JAPANESE:
                default:
                    Debug.Log($"Language is {language} Set Something");
                    break;
            }
        });
    }
}

예제기때문에 퍼포먼스는 크게 신경쓰진 않고 TestUniRX 클래스를 찾아서 세팅해주고 만들어두었던 ReactiveProperty에 접근해서 구독이벤트를 추가해준다. 이렇게 하면 실행 결과는 다음과 같다.

Play 실행 직후

Clear한 뒤 Set Bool 버튼을 누르면
추가해둔 이벤트들이 실행된다.
내친김에 Set Language도 누르면
마찬가지로 추가해둔 이벤트들이 실행된다.
동적으로 추가가 되는 만큼 정말 강력한 기능이라고 생각한다.
이걸 Get이나 Set, 기타 함수들에서 작성하려면 짜기 나름이겠지만 상당히 어려운 부분이 분명 존재할 것이다.

그러면 동적으로 추가했으니 제거도 해야 의미가 있을 것이다.
ReactiveProperty가 IDisposable을 상속받기 때문에 Dispose 함수를 통해 해제할 수는 있지만 통째로 해제되기 때문에 부분삭제 방법은 한번 더 찾아봐야겠다...

필자의 예제 만들어둔 주소다. TestScene을 실행시키고 SetBool과 SetLanguage 버튼을 눌러보라 간단하게 만든 것이기 때문에 Language의 경우 enum 범위를 안넘어가도록 세팅하면 된다.
https://github.com/YeonduBori/UnityTest/tree/main/UniRxPractice/Assets

참조 : https://skuld2000.tistory.com/31
반응형 프로그래밍 및 UniRx 전반에 대해 설명을 매우 잘해두셔서 도움을 많이 받았다.

profile
NdotLight 클라이언트 프로그래머

0개의 댓글