본문 바로가기
Win32 API/기초

Win32 API 기초 : Collider (1)

글: 시플마 2024. 7. 20.

게임에서 반드시 필요한 충돌을 구현해야 합니다.

 

플레이어와 몬스터 오브젝트는 

충돌 범위를 나타내는 Collider( 빨간 부분 )를 갖고 있습니다.

 

Collider는 오브젝트를 따라다니게 되며, 

오브젝트끼리 닿았을 때, Collider가 서로

닿았다고 판단하여 이벤트를 발생시킬 수 있죠.

 

 

여기서 주의할 점이 있습니다.

 

만약 Collider가 닿는 순간 체력이 10

내려간다는 코드를 작성했다고 가정해 봅시다.

 

두 오브젝트가 서로 닿은 상태로

지나가게 되면 매 프레임마다 닿았다는 

이벤트가 발생하여 체력이 10이 아닌 

1000이 내려가는 현상이 발생할 수 있죠.

 

그래서 마치 키 입력을 구현했던 것처럼

닿는 순간에만 체력이 10 내려가게 하고,

계속 접촉된 상태라면 이벤트를 발생시키지

않도록 해야 합니다.

 

이후 두 오브젝트가 다시 떨어지면

닿을 수 있는 상태로 만들고 이 상태에서

접촉 시, 다시 체력이 내려가도록 해야 하죠.

 

 

 


 

 

윈도우 창에 등장하는

모든 것들을 오브젝트라고 합니다.

 

즉 체력 바나 각종 UI도 오브젝트입니다.

 

그래서 모든 오브젝트가

충돌을 인지하도록 구현하면 안되겠죠.

 

플레이어가 UI랑 부딪혔다고 체력이 내려가면 안되니까요.

 

 

오브젝트의 Collider 설계 단계에서 생각해 볼 수 있는 건

아래 그림처럼 Collider가 필요한 오브젝트와 그렇지 않은

오브젝트를 나누어서 코드를 작성하는 것이죠.

 

근데 이렇게 설계를 하면

기획이 수정되어, 체력 바도

충돌할 수 있도록 구현하고 싶다는 얘기가 나왔을 때

수정하기가 상당히 힘들어집니다.

 

 

그래서 Collider 같은 경우에는

클래스를 따로 만들어서 일단 모든

오브젝트가 Collider 멤버를 갖고 있도록 하고

Collider가 필요한 오브젝트만 활성화시킬 겁니다.

 

 

Collider를 구현하기 위한 CCollider 클래스를 만들었습니다.

 

CCollider 클래스의 헤더 파일을 보면 

멤버로 CObject형 포인터 m_pOwner를

갖고 있습니다.

 

Collider 객체가 어떤 오브젝트의

소속인지, 즉 어떤 오브젝트를

따라다니는 Collider인지 나타냅니다.

 

private로 선언이 되었지만

friend 키워드를 통해 CObject 클래스에서

접근이 가능하도록 하였습니다. 

 

왜냐하면 CObject 클래스에서 

CCollider 클래스의 멤버에

값을 대입할 것이기 때문이죠.

 

 

11번째 줄에 있는 finalupdate 함수를 보기 전에

 

CObject 클래스의 변경점을 살펴봅시다.

 

11번째 줄에 CCollider형

포인터 m_pCollider가 추가되었습니다.

 

멤버 m_pCollider는 특정 Collider를

가리키는 멤버입니다.

 

해당 멤버가 있기에 CObject 클래스의

자식 클래스로 생성된 모든 객체는 

Collider를 가질 수 있겠죠.

 

 

20번째 줄에 있는 CreateCollider 함수는

멤버 m_pCollider가 Collider를 가리킬 수 

있도록 하는 함수입니다.

 

CreateCollider 함수의 정의를 보면

 

멤버 m_pCollider가 CCollider형 객체가 존재하는

동적 할당된 공간을 가리키도록 합니다.

 

그리고 CCollider 객체가 갖고 있는

멤버 m_pOwner에 this를 대입합니다.

 

즉 멤버 m_pCollider는 Collider를 가리키고

그 Collider는 자신을 가리키는

오브젝트를 가리키게 되어 서로 가리키는

상태가 되는 것이죠.

 

 

그리고 오브젝트가 제거되면서

소멸자가 호출될 때

 

멤버 m_pCollider가 가리키는

Collider를 제거해 줍니다.

 

물론 가리키는 Collider가

존재하는 경우에만 제거를 해 주어야 하겠죠?

 

 

 


 

 

 

CCollider 클래스에는 멤버 함수인

 

finalupdate 함수가 존재하였죠.

 

 

프로그램이 실행되면서

CCore 클래스로 만든 객체, 즉 코어 객체가

만들어지고 코어 객체는 프로그램을 총괄합니다.

 

코어 객체가 갖고 있는 progress 멤버 함수가 

계속 프레임마다 실행되면서 각 매니저를 업데이트시키죠.

 

그 중 60번째 줄에 있는 CSceneMgr의 update 함수는 

씬에 존재하는 모든 오브젝트의 update 함수를 각각 호출시키죠.

 

그러면 오브젝트 자신이 보유하고 있는

update 함수를 통해 각 오브젝트가 해야 하는 동작을

하게 됩니다. 플레이어의 경우 키 입력을 실시간으로

받아 움직이며 발사체를 발사할 수 있도록 구현했었고,

몬스터의 경우 프레임마다 지정한 방향으로 움직이도록

구현을 했었습니다.

 

 

오브젝트가 움직일 때 Collider도 같이

오브젝트를 따라 움직여야겠죠?

 

37번째 줄에 있는 finalupdate 함수는

이 Collider의 위칫값을 오브젝트 위치에 따라

업데이트해 주기 위해 호출하는 함수입니다.

 

씬이 보유하고 있는 모든 오브젝트에 하나씩 접근하면서

해당 오브젝트가 갖고 있는 멤버 함수 finalupdate 함수를

호출합니다.

 

 

그러면 각 오브젝트는 자신이 보유한 

finalupdate 함수를 호출하면서 아래 코드를 실행하죠.

 

만약 해당 오브젝트의 멤버 m_pCollider가

존재한다면, 즉 해당 오브젝트가 Collider를

가리키고 있다면 그 Collider의 finalupdate 함수를

호출하게 됩니다.

 

 

그러면 Collider 클래스의 finalupdate 함수가

최종적으로 실행이 될 겁니다.

 

아직 코드를 작성하지는 않았지만

여기서 본격적으로 Collider의 위칫값을

업데이트하게 되겠죠.

 

 

이때 중요한 점은 CObject 클래스의 

finalupdate 함수에 선언 부분에 있습니다.

 

씬 매니저를 통해 현재 씬에

존재하는 오브젝트를 관리하죠.

 

이때 씬은 오브젝트를 관리하기 위해

 

Vector형 멤버를 갖고 있고

Vector형 멤버의 각 인덱스는 CObject형

포인터로 구성이 되어 있습니다.

 

즉 오브젝트의 부모 클래스인, CObejct형

포인터로 자식 클래스로 생성한

각종 오브젝트들을 관리하고 있는 것이죠.

 

그래서 finalupdate 함수는 자식 클래스에는

존재할 필요가 없고 부모 클래스 쪽에만

구현이 되어 있으면 됩니다.

 

어떤 자식 클래스로 만든 객체든 간에

부모 클래스가 지닌 finalupdate 함수를

갖고 있기 때문입니다.

 

finalupdate 함수는

부모 클래스에 아래 코드처럼

 

void finalupdate(); 

 

선언해 주면 자식 클래스로 만든

객체라고 해도 알아서 부모 클래스 쪽

finalupdate 함수를 호출하게 될 겁니다.

 

모두 부모 클래스 쪽 함수를 호출하게

할 것이므로 virtual 키워드도 붙여주지 않는 것이죠.

 

 

근데 이러한 구조를 모르는 다른 사람이

부모 클래스 쪽에 존재하는 finalupdate 함수를

보고 자식 클래스에 오버라이딩하여 

프로그램을 실행해 보면 계속 부모 클래스에

존재하는 finalupdate 함수가 실행되어

의문을 가지게 될 겁니다.

( 오버라이딩하여도 부모 클래스에 존재하는 

finalupdate 함수가 실행 이유는 CScene 클래스에서

오브젝트들을 CObject형 포인터로 관리하기 때문이었죠? )

 

사실 이렇게만 하여도 의도대로

오버라이딩을 막기는 했지만

다른 사람이 봤을 때는,

 

"자식 클래스 쪽에 오버라이딩했는데

왜 자식 클래스 쪽 함수가 실행되지 않는 거지?"

 

라는 의문점만 남기게 된 겁니다.

 

 

그래서 아예 virtual 키워드를 붙여 

가상 함수로 만들고 맨 뒤에 final 키워드를

붙여 주면 해당 함수는 자식 클래스에서 

재정의할 수 없게 됩니다.

 

CObject 클래스의 자식 클래스인

CPlayer 클래스에서 finalupdate 함수를

오버라이딩하려고 하자 재정의할 수 없다며

오류가 발생하는 모습입니다.

 

이를 통해 정말 자식 클래스 쪽에서

finalupdate 함수가 오버라이딩되는 것을

막았음을 확인할 수 있고, 다른 사람이 봤을 때도

부모 클래스 쪽 finalupdate 함수만 호출되도록

했다는 것을 확실하게 파악할 수 있게 되었습니다.

 

 


강의 출처 : https://www.youtube.com/watch?v=4l7WDrs2NPI&list=PL4SIC1d_ab-ZLg4TvAO5R4nqlJTyJXsPK&index=25


'Win32 API > 기초' 카테고리의 다른 글

Win32 API 기초 : Collider (3)  (0) 2024.07.28
Win32 API 기초 : Collider (2)  (0) 2024.07.27
Win32 API 기초 : Resource Manager  (0) 2024.07.19
Win32 API 기초 : Resource (2)  (0) 2024.07.18
Win32 API 기초 : Path Manager  (2) 2024.07.14