사실 나는 새로운 시스템 혹은 라이브러리의 도입/사용을 굉장히 꺼려하는 타입이다. NGUI보다 UGUI를 선호하며, IDE는 확장기능 없이 순정만을 선호하고, 단축키를 바꾸는 일도 드물다. 코딩에 그렇게나 편하다던 해피해킹 키보드는 Fn키 사용에 벽을 느끼고 2주만에 다시 되팔아버렸고. 어떻게 보면 개발자로선 아주 못된 마음가짐일지도 모른다. 조금의 항변을 해보자면 확장/기능성 라이브러리에 익숙해지면, 그 라이브러리 없이는 코딩을 할 수 없다. 또는 개발 도중에 라이브러리가 도입되면 소스분석에 시간이 더 들어간다, 뭐 그런 이유 때문이리라.

 

 UniRx에 대한 배경과 기초설명은 조금만 찾아보면 아주 쉽게 찾아볼 수 있다. 실제로 현업에선 17년도부터 알음알음 써왔던것 같기도 하고. 이 포스트에선 UniRx의 사용방법이나 기초설명 보다는 사용기/예제에 초점을 맞추고자 한다. 

 

 실제로 UniRx는 굉장히 편리하지만 내가 느낀 단점도 있다. 나름의 학습 코스트가 높고, 개념적으로도 어렵다. 기존의 OOP 프로그래밍 패러다임과 워낙 다르다보니 바로 와닿지 않는 부분도 많다. Observable과 Stream을 사용한 비동기의 개념이다. 유니티 UI 내 OnClick 메소드는 잊어버리는게 나을 정도고, 이를 위해서는 설계 단계에서부터 UniRx를 포함해 설계를 해야하는게 좋을 정도이다. 즉, UniRx는 굉장히 편리한 라이브러리가 아닌 언어 확장이라고 생각할 필요가 있다. 몇 가지 예제를 기록해 둔다.

 

1. 더블클릭 판정 ( Observable / Operator / Subscribe )

 UniRx의 핵심이 되는 3가지 개념이고, 이를 한번에 잘 나타내는 예제는 바로 더블클릭 판정이다.

더블클릭 판정 예제

 생성된 Observable 객체는 유니티 MonoBehaviour의 매 프레임마다 값을 관찰하는 스트림을 생성한다. Where 람다절에서 매 Update 함수 호출시 마우스 왼쪽 버튼 클릭 여부를 감시하고, clickStream 스트림으로 값을 바로 흘려 보내지 않고 버퍼에다 저장한다. 즉, 버퍼에 모인 값의 개수가 2개 이상인 경우를 체크하는데, Throttle 밸브를 250ms 동안 열어두고 있다는 뜻. 그리고 최종적으로 Subscribe 절에서 감지 성공시 이벤트를 넣어주면, 끝난다.

 

 

2. Update() 없애기

 아마 Update 쓰기 좋아하는 유니티 개발자 없을거라 생각한다. 프레임마다 호출되는 메소드는 사용 부담이 너무나 크다. 대부분 코루틴을 활용한 비동기 처리를 던지거나, 실시간 변수감지를 탐지하는 무언가의 OOP 확장 메소드를 도 사용하고 여간 불편한게 아니다. 그러다보면 또 if-else절로 도배가 될 것이도, 가독성에도 좋지 않고 가정이 무너지고 사회가 무너지고...

무려 Awake/Start 단에서 한 번만 등록해두면 끝난다!!

 이걸 보면 LINQ를 몰라도 '아 LINQ는 이렇게 쓰는거구나' 감이 올 정도로 굉장히 간단명료해진다. (ㅋㅋㅋㅋㅋㅋ) 저렇게 캐릭터의 유한상태머신을 컨트롤하는 경우에도 매 Input값을 감지하기 위해 Update문을 돌릴 필요가 전혀 없어진다. 단지 Observe하는 Stream을 열어둘 뿐이므로, 자원 관리에도 탁월하다. 그리고 무엇보다, 병렬적으로 쉽게 알아볼 수 있게 된다. 즉, 기능 추가, 제거, 변경이 훨씬 용이해지게 되고 코드가 선언적이 되며 처리 의도를 쉽게 알아볼 수 있다. 이 얼마나 깔끔한가!!!

 

 단, Observable.EverUpdate에는 한가지 주의점이 있는데 유니티 MonoBehavior가 Destroy될 때에 OnCompleted가 발생되지 않는다는 것. 위에 예제로 적어둔 UpdateAsObservable과는 좀 다른 녀석인데, 해당 스크립트가 있는 gameObject가 파괴되면 해당 오브젝트를 참조하고있는(transform 프로퍼티라던가..) 녀석들이 죄다 Null이 되며 예외가 발생한다.

 

3. Reactive Property 활용

 

 이 Reactive Property의 가장 두드러지는 장점은, 유니티 에디터 자체에서 직렬화Serializable이 가능하단 뜻이다. 당연히 Inpector에 표시되고, 편집 가능하고, 커스텀이 가능하다. 당연히 Stream을 제어하므로 즉각적인 값 반영도 이루어진다. 캐릭터의 HP, 남은 시간 등등 값이 바뀜에 따라 분기가 달라지는 코드를 수동으로 작성하지 않고 ReactiveProperty를 쓰면 아주 손쉽게 Stream을 먹일 수 있다!

+ Recent posts