TimeProvider
/FrameProvider
도입 등을 통해 이를 개선했다. 이처럼 R3는 “에러 처리 방식 변경(OnErrorResume)”, “Subject 성능 개선(ImmutableArray 회피)”, “구독 누수 방지” 등 기존 Rx/UniRx의 한계를 보완한 기능들을 제공한다.R3 설치 방법: Unity 프로젝트에 R3를 도입하려면 먼저 NuGetForUnity를 설치해야 한다. (예: Unity 패키지 매니저에서 Git URL로 NuGetForUnity를 추가하여 설치한다.)
이후 메뉴 NuGet → Manage NuGet Packages
를 열고 “R3” 패키지를 검색해 설치한다.
마지막으로 Unity 패키지 매니저에서 아래 Git URL을 이용해 R3.Unity
확장 패키지를 설치한다.
<https://github.com/GlitchEnzo/NuGetForUnity.git?path=/src/NuGetForUnity>
<https://github.com/Cysharp/R3.git?path=src/R3.Unity/Assets/R3>
기본 사용 예제: R3의 사용법은 표준 Rx.NET과 거의 같다. 예를 들어, Observable.Timer
와 Observable.Interval
로 간단한 타이머/주기 작업을 만들 수 있다:
using R3;
// 2초 후 메시지 출력
Observable.Timer(TimeSpan.FromSeconds(2))
.Subscribe(_ => Debug.Log("2초 경과"));
// 1초 간격 증가하는 숫자 중 짝수만 출력
Observable.Interval(TimeSpan.FromSeconds(1))
.Select((_, i) => i)
.Where(x => x % 2 == 0)
.Subscribe(x => Debug.Log($"짝수: {x}"));
이 외에도 Rx의 Where
, Select
, TakeUntil
, ForEachAsync
등 대부분의 연산자를 사용할 수 있다. 예를 들어 타이머와 구독 해제를 보여주는 예시는 R3 공식 예제에도 나와 있다.
Unity에서의 사용 패턴: R3는 Unity 엔진과 연동한 편의 기능을 제공한다. 대표적으로 Observable.EveryUpdate()
를 사용하면 매 프레임마다 이벤트를 발생시켜 기존 Update()
루틴을 대체할 수 있다.
// 매 Update마다 실행 (예: 스페이스 키 입력 감지)
Observable.EveryUpdate()
.Where(_ => Input.GetKeyDown(KeyCode.Space))
.Subscribe(_ => Debug.Log("Space 키 누름"));
또한, UI 버튼 등의 이벤트를 스트림으로 처리할 수 있습니다. 예를 들어 Button
컴포넌트가 클릭될 때를 옵저버블로 변환해 구독할 수 있다.
// UI 버튼 클릭 이벤트 처리
myButton.OnClickAsObservable()
.Subscribe(_ => Debug.Log("버튼 클릭됨"));
ReactiveProperty도 R3에서 지원하는 기능입니다. 이는 내부에 값(Value
)을 보유하며 값 변경을 구독 가능한 형태로 제공합니다. 예를 들어:
var hp = new ReactiveProperty<int>(100); // 초기 HP 100
hp.Subscribe(x => Debug.Log($"HP: {x}")); // HP 변화 구독
hp.Value -= 10; // HP 감소 시 알림 발생
위 예제에서 버튼 클릭 시 hp.Value
를 감소시키거나, 다른 조건에서 .Where
, .Select
등을 사용해 로직을 추가할 수 있다. R3는 이처럼 데이터 모델(Observable)과 UI를 연결하여 반응형으로 처리하는 패턴을 쉽게 구현할 수 있게 도와준다.
UniRx와의 차이점 (장단점): R3는 UniRx와 사용법이 유사하지만 내부 구현과 철학이 다르다. 주요 차이점은 다음과 같다:
IObservable<T>/IObserver<T>
인터페이스를 사용하고, 파이프라인에서 에러 발생 시 기본적으로 구독이 해제된다. 반면 R3는 Observable<T>/Observer<T>
추상 클래스를 쓰고 OnErrorResume
를 사용해 에러에도 스트림을 유지한다.Dispose()
시 모든 구독을 안전하게 정리한다. 예를 들어 R3의 모든 Subject
는 Dispose()
되면 OnCompleted
를 호출하여 구독을 자동 해제한다. 이를 통해 메모리 누수를 방지한다.Subject
추가·제거 시 내부적으로 ImmutableArray
를 사용하기 때문에 빈번한 구독 변경 시 많은 할당이 발생할 수 있다. R3는 이 문제를 개선하여 메모리 할당을 줄이고 성능을 높였다.EveryUpdate
, IntervalFrame
등 프레임 단위 연산자를 제공하며, .NET 8
의 TimeProvider
와 별도로 FrameProvider를 도입해 Unity 게임루프에 맞는 시간 제어를 지원한다. (UniRx도 프레임 연산을 지원했지만, R3는 더욱 유연한 시간/프레임 추상화를 적용했다.)async/await
등과 자연스럽게 통합된다.