일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- DirectX12
- 장치 초기화
- 게임 디자인 패턴
- 노멀 맵핑
- light
- Dynamic Indexing
- C++
- 게임 프로그래밍
- 입방체 매핑
- gitlab
- Frustum Culling
- TCP/IP
- effective C++
- Render Target
- 동적 색인화
- InputManager
- Direct3D12
- 네트워크
- 네트워크 게임 프로그래밍
- gitscm
- 절두체 컬링
- 디퍼드 렌더링
- direct3d
- 조명 처리
- FrameResource
- Deferred Rendering
- DirectX
- 게임 클래스
- 직교 투영
- 큐브 매핑
- Today
- Total
코승호딩의 메모장
[관찰자 패턴] 본문
관찰자 패턴은 자바나 언리얼의 핵심 라이브러리에 들어가 있을 정도로 널리 사용되고 잘 알려진 패턴이다. 관찰자 패턴을 이해하기 위해 하나의 예시를 보자. 기획팀에서 게임에 업적 달성 시스템을 추가해달라고 한다. 특정 몬스터 100마리 죽이기, 다리에서 떨어지기와 같은 기준을 달성하면 배지를 얻을 수 있다고 하자. 업적 종류가 다양하다 보니 깔끔하게 코드를 짜기가 어려울 것이다. 또한 물리 엔진 가운데에 업적 관련 함수를 넣는 것도 맘에 들지 않는다.
이렇게 여러 게임 플레이 요소에서 업적을 발생시키게 된다면 커플링 없이 어떻게 코드를 짤 수 있을까? 이때 쓰는 것이 바로 관찰자 패턴이다. 관찰자 패턴은 어떤 코드에서 무언가 일이 발생했을 때, 누가 받든 상관 없이 알림을 보낸다. 예시로 물체가 바닥으로 추락하는지를 추적하는 중력 물리 코드가 있다고 가정하자. 다리에서 떨어지기 업적을 구현하기 위해 업적 코드를 물리 코드에 밀어 넣는 것은 앞서 봤듯이 맘에 들지 않고 코드가 지저분해진다. 이를 다음과 같이 변경할 수 있다.
void Physics::UpdateEntity(Entity* entity) {
bool wasOnSurface = entity.IsOnSurface();
entity.Accelerate(GRAVITY);
entity.Update();
if (wasOnSurface && !entity.IsOnSurface())
Notify(entity, EVENT_START_FALL);
}
이 코드는 방금 떨어지기 시작했으니 누군지는 모르겠지만 알아서 해달라는 뜻이다. 이 때, 업적 시스템은 물리 엔진이 보낸 알람을 받기 위해 스스로를 등록하고 알림이 오면 그 때, 업적 달성으로 배지를 제공한다. 이렇게 물리 엔진에 덕지덕지 업적 달성 정보를 붙이지 않더라도 업적이 알림을 받고 알아서 업데이트를 할 수 있도록 해준다.
다음으로 관찰자 패턴에 대해서 자세히 살펴보자
class Observer {
public: virtual ~Observer() {}
virtual void OnNotify(const Entity& entity, Event event) = 0;
};
이 Observer 클래스는 다른 객체를 관찰하는 클래스로 인터페이스로 정의된다. 어떤 클래스든 Observer 클래스를 상속 받기만 하면 관찰자가 될 수 있다. 따라서 앞에서 살펴본 업적 시스템에 Observer를 추가하자.
class Achievements : public Observer {
public:
virtual void OnNotify(const Entity& entity, Event event) {
switch (event) {
case EVENT_ENTITY_FELL:
if (entity.IsHero() && mHeroIsOnBridge)
Unlock(ACHIEVEMENT_FELL_OFF_BRIDGE);
break;
//...
}
}
private:
void Unlock(Achievement achievement) { // 업적 잠금 해제 }
bool mHeroIsOnBridge;
};
Achievments 클래스는 관찰자의 인터페이스를 상속 받음으로써 관찰자 역할을 한다. 대상이 보낸 알림을 받으면 처리한다.
대상은 관찰자 목록을 들고 있으며 알림을 누가 받을지를 제어한다.
class Subject {
public:
void AddObserver(Observer* observer) { // 배열에 추가 }
void RemoveObserver(Observer* observer) { // 배열에 제거}
private:
Observer* mObservers[MAX_OBSERVERS];
int mNumObservers;
};
이렇게 대상은 관찰자와 상호작용이 가능하지만 서로 커플링되어 있지 않다. 대상은 관찰자를 여러 목록으로 관리하면서 다른 관찰자들은 서로 다른 관찰자들이 있는지조차 모른다.
class Subject {
//...
protected:
void Notify(const Entity& entity, Event event) {
for(int i = 0; i < mNumObservers; ++i)
mObservers[i]->OnNotify(entity, event);
}
};
대상은 단지 어떠한 행동이 일어났을 때, 자신의 관찰자 목록에 알림을 보낼 뿐이다.
'디자인 패턴 > 게임 프로그래밍 패턴' 카테고리의 다른 글
[경량 패턴] (0) | 2023.09.26 |
---|---|
[명령 패턴] (0) | 2023.09.25 |
[게임 프로그래밍 패턴] (0) | 2023.09.25 |