Game Development, 게임개발/개발

Handling Event, 이벤트 활용하기 - 3, 옵저버패턴, Observer Pattern [Unity]

게임이 더 좋아 2021. 4. 4. 03:27
반응형
728x170

 

우리가 왜 이벤트를 활용해보았을까.

바로 옵저버 패턴이란 것을 익혀보기 위함이었다.

 

옵저버 패턴은 어떤 때에 쓰일까??

다수의 클래스가 하나의 이벤트에 대해 무엇인가 해야할 때 쓰인다.

이전 글에서 플레이어가 죽거나, 적이 죽을 때처럼 

많은 곳에서 참조할 때?? 그정도?

 

알아보자

 


 

옵저버 패턴은 이런 식으로 진행된다.

 

 

하지만 모든 Observer마다 Notify를 하긴 귀찮기에 인터페이스를 이용하기도 한다.

 

 


 

이번에는 목숨이 다 소모되어서 

게임이 끝났을 때를 가정해보자

 

 

게임 상에 남은 Enemy와 아이템은 없어지고 점수는 0으로 초기화되고 GAME OVER 문구가 떠야한다.

여기서 옵저버 패턴을 한 번 써보자.

 

 

그럼 인터페이스 만들어보자

 

 

이제는 Subject에 Private으로 Observer 리스트 만들고

Add, Remove 가능하게 해야겠지.

 

GameSceneController를 Subject로 만들고 

선언하자

 

리스트 만들고 추가,삭제까지 만들었다.

초기화도 해주자.

 

마지막으로 알려줘야하니까

현재 리스트에 들어있는 옵저버들에게 알려주는 메서드를 짠다.

 

 

그럼 게임이 끝날 때는 언제인가를 찾아보니

우주선이 파괴될 때 live가 0이면 게임이 끝나게 된다.

 

**우리는 이전에 아래와 같은 이벤트가 발생한다는 것을 설정해놨다.

 

 

이렇게 되면 게임이 끝났을 때 리스트에 있는 옵저버들에게는 알림이 간다.

 


 

HUDController에 이제 스크립트를 보자.

Notify를 받아야 하므로 인터페이스를 받는다.

 

??? 원래 다중 상속이 안되는 상태라 저렇게 빨간줄이 그어져있다.

 

그렇기에 인터페이스로 받아들이기 위해 VisualStudio가 물어봐준다.

 

그렇게 인터페이스를 받으면

자동으로 인터페이스에서 받아와 정의하라고 메서드를 만들어준다.

 

하지만 그 전에

옵저버는 해당 리스트에 들어가있어야 한다. 그래야 알림을 받을 수 있다.

 

때문에 GameSceneController를 참조한 Start 안에서 

옵저버로 추가시킨다.

 

 

이제 준비는 다 되었으니

실제로 알림을 받았을 때 

어떻게 행동할 것인지 정하면 된다.

 

마침 HUDController에서

이 메서드가 텍스트를 보여주는 메서드다.

GAME OVER를 보여줘야할 것 아니냐

 

추가시켜준다.

 

 

그 다음에는 아이템과 적을 스크린에서 없애야겠지??

 

아이템도 HUDController와 같이 인터페이스를 받는다.

 

해당 알림이 오면 이 컴포넌트를 가지는 오브젝트를 파괴하면 되겠다.

 

 

적도 똑같이 하면된다.

 

 

어 근데???

여긴 왜 AddObserver() 메서드 실행 안해..??

 

사실 이렇게 Spawn 되는 것들은

GameSceneController에서 각 스크립트를 참조하니까.

Spawn과 동시에 옵저버를 할당해주면 된다.

 

 

 

오 그럼 끝나나???

 


 

사실 이렇게만 하면 오류가 생긴다.

 

 

아 이게 왜 뜨냐..?

로그를 띄워보자.

 

 

Null이 떠버렸다.

이건 Observer가 Notify()하기 전에 사라져서 그렇다.

???

 

즉, null에게 Notify()를 하지 않으려면

Observer가 파괴되면 확실히 그 remove를 시켜서 리스트에서도 빼줘야 한다는 말이다.

 

그럼 해당 PowerupController에서 언제 오브젝트가 파괴되는지 한 번 보자

 

2가지 경우가 있다.

 

1. 화면 밖으로 나갔을 때

2. 플레이어가 먹었을 때

 

즉, 1번과 2번 경우 파괴되었을 때 Remove를 확실히 해줬는가??

그게 핵심이다.

 

그래서 새롭게 함수를 짜주기로 했다. 1번,2번의 경우에는 옵저버리스트에서 빼주기로.

 

 

안타깝게도 참조자가 없어서 직접 참조했다.

-> 근데 GameSceneController는 유일해서 괜찮았다. 이미 말했듯이

 

Enemy도 똑같이 리스트에서 빼주는 작업하고 파괴해주자.

 

 

와 이제 진짜 끝????

 


 

음.. 

총알을 쏘고 총알이 없어지기 전에 플레이어가 죽게 되었다.

 

오류가 생겼다.

총알도 사라져야 하나...? ㅎㅎ

 

 

우리는 일전에

이런 스크립트를 만들었다.

저기에는 이벤트가 쓰였다. 

 

PlayerController에는 이렇게 쓰였고

 

 

ProjectileController에는 

이렇게 쓰였다.

 

우리는 옵저버 패턴처럼

이벤트를 그렇게 이용하는 방법을 앞에서 배웠다.

 

근데 오류가 나니까 어떻게 된지 알아보기 위해 잠깐 이렇게 코드를 바꾼다.

 

 

GetInvocationList() 메서드가 이 이벤트를 참조하고 있는 리스트를 반환한다.

 

그래서 총알을 쏴서 밖에 나가지니까

이렇게 로그가 뜬다.

 

해당 이벤트를 참조하는건 EnableProjectile 밖에 없었다.

 

즉, 다시 생각해보면 플레이어 오브젝트는 파괴되어서 사라졌는데

해당 스크립트에 있는EnableProjectile()을 call하려고 하니까 오류가 생기는 것이다.

 

그니까 해당 오브젝트가 파괴되면 이벤트가 참조하고 있는 대상도 빼줘야 부르지 않게 될텐데

 

 

기존 참조 projectile을 이용하기 위해

새로 lastProjectile을 따로 만들어 옮겨두고

 

 

플레이어가 파괴되기 전에 ProjectileOutOfBounds 이벤트에서 해당 메서드 참조를 빼버리는 것이다.

 

이렇게 참조를 해제하지 않아서 나는 오류가 생긴다면..?

우리가 만든 이벤트에 해제하지 않는 이벤트가 하나 더 있었다.

 

 

이놈도 삭제해주지 않으면 오류가 날 수 있을 것이다.

 

그래서 플레이어가 파괴되면 이것도 삭제하기로 했다.

 

 


거의 다왔다.

 

이벤트와 옵저버패턴 비슷하면서도 달라서

이해하기 힘들고 왔다갔다 하면서 하느라 정말 힘들었다.

 

비교를 굳이 하자면

 

약간씩 다르다.

Notify를 어떻게 하는 지도.. 잘 생각해보자

 


 

 

마지막으로 Publisher Subscriber Pattern에 대해 알아보고 끝내겠다.

 

 

옵저버 패턴과 비슷하게 생겼다.

뭔가 다르게 생기긴 했다.

이것의 특징은 Publisher와 Subscriber 간에 의존도가 없다는 사실이다.

Broker를 거치기 때문에 저렇게 낮은 결합도(Coupling)를 유지할 수 있게 되는 것이다.

 

 

 

왜 이야기를 꺼냈느냐..?

그렇게 스크립트를 고쳤어도

아직도 오류가 생긴다.

 

 

그래서 우리는 Broker를 사용하기로 하였다.

새로운 스크립트를 만든다.

아까와 달라진점이 있다면 이벤트가 static으로 선언되었다는거?

 

 

 

우리는 브로커에서 새로운 이벤트를 만들었기 때문에

기존의 이벤트를 삭제해도 된다.

 

 

 

당연히 이 이벤트를 사용했던 것들도 바꿔줘야 한다.

 

 

 

??왜 바로 접근이 가능하지??

static으로 선언해서 그렇다.

 

그럼 이제 메서드를 참조해야겠지??

이렇게 하면 오브젝트가 파괴되거나 비활성화 될 때 알아서 참조가 사라지게 된다.

 

아까 참조해놨던 몇개 것들만 고치면 된다.

 

결국 어떻게 수정했느냐??

 

 

이렇게 수정해서 완료했다.

이제 오류가 없이 잘 실행된다 끝!!!!

 


지금까지

Event -> Observer -> Publisher, Subscriber 까지 비슷하지만 점점 결합도(Coupling)가

낮아지게끔 만들었다.

 

수고했다. 나 .. 이해하느라 고생했네

728x90
반응형
그리드형