본문 바로가기
Unity/스크립터블오브젝트

Scriptable Object - 개념: 기본 아이디어, 기존 싱글톤의 장단점 (3)

by PlaneK 2020. 5. 29.

샘플 프로젝트 (Unite - 2017)

소스 : github.com/roboryantron/Unite2017

 

roboryantron/Unite2017

Sample project for Game Architecture with Scriptable Objects from Unite Austin 2017 - roboryantron/Unite2017

github.com

기본 아이디어

아이디어를 크게 도식화 하면 위와 같다. Player와 다른 개체들 사이를 Asset이 이어주고 매개한다.

Player라는 프리팹 개체에는 UnitHealth.cs라는 MonoBehaviour가 정의됐다. 해당 스크립트에는 HP 라는 필드가 선언됐다. 그리고 값으로 해당 에셋이 링크됐다. Player 개체는 플레이 중 HP의 값을 변경한다.

MonoBehaviour는 필요한 프로퍼티를 명시만해주고 값이 정의된 SO의 asset파일을 링크해준다.

Player 개체가 HP의 값을 변경하는 한편, HealthUI개체는 현재 HP값을 토대로 UI에 디스플레이한다. HealthUI의 MonoBehaviour 스크립트는 Player와 똑같은 에셋을 참조하고 있다. 

즉, HP 에셋은 Player가 Write/Read, 다른 개체가 Read 하면서, 공유되고 있다. 이를 공유데이터(Shared Data)라고 한다. 

공유 데이터를 에셋으로 운용하는 것은 기존 방식과 어떤 것이 다르고 무슨 이점을 얻을 수 있을까?

기존 싱글톤 매니저의 장단점

기존 방식처럼 Player와 다른 개체들이 같이 사용하는 공유데이터를 싱글톤 PlayerManager가 매개한다고 생각해보자.

장점

1. 플로우를 이해하기 쉽다.

2. 접근하기 쉽다. 코드 상에서 PlayerManager.Instance만 타이핑하면 된다.

3. 계획하기 편리하다. PlayerManager는 프로젝트에서 사라지지 않고 영속한다. 

단점(Asset으로 극복 가능한)

1. 모듈화 원칙을 깬다.
결론부터 말하면 싱글톤 매니저는 변경에 유연하지 못하고 확장성을 저해한다. MonoBehaviour 스크립트는 디펜던시의 인스턴스가 있는지 없는지 확인해야하며 초기화에 신경써야 된다. 딱히 문제 없어보이지만 '런타임' 중에 해야한다는 것이 맹점이다. 모든 MonoBehaviour 스크립트의 인스턴스는 런타임 때 생성된다. 공든 탑처럼 쌓아올린 초기화 프로세스가 변경에 의해 무너질 수 있어서 변경이 두려워진다. 변경을 반영하기 위해서는 공든 탑을 반드시 점검해야되기 때문에 프로젝트 규모가 크면 클수록 변경 작업의 규모도 덩달아 커진다.

모듈화가 안되면 테스트 비용도 커진다. 새로운 테스트용 씬을 생성하고 그 안에 필요한 모든 오브젝트를 배치해야한다. 필요시 MonoBehaviour 스크립트의 초기화 처리도 수정해야한다.


MonoBehaviour 스크립트의 인스턴스는 런타임 중 생성된다.
따라서 디펜던시를 참조하기 위해 런타임 중에 인스턴스를 초기화해야 한다.
인스턴스를 구하는 2가지 방법이 있다.

첫 번째, Find() 함수를 사용한다.
방법은 간단하지만 Null 참조 없이 안정적으로 할당하려면 Race Condition을 관리해야한다.
개체 수가 많을수록 성능을 요구하며 최적화를 저해한다.

두 번째, 런타임 이전에 Drag&Drop으로 미리 초기화한다.
씬에 미리 필요한 개체들을 씬에 배치하기 때문에 Race Condition이 발생할 우려는 없다.
하지만 디펜던시가 복잡할수록, 또 개체 수가 많을수록 Drag&Drop 작업 부담도 커진다.
이는 프리팹 단에서 해당 디펜던시를 미리 초기화 할 수 없기에 발생한다.

에셋으로 대체하면...

프리팹 단에서 Inspector 작업 한번으로 모든 초기화 작업을 마친다. 런타임 중에 고려하지 않아도 되므로 씬 내 배치 상태에 대한 초기화 프로세스는 고민하지 않아도 된다.

분리된 프리팹을 디펜던시에 의해 합치지 않아도 된다. 디펜던시 인스턴스는 디스크에 직렬화 된 에셋을 참조하면 된다.

변경 시 해당 MonoBehaviour 스크립트와 관련한 ScriptableObject만 수정하면 된다. 디펜던시와 연관된 모든 초기화 작업은 Inspector뷰에서 이뤄지기 때문에 코드 수정이나 씬 배치 점검이 필요없다.

 

2. OOP의 다형성 원칙을 깬다.
MonoBehaviour 스크립트는 사용자로써 인스턴스의 구체적인 타입이 뭔진 몰라도 어떤 형태든 동작하기만을 바란다. 이는 OOP가 지향하는 기본 원칙이다(변경시 사용자의 코드도 수정 범위에 포함돼 변경 비용이 커지는 것을 방지). 하지만 싱글톤 매니저는 Instance의 타입을 사용자가 직접 알고 있다는 점에서 다형성 원칙이 깨져버린다.

단, Singleton의 자식 클래스로 여러 Manager를 두고 MonoBehaviour 스크립트에 Drag&Drop으로 초기화하면 다형화 할 수 있다.

에셋으로 대체하면...

사용자인 MonoBehaviour 스크립트는 PlayerManager라는 추상클래스만 알고있고 실제 인스턴스는 에셋에 따라 디버깅용 PlayerManager, 튜토리얼 PlayerManager로 구체화 된다. 

 

3. 유일한 하나의 인스턴스여야만 한다.
만약 플레이어가 2명이면 싱글톤 매니저의 의미는 무색해진다. PlayerManager를 하나 더 만들거나 시스템을 변경해야 한다.

에셋으로 대체하면... 

PlayerManager와 관련한 ScriptableObject 에셋을 필요한 만큼 추가해주면 된다.

 

댓글