겨울팥죽 여름빙수

2023.02.24 - [게임을 만들자/Unity] - 1. UniRx 시작, Subject와 ReactiveProperty

2023.08.14 - [게임을 만들자/Unity] - 3. UniRx, 메시지 제어(Select, Where..)

2023.08.25 - [게임을 만들자/Unity] - 4. UniRx, 메시지 병합(Merge, CombineLastest, Concat)

 

 

  •  Observable메소드
    • IObservable객체, 즉 구독 가능한 객체를 만든다.
    • Subject나 ReactivePropery 외에, 프레임워크에서 제공하는 기능들을 구독해야 하는 경우들이 있다. 예를들어 GameObject의 Update, 코루틴 등, 다양한 상황을 구독하고 싶을 때가 있다. Observable클래스에서 이러한 것들을 가능하게 해주는 메소드를 제공하고 있다.

 

  • Observable.Create
    • 메시지를 발행하는 IObservable 객체를 직접 생성한다.
    Observable.Create<int>(_observer =>
    	{
    	    for (var i = 0; i < 10; i++)
    	    {
    	        _observer.OnNext(i);
    	    }
    	
    	    return Disposable.Create(() =>
    	    {
    					//dispose 시 처리
    	    });
    	}).Subscribe(_value =>
    	{
    	    Debug.Log(_value);
    	}).AddTo(this);
    
    • 어떤 함수의 callback결과를 받고 싶을 때 사용한다.
    public async Task ShowInquiry()
    {
    	//showInquiry 함수의 콜백을 기다린다.
    	var result = await Observable.Create<ResultAPI>(_observer =>
    	{
    		AuthV4.showInquiry(_ =>
    		{
    			//showInquiry의 람다 콜백발생 시, OnNext호출
    			_observer.OnNext(_);
    		});
    		return Disposable.Empty;
    	}).Take(1).ToTask(); //OnNext발생 때 까지(take 1이므로 한번만), await한다.
          
    	if (result.isSuccess())
    	{
    		//결과 처리
    	}
    }
    
     

 

  •  Observable.Return
    • 메시지를 1개 발행한다. 구독 즉시 메시지를 발행.
    Subject<bool> OnHeroDead = new();
    
    var observableHeroDead = Observable.Return(false).Concat(OnHeroDead);
    
    • Subject같은 경우, 구독 시에 메시지를 발행하지 않는다. 만약 구독 시 메시지를 발행하고 싶은 경우 사용한다. 위 코드에서 [observableHeroDead] 구독 시, false값을 발행하고, [OnHeroDead]의 메시지 발행을 기다린다.

 

  •  Observable.FromEvent
    • event로부터 IObservable객체를 만든다.
    //이벤트 생성
    Action<int> testEvent = _i =>
    {                
        Debug.Log($"testEvent {_i}");
    };
    
    //이벤트로 IObservable객체를 만든다.
    var testObservable =  Observable.FromEvent<int>(
        _h => testEvent += _h,
        _h => testEvent -= _h);
    
    //이벤트로 만든 IObservable 객체를 구독한다.
    testObservable.Subscribe(_i =>
    {
        Debug.Log($"testEvent in Subscribe {_i}");
    }).AddTo(this)
    
    //이벤트 발생 결과
    //testEvent 10
    //testEvent in Subscribe 10
    testEvent(10);
    
    • 인자가 여러 개 있는 경우
    public delegate void OnComplate (int _value,string _msg);
    public event OnComplate onComplateEvent;
    
    Observable.FromEvent<OnComplate,(int, string)> 
        h => h.Invoke,     //h => (_value,_msg) => h (_value,_msg) 이렇게도 가능
        h => onComplateEvent += h,
        h => onComplateEvent -= h,
        .Subscribe (_ => {...}))
    
    • 위 코드처럼 이벤트 하나를 구독하는 것은 크게 의미가 없어 보인다.
    • 하지만 여러 이벤트를 조합해 사용하는 경우(Merge, Combine, Zip, Concat등) 유용할 듯 하다.
      • 예를 들어, 두 개의 이벤트가 모두 발생할 때까지 기다려야 하는 경우(CombineLastest), 두 이벤트 중 하나라도 발생하는 경우(Merge)

 

  • Observable.Timer
    • 일정 시간 후, 메시지를 발행한다. 인자가 1개인 경우, 메시지를 한번 발행한다. 인자가 2개인 경우 2번째 인자 시간마다, 주기적으로 메시지를 발행한다.
    • Observable.TimerFrame는 일정 프레임 후 메시지를 발행한다. 사용방법은 Timer와 같다.
    //10초 후 메시지 한개 발행
    Observable.Timer(TimeSpan.FromSeconds(10))
        .Subscribe(_ => {...})
    		.AddTo(this);
    
    //10초 후 메시지를 한개 발행하고, 그 후 1초마다 주기적으로 메시지를 발행
    Observable.Timer(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(1))
        .Subscribe(_ => {...})
        .AddTo(this);
    

 

  •  Observable.Interval
    • 일정 시간 간격으로 실행
    • Interval 함수는 메인스레드에서 동작하므로 Thread-Safe하다
    • Observable.Timer와 동일한 기능.
    //x값은 0부터 시작해 점차 증가한다. 아래 코드는 1씩
    Observable.Interval(TimeSpan.FromSeconds(1))
                    .Subscribe(x => Debug.Log(x));
    

 

  •  Observable.FromCoroutine
    • 코루틴을 Observable로 변환한 후 사용.
    • dispose()를 사용하여, 중단할 수 있다.(CompositDisposal 이용)
    CompositeDisposable compositeDisposable = new new();
    void TestCoroutine()
    {
    		//기존에 등록된 구독을 취소
    		compositeDisposable.Clear();
    
    		Observable.FromCoroutine<int>(_observer => TestCoroutine(_observer))
        .Subscribe(_ =>
        {
            Debug.Log(_); //TestCoroutine에서 OnNext할 때마다 실행됨
        })
        .AddTo(compositeDisposable);
    }
    
    IEnumerator TestCoroutine(IObserver<int> _observer)
    {
        var i = 0;
        while (i < 100)
        {
            //do 
            //...
            yield return new WaitForSeconds(1);
            _observer.OnNext(i++);
        }
        _observer.OnCompleted();
    }
    

 

  •  Observable.EveryUpdate
    • GameObject의 Update마다 메시지를 발행한다.
    Observable.EveryUpdate()
      .Where(_ => Input.GetKeyDown(KeyCode.Space))
      .Subscribe(_ => Debug.Log("Space Key Down"));
    

 

  •  ObserveEveryValueChanged
    • 클래스의 멤버 변수 값이 변경 되는 경우, 메시지를 발행한다.
    private CharacterController _controller;
    
    private void Start()
    {
    		_controller = GetComponent<CharacterController>();
    
        _controller.ObserveEveryValueChanged(x => x.isGrounded)
            .Subscribe(isGrounded => Debug.Log($"isGrounded changed to {isGrounded}"));
    }
    
    • 위 코드에서 CharacterController 클래스의 isGrounded멤버 값이 바뀌는 경우 메시지를 발행된다.
      • 내부적으로, 매 루프 마다 값을 체크한다.
        • 파라미터로 Update, FixedUpdate, EndOfFrame를 선택할 수 있다.
profile

겨울팥죽 여름빙수

@여름빙수

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!