Win32 API/기초

Win32 API 기초 : Event Manager (3)

시플마 2024. 8. 16. 16:23

플레이어의 발사체와 몬스터가 충돌하면

몬스터가 화면 상에서 제거되도록 하겠습니다.

 

CObject 클래스에 존재하는 OnCollisionEnter 가상 함수를

 

CMissile 클래스에서 오버라이딩하여 구현합니다.

 

인자로 받은 Collider형 포인터 _pOther가 

가리키는 Collider가 달려있는 오브젝트를 

pOtherObj가 가리키도록 합니다.

 

pOtherObj가 가리키는 오브젝트의 이름이

"Monster"라면 발사체 자기 자신을 제거하도록

DeleteObject 함수를 호출하고 자기 자신을 인자로 넘깁니다.

 

 

DeleteObject 함수는 전역 함수처럼 사용할 수 있는

 

오브젝트 제거를 위한 함수입니다.

 

tEvent형 지역 변수 even의 멤버 eEvent에

DELETE_OBJECT 이벤트로 설정하고

lParam에 인자로 받은 오브젝트의

주솟값을 저장한 후, 값이 설정된 even을

이벤트 매니저의 AddEvent 함수의 인자로 넘깁니다.

 

이러면 이벤트 매니저가 갖고 있는

이벤트 vector에 제거될 오브젝트로

저장이 되겠죠.

 

 

그리고 다음 프레임에 제거 예정인 오브젝트를

모으는 vector에 해당 오브젝트가 저장이 될 것이고

다음 프레임에는 완전히 제거가 됩니다

 

 

 


 

 

 

메모리상에는 발사체 오브젝트가 제거되었겠지만,

화면상에도 사라져야겠지요.

 

 

CScene 클래스의 update 함수의 정의를 보시면

 

m_arrObj에 저장된, 씬에 존재하는

모든 오브젝트를 update해 주고 있습니다. 

 

이때 if 문을 통해 m_arrObj에 저장된

오브젝트가 제거될 상태가 아닌 경우에만

update를 하도록 되어 있습니다.

 

 

또한 render 함수에서도

 

iterator를 통해 m_arrObj에 저장된

오브젝트에 접근하여 제거될 예정이 

아닌 경우에만 render를 해 주고,

m_arrObj에 저장된 오브젝트가

제거될 예정이라면 erase 함수를 통해

m_arrObj에서 제거해 줍니다.

 

 

iterator는 처음에 [0]번 인덱스에 

있는 그룹을 가리키게 됩니다.

 

그리고 [0]번 인덱스가 가리키는,

즉 0번 그룹에 속하는

모든 오브젝트를 순회하며

Dead 상태인지 아닌지 판단합니다.

 

Dead 상태가 아닌 경우 해당 오브젝트를

render하고 iter의 값을 증가시켜

다음 오브젝트를 가리키게 됩니다.

 

만약 Dead 상태라면 해당 오브젝트를

erase 함수를 통해 그룹에서 제거합니다.

 

erase 함수는 제거한 오브젝트의

다음 오브젝트를 가리키는 iterator를 반환하므로

iterator는 다음 오브젝트를 가리킬 수 있게 됩니다.

 

 

update 함수와 render 함수와는 다르게 

 

finalupdate 함수는 그대로

모든 오브젝트의 finalupdate를 진행합니다.

 

finalupdate 함수는 오브젝트가 

갖고 있는 Collider의 값을 update하는 함수이기 때문에

사라질 오브젝트의 Collider라도 마지막까지 update를

진행하여 사라지기 직전까지 충돌 판정을 할 수 있도록 하는 것이죠.

 

 

 

 


 

 

 

 

CPlayer 클래스의 발사체를

생성하는 함수인 CreateMissile에서 

 

발사체의 멤버 함수 SetName 함수를 호출하여 

이름을 설정해 줍니다.

 

 

CMissile 클래스의 생성자에서

 

GetCollider 함수를 호출하여 발사체에

붙어 있는 Collider를 호출한 후, SetColliderSize 함수를

호출하여 Collider의 크기를 설정해 줍니다.

 

 

그런 다음 render 함수에서 

 

component_render 함수를 호출하여 

발사체가 보유한 Collider의 범위가

화면상에 출력될 수 있도록 합니다.

 

 

그리고 시작 씬에서 몬스터와 플레이어의 발사체가

충돌하는지 체크해 줄 것이므로

CScene_Start 클래스의 Enter 함수에

 

콜리전 매니저의 CheckGroup 함수를 호출하고 MONSTER와

플레이어 발사체를 의미하는 PROJ_PLAYER 그룹 타입을 인자로 넘깁니다.

 

또한 39번째 줄에서 몬스터의 이름을 설정해 주고 있습니다.

 

 

그리고 CMonster클래스에서

OnCollisionEnter 함수를 오버라이딩하여 

 

몬스터와 충돌이 발생한 Collider의 오브젝트가

"Missile_Player"라는 이름을 갖고 있으면 

몬스터의 체력이 내려갈 수 있도록 

몬스터의 체력을 나타내는 멤버 m_iHP의

값을 하나 내립니다.

 

그리고 m_iHP의 값이 0 이하가 되면

DeleteObject를 호출하여 자기 자신을 인자로

넘겨 몬스터 자신이 제거될 수 있도록 합니다.

 

 

 


 

 

 

이렇게까지 진행하고 실행하면 정말 오브젝트끼리

충돌을 인지하여 제거가 되는 모습을 확인할 수 있습니다.

 

하지만 문제점이 하나 발생하죠.

 

플레이어의 발사체와 몬스터가 충돌하면

플레이어의 발사체가 제거되지만

여전히 몬스터의 Collider는 빨간색,

즉 계속 충돌 중인 상태로 표시가 될 겁니다.

 

이유는 몬스터가 방금 충돌했던 플레이어의

발사체가 제거되었음을 인지하지 못하고

계속 충돌 중인 상태로 인식하는 것이죠.

 

이러한 문제를 해결하려면

CCollisionMgr 클래스의

CollisionGroupUpdate 함수의 아래 부분을 수정해야 합니다.

 

79 ~ 89번째 줄을 보시면 

if 문을 통해 이전 프레임에서 충돌이 발생했고

현재도 충돌 중인 두 오브젝트 중,

둘 중의 하나라도 제거 예정이라면

OnCollisionExit 함수를 통해

두 오브젝트가 서로 충돌에서 벗어난 

상태로 설정해 주는 겁니다.

 

그게 아니라면 두 Collider는 

계속 충돌 중인 상태로 설정하죠.

 

그리고 iter가 가리키는, 이전 프레임 기준

두 그룹의 Collider의 충돌 상태를

나타내는 map형 객체의 second 값을

false로 설정해 줍니다.

( map형 객체의 second 값에는

두 Collider의 충돌 상태를 저장하고 있습니다.

충돌했다면 true, 충돌하지 않았다면 false를 저장하죠. )

 

몬스터와 플레이어의 발사체가 충돌했었고

현재도 충돌했지만 플레이어의 발사체가

제거될 예정이라면 몬스터와 발사체는

충돌 상태에서 벗어난 상태로 설정하는 것이죠.

 

 

현재 충돌 중이지만 이전에는 충돌한 적이

없다면 92번째 줄에 있는 else 문이 실행되죠.

 

94 ~ 99번째 줄에 있는 if 문은 

충돌이 이제 막 발생한 두 Collider가

모두 제거될 예정이 아닌 경우에만

두 Collider가 충돌이 시작된 상태로

설정해 줍니다.

 

두 Collider 중 하나라도 제거될 예정이라면

아무 일도 발생하지 않습니다.

 

이전에 충돌한 적이 없는 상태에서 어차피 

제거될 예정인 Collider와의 충돌은 아예

충돌로 보지 않겠다는 것이죠.

 

몬스터와 플레이어 발사체가 충돌하기 직전,

갑자기 플레이어의 발사체가 제거될 예정이라면

아무 일도 발생하지 않겠죠?

 

 

103번째 줄에 있는 else 문은

현재 충돌하고 있지 않지만,

이전에는 충돌한 적이 있는 

두 Collider에 대한 코드입니다.

 

106 ~ 111번째 줄에 있는

if 문에 의해 두 Collider는 

충돌에서 벗어난 상태가 되죠.

 

이 부분은 따로 수정할 필요가 없겠죠.

 

 

 


 

 

 

디버깅를 하면서 한 프레임씩

프로그램이 어떻게 동작하는지 살펴보다 보면

갑자기 이상한 값이 나오는 것을 확인할 수 있습니다.

 

예를 들어 디버그를 진행하면서 

플레이어의 발사체를 발사하는 부분을 보았더니

발사체의 위칫값이 엄청 높아진다거나 하는 것이죠.

 

이유는 디버깅을 진행하는 와중에도

프로그램의 시간이 흐르기 때문입니다.

 

 

1초 동안 60번 update를 수행하여 

발사체를 y 방향으로 움직이는 컴퓨터가

디버깅을 통해 10초 동안 멈춘 후

다음 update를 수행하면 밀린 시간만큼

발사체를 y 방향으로 이동시키기 위해

DT가 순간적으로 엄청 올라가게 되겠죠.

 

이러면 디버깅을 통해 값을 확인하기 어렵습니다.

 

이를 해결하기 위해 CTimeMgr 클래스의

 

update 함수를 통해

DT의 값을 업데이트하는

부분을 손봐야 합니다.

 

39 ~ 42번째 줄을 보면

if 문을 통해 DT의 값이 1 / 60보다 

더 커지는 경우 DT의 값을

1 / 60로 설정해 주고 있습니다.

 

디버깅을 통해 아무리 시간이 지연되어도

힌 프레임이 실행되는 시간을 최대 1 / 60( 60FPS )로 보고

이 시간이 이상이 걸려도 DT의 값을 1 / 60 이상으로

높이지 않겠다는 것이죠.

 

 

근데 사실 지연된 만큼 DT를 올리고

빠르게 작동한 만큼 DT를 내려  

다양한 환경에서 일정한 성능이 나올 수 있도록 

하는 것이 맞습니다.

 

디버깅을 할 때 편의를 위해서 

DT의 값이 1 / 60를 넘어가면

1 / 60로 고정해 주는 것이죠.

 

디버깅을 할 때 편의를 위한 코드이므로

디버깅할 때에만 동작하는 코드여야 합니다.

 

#ifdef _DEBUG 구문과 #endif 구문을 통해 

디버그 모드일 때만 두 구문 사이의

코드를 실행하게 되는 것이죠.

 


강의 출처 : https://www.youtube.com/watch?v=X-wF-BVT_zE&list=PL4SIC1d_ab-ZLg4TvAO5R4nqlJTyJXsPK&index=33