2022년 12월 28일 수요일

Unity Zenject 사

Unity Zenject 사

Unity DI 어셋 Zenject 사용

의존성 주입을 보다 쉽게

클래스나 오브젝트간에 서로 참조하는 의존관계를 약하게 하여 프로그램의 유연성과 테스트커버리지를 높이기 위한 방법은 여러가지가 있다.
물론 직접 Interface등을 설계하여 만들수도 있지만, Zenject 같은 DI 라이브러리를 사용하면 좀더 편하게 의존관계를 설정할수있다.
아키텍쳐관점에서 본다면, VIew-Model-Repository 가 서로 강하게 연결되어있으면 코드의 수정이나 재활용시에 매우 손이 많이 가기 때문에 최대한 인터페이스화 하여 설계하여야한다.

Context

SceneContext

SceneContext는 Hiracky 메뉴에서 마우스오른쪽 버튼을 눌러서 생성할수 있다.
화면에 SceneContext는 Scene에서 필요로할 이런저런 것들(Context)을 정의해놓는 곳이다.
일단 뭔지 함보자.
1.Hierachy -> 오른쪽 마우스 -> Zenject -> SceneContext 로 Scene에서 사용할 Context 객체를 만든다.
enter image description here

  1. SceneContext객체를 선택하면 Inspector속성에 스크립트들을 지정하라는 곳이 보인다.
    enter image description here

  2. Unity 의 MonoBehavior가 시작될때, 즉 화면에 씬이 로딩되는 순간에 뭔가를 처리해야 할때는 Mono Installers영역에 해당 스크립트를 넣어주면된다. 지금 예제에서는 모노가 시작될때 디버그 영역에 문자를 출력할 것이기 때문에 해당스크립트를 작성하고 Mono Installers에 지정할것이다.

  3. 적당한 곳에 스크립트(MyInstaller.cs)를 만들고 아래처럼 입력하자.
    아래그림처럼 만든 스크립트를 Mono Installer에 넣어야 도니다.
    enter image description here

public class MyMonoInstaller : MonoInstaller  
{  
  public override void InstallBindings()  
 {  
   Container.Bind<string>().FromInstance("Hello!");  
   //간단히 설명하자면  Container 에서  string형식으로 주입을 요청하는 곳에서는 "Hello" 인스턴스를 생성해서 돌려주도록 Bind를 하자. 라는의미이다.
   Container.Bind<Greeter>().AsSingle().NonLazy();  
   //마찬가지로 Container 에서  Greeter 클래스 형식으로 주입을 요청하는 곳에서는 컨테이너상에 오로지 한개만 존재하는 (AsSingle) 인스턴스를 생성해서 돌려주도록 Bind를 하자. 라는의미이다. 
   //NonLazy가 붙어있으므로 MyMonoInstaller 스크립트가 SceneContext에서 로드됨과 동시에 Greeter 객체가 생성된다.
 }
}  
  
public class Greeter  
{  
  public Greeter(string message)  
 {  
   Debug.Log(message);  
 }
}
  1. 위에서 만든 스크립트를 적절한 GameObject나 또는 SceneContext등에 붙여주고 나면, SceneContext의 Mono Installers 부분에 해당 객체목록이 보인다.
    enter image description here
  2. 플레이 해보면 "Hello"문자열이 Console에 출력된다. ( 출력이 안되었다면, 스크립트오류를 확인하거나, 위의 Auto Run 이 체크되어있나를 확인한다.)
  3. 정리해 보자면 씬을 플레이 하는 순간 SceneContext 객체에 지정된 스크립트들이 실행되는데, 이때 MonoBehavior를 상속받아 실행되는 Mono Installers 부분에서는 MyMonoInstaller에 정의된 스크립트를 실행하도록 하는것이다.
  4. 정리하자면
    1. SceneContet를 Hierarchy에 추가한다.
    2. MonoInstaller를 확장하는 새로운 스크립트를 만들어서 현재 씬에서 주입이 필요한 모델 또는 객체 등을 Zenject의 주입패턴(Bind)에 맞게 주입을 하는 내용을 입력한다.
    3. 해당객체를 각각의 스크립트 등에서 new선언없이 사용한다.
  5. 조금 바꾸어 보자.
    Scene에 아무 객체나 만들고, 아래와 같은 스크립트를 지정한다.
public class monoin : MonoBehaviour  
{
	[Inject]  Greeter _greeter;  
	[Inject] string msg;  
	private void Start()  
	{  
	  _greeter.Greeting(msg);  
	}
}
// Greeter도 수정해보자.

public class Greeter  
{  
  public Greeter(string message)  
 {  
   Debug.Log(message);  
 }
   
public void Greeting(string say)  
{  
  Debug.Log(say);  
}
}

위에서 string 타입의 값은 무조건 "Hello!"를 되돌려 주게 되어있으므로, Hello가 Greeter생성시 한번, Greeting호출시 한번 더 호출된다.

  1. 인터페이스를 Bind 하도록 바꾸어 보자.
    인터페이스를 공유하는 두개의 클래스를 zendject의 bind id에 따라 구분해서보여주자.
// MonoInstaller에 지정해준 스크립트는 다음과 같다.
public class MyMonoInstaller : MonoInstaller  
{  
  public override void InstallBindings()  
 {  Container.Bind<string>().FromInstance("Hello!");  
  Container.Bind<IGreeter>()  
 .WithId("FromGreeter")  
 .To<Greeter>()  
 .AsSingle()  
 .NonLazy()  
 .IfNotBound();  
  Container.Bind<IGreeter>()  
 .WithId("FromMyGreeter")  
 .To<MyGreeter>()  
 .AsSingle()  
 .NonLazy()  
 .IfNotBound();  
 }}  
  
public class Greeter : IGreeter  
{  
  public string Greeting(string say)  
 {  
  return say + " ^_^";  
 }}   
public class MyGreeter : IGreeter  
{  
  public string Greeting(string say)  
 {  return say+ " @_@ My";  
 }}
 
// 호출쪽에서는 다음과 같다.
[Inject(Id = "FromGreeter")] IGreeter _greeter;  
[Inject (Id = "FromMyGreeter")] IGreeter _myGreeter;  
[Inject] string msg;  
private void Start()  
{  
  Debug.Log(_greeter.Greeting(msg));  
  Debug.Log(_myGreeter.Greeting(msg));  
  //Use When SetActive(true) and enabled = true  
}
물론 두개의 다른(^^, @@) 가 출력된다.

사례1. 두개의 씬을 이동할때 공유된 Model을 이용하자.

씬이 바뀌어도 유지되야 되는 변수, 재화,에너지 등이 있다. 이를 씬을 로드할때마다 불러오고/검증하는것은 불필요한 리소스낭비이다.
따라서 두개의 씬이 같은 모델을 공유하고 같은 데이터를 참고하도록 하면 되는 데 이때 Zenject의 LoadScene의 extraBinding을 이용하여 씬이동시에 공유할 인스턴스를 넘겨주면 된다.

1. 먼저 두개의 씬 A,B를 만들자

각 씬에는 서로 씬이동하는 버튼과 텍스트, 공격 버튼을 배치해서 각 씬에서 공격버튼을 눌렀을때 텍스트가 변경되도록 하자.
씬을 이동하더라도 텍스트의 숫자는 같은 데이터를 참고하므로 초기화 되지 않도록 한다.

2. 각 씬에 SceneContext를 추가한다. Zenject를 이용하려면 추가해야한다.

0 comments:

댓글 쓰기