본문 바로가기

게임개발

[Unity] [오류 수정] UnityEvent VS Unirx Observable

! 기존 글에는 오류가 있어 수정하였습니다.

Unirx를 도입해 게임을 제작하면서

과연 Observable을 구독(Subscribe)하고 해제(Dispose)하는데 얼마나 메모리와 시간을 잡아먹는지 궁금해졌다.

동시에 UnityEvent비교했을 때 얼마나 차이나는지도 알아보고 싶어졌다.

(Rx 전문가 분의 조언으로 깨달은 점) 들어가기 전에 주의 사항!!

사실 이 둘을 비교하는 것은 결이 많이 달라 무리가 있는 것이 사실입니다.
따라서, 이 글을 읽으면서 Rx의 패러다임을 매우 협소하게 오해할 수 있으니 주의하시길 바랍니다.

비교의 목적은 단순히 기능적으로 콜백메서드를 대체할 수 있는 Rx의 극히 일부분의 사용법에 대한 비교임을 알려드립니다.

아래는 아무 것도 없는 빈 프로젝트의 비어있는 씬에서 빈 게임 오브젝트를 하나 생성해

스크립트를 넣고 Observable과 UnityEvent의 각각의 구독(Subscribe, AddListener)과 해제(Discribe, RemoveListener)를 10만번 돌린 결과이다.

거의 차이가 없는 것을 알 수 있었다. 둘 다 가비지 또한 없었다.

빌드 후 테스트 또한 큰 차이는 없었다.

결론: 무엇이 더 좋다기 보다는 용도와 설계에 맞게 쓰면 된다.


아래는 실제 사용한 코드

더보기
using System;
using System.Collections;
using System.Diagnostics;
using UniRx;
using UnityEngine;
using UnityEngine.Events;
using Debug = UnityEngine.Debug;

public class NewBehaviourScript : MonoBehaviour
{
  private Action<decimal, string, Action<decimal>> action = default;
  private Action<long> testAction = l => { };
  private IObservable<long> testObservable;
  private IDisposable test;
  private UnityEvent test2 = new();

  void Awake()
  {
    testObservable = Observable.EveryUpdate();
    action = (count, identifier, work) =>
    {
      Stopwatch sw = new Stopwatch();
      sw.Start();
      work(count);
      sw.Stop();
      Debug.Log($"{identifier}: {sw.ElapsedMilliseconds}ms");
    };

    action(1, "UnityActionTest", EventSubscribeDescribeTest);
  }

  private IEnumerator Start()
  {
    yield return null;
    for (int i = 0; i < 20; i++)
    {
      yield return null;
    }
    yield return null;

    action(100000, "ObservableTest", ObservableSubscribeDescribeTest);

    for (int i = 0; i < 20; i++)
    {
      yield return null;
    }
    yield return null;

    action(100000, "UnityEventTest", EventSubscribeDescribeTest);

    Debug.Log("TestEnd");
  }

  void ObservableSubscribeDescribeTest(decimal count)
  {

    for (int i = 0; i < count; i++)
    {
      test = testObservable.Subscribe(testAction);
      test.Dispose();
    }
  }

  void EventSubscribeDescribeTest(decimal count)
  {
    for (int i = 0; i < count; i++)
    {
      test2.AddListener(TestAction);
      test2.RemoveListener(TestAction);
    }
  }

  void TestAction()
  {
    return;
  }
}

이 테스트 코드는 <Unity: 코드 블록 성능 측정 방법 (jeon.sh)> 에서 도움을 받았습니다.