충돌 및 물리 시스템 구조
개요
(1) 게임 내 충돌 및 물리 시스템은 CQuadTree 기반 공간 분할 알고리즘 안에서 충돌 채널 및 프로필을 함께 활용하여 불필요한 충돌 검사를 최소화합니다. CCollisionManager가 충돌 알고리즘을 제공하며, 이를 통해 선별된 충돌체들의 실제 충돌 여부를 검사합니다.
(2) 충돌체 쌍 단위로 충돌을 관리하며, 충돌 이벤트(ENTER/STAY/EXIT)를 전달합니다. 또한 물리 처리가 필요한 경우에는 CPhysicsManager와 CRigidbody가 겹침을 해소하고 물리적 반응을 처리하여 안정적인 충돌 처리를 보장합니다.
충돌 및 물리 시스템 흐름도
1. 초기화 흐름도 | 자세히 보기
/Block-References/01_Game-Projcets/Self-Made-Game-Framework/Source/Collision--and--Physics-System-Initialization-Flowchart_Kor.png)
본 흐름도는 충돌 및 물리 시스템의 초기화 과정을 간단히 보여줍니다.
2. 업데이트 흐름도 | 자세히 보기
/Block-References/01_Game-Projcets/Self-Made-Game-Framework/Source/Collision--and--Physics-System-Update-Flowchart_Kor.png)
본 흐름도는 충돌 및 물리 시스템의 업데이트 과정을 간단히 보여줍니다.
충돌 관련 클래스 목록 및 설명
1. CCollisionManager 클래스 | 저장소 보기
class CCollisionManager { private: CCollisionManager(); ~CCollisionManager(); private: static CCollisionManager* mInst; std::unordered_map<std::string, FCollisionProfile*> mProfileMap; public: bool Init(); bool CreateProfile(const std::string& name, ECollision::Channel myChannel, ECollision::Interaction interaction); bool SetCollisionInteraction(const std::string& name, ECollision::Channel otherChannel, ECollision::Interaction interaction); FCollisionProfile* FindProfile(const std::string& name); public: bool AABBCollision(CBoxCollider* collider1, CBoxCollider* collider2); bool CircleCircleCollision(CCircleCollider* collider1, CCircleCollider* collider2); bool AABBCircleCollision(CBoxCollider* collider1, CCircleCollider* collider2); bool AABBPointCollision(CBoxCollider* collider, const FVector2D& point); bool CirclePointCollision(CCircleCollider* collider, const FVector2D& point); bool AABBPointCollision(const SDL_Rect& rect, const FVector2D& point); public: static CCollisionManager* GetInst(); static void DestroyInst(); };
- 역할:
- 모든 충돌 프로필을 저장하고 관리합니다.
- 충돌 계산 알고리즘을 제공하여 다양한 충돌체의 충돌 여부를 판별합니다.
- 핵심 변수들:
mProfileMap:- 모든 충돌체의 충돌 프로필을 관리하고, 각 충돌체 간 상호작용 설정을 저장합니다.
- 핵심 함수들:
- bool AABBCollision(CBoxCollider* collider1, CBoxCollider* collider2):
- 두 박스형 충돌체의 AABB 겹침을 판정하고, 겹치면 교차 중심점을 양쪽
mHitPoint에 기록합니다.
- 두 박스형 충돌체의 AABB 겹침을 판정하고, 겹치면 교차 중심점을 양쪽
- bool CircleCircleCollision(CCircleCollider* collider1, CCircleCollider* collider2):
- 두 원형 충돌체가 반지름 합 대비 중심 거리로 겹치는지 판정하고, 겹치면 중간점을
mHitPoint로 기록합니다.
- 두 원형 충돌체가 반지름 합 대비 중심 거리로 겹치는지 판정하고, 겹치면 중간점을
- bool AABBCircleCollision(CBoxCollider* collider1, CCircleCollider* collider2):
- 박스와 원의 충돌을 원의 중심과 가장 가까운 박스 테두리까지의 거리로 판정하고, 겹치면 그 최근접점을
mHitPoint로 기록합니다.
- 박스와 원의 충돌을 원의 중심과 가장 가까운 박스 테두리까지의 거리로 판정하고, 겹치면 그 최근접점을
- bool AABBCollision(CBoxCollider* collider1, CBoxCollider* collider2):
2. CSceneCollision 클래스 | 저장소 보기
class CSceneCollision { friend class CScene; public: CSceneCollision() = delete; CSceneCollision(CCamera* camera); ~CSceneCollision(); private: CQuadTree* mQuadTree; std::unordered_map<FColliderPair, EPair::Status> mPairs; public: void Update(float deltaTime); void LateUpdate(float deltaTime); public: void AddCollider(CCollider* collider); void HandleCollision(CCollider* collider1, CCollider* collider2); private: void CleanPairs(); };
- 역할:
- 씬 단위 충돌 관리 시스템으로,
CQuadTree를 이용해 등록된 충돌체 쌍을 관리하고 상태(ENTER/STAY/EXIT)를 추적합니다. - 충돌 이벤트를 각 충돌체에 전달하며, 필요시 물리적 충돌 처리를
CPhysicsManager와 연계하여 수행합니다.
- 씬 단위 충돌 관리 시스템으로,
- 핵심 변수들:
mQuadTree:- 씬의 충돌체들을 공간 분할하여 관리하는
CQuadTree를 가리키는 포인터입니다.
- 씬의 충돌체들을 공간 분할하여 관리하는
mPairs:- 모든 충돌체 쌍(
FColliderPair)과 충돌 상태(EPair::Status)를 관리합니다.
- 모든 충돌체 쌍(
- 핵심 함수들:
- void HandleCollision(CCollider* collider1, CCollider* collider2):
- 두 충돌체의 교차 여부를 판정하고 상태를 업데이트합니다.
- 처음 충돌 시,
OnCollisionEnter(CCollider* other)를 호출합니다. - 지속 충돌 시,
OnCollisionStay(CCollider* other)를 호출합니다.- 물리 상호작용이 있는 경우,
CPhysicsManager를 통해 처리합니다.
- 물리 상호작용이 있는 경우,
- 충돌 종료 시,
OnCollisionExit(CCollider* other)를 호출합니다.
- 처음 충돌 시,
- 두 충돌체의 교차 여부를 판정하고 상태를 업데이트합니다.
- void CleanPairs():
- 충돌체 쌍 중 비활성화된 충돌체가 있을 경우, 충돌 종료 이벤트를 발생시키고,
mPairs에서 해당 충돌체 쌍을 제거합니다.
- 충돌체 쌍 중 비활성화된 충돌체가 있을 경우, 충돌 종료 이벤트를 발생시키고,
- void HandleCollision(CCollider* collider1, CCollider* collider2):
3. CQuadTree 클래스 | 저장소 보기
class CQuadTree { friend class CSceneCollision; private: CQuadTree() = delete; CQuadTree(CCamera* camera); ~CQuadTree(); private: CQTNode* mRoot = nullptr; std::vector<CCollider*> mColliders; public: void Update(float deltaTime); void LateUpdate(float deltaTime); public: void AddCollider(CCollider* collider); private: void UpdateBoundary(); };
- 역할:
- 씬의 모든 충돌체를 관리하고, 쿼드트리의 최상위 노드를 카메라 영역에 맞춰 관리합니다.
- 핵심 변수들:
mRoot:CQTNode의 최상위 노드입니다. 모든 충돌체는 이 노드를 기준으로 분배됩니다.
mColliders:- 씬에 존재하는 모든 충돌체를 보관하는 컨테이너입니다.
- 핵심 함수들:
- CQuadTree(CCamera* camera):
- 매개변수 생성자,
MAX_SPLIT을 기준으로 전체 노드 수를 계산한 뒤, 그 수만큼CQTNode전용 메모리 풀을 생성하고,mRoot에 메모리를 할당하여 초기화합니다.
- 매개변수 생성자,
- void Update(float deltaTime):
mRoot의 영역을 카메라 기준으로 업데이트합니다.- 모든 충돌체를
mRoot에 추가한 뒤,mRoot의 업데이트 함수를 호출합니다.
- CQuadTree(CCamera* camera):
4. CQTNode 클래스 | 저장소 보기
class CQTNode { friend class CQuadTree; public: CQTNode(); ~CQTNode(); private: CCamera* mCamera; CQTNode* mParent; CQTNode* mChilds[4]; std::vector<CCollider*> mColliders; SDL_FRect mBoundary; int mSplitLevel = 0; public: void Update(float deltaTime); void Render(SDL_Renderer* renderer); public: bool HasChild(); bool ShouldSplit(); void Split(); bool IsWithinNode(CCollider* collider); void AddCollider(CCollider* collider); void MoveCollidersToChildren(); void Clear(); };
- 역할:
- 쿼드트리의 개별 노드를 나타내며, 충돌체 저장과 노드 분할 및 재배치를 담당합니다.
- 충돌 계산은 직접 수행하지 않고, 충돌체 간 상호작용을
CSceneCollision에 위임합니다.
- 핵심 변수들:
mColliders:- 해당 노드에 속한 충돌체를 보관하는 컨테이너입니다.
mChilds[4]:- 쿼드트리 분할 시 생성되는 자식 노드 배열입니다.
mBoundary:- 이 노드가 담당하는 공간 영역을 나타냅니다.
- 핵심 함수들:
- void Update(float deltaTime):
- 자식 노드가 있는 경우, 재귀적으로 각 자식 노드의
Update를 호출합니다. - 자식 노드가 없는 경우, 노드 내 모든 충돌체 쌍에 대해, 충돌 설정이 허용된 경우 충돌을 확인합니다.
- 자식 노드가 있는 경우, 재귀적으로 각 자식 노드의
- void AddCollider(CCollider* collider):
- 부모 노드에서 전달된 충돌체가 자신의 범위 안에 있는지 확인합니다.
- 범위 밖에 있을 경우, 함수를 종료합니다.
- 범위 안에 있을 경우:
- 자식 노드가 있으면, 해당 충돌체를 속하는 자식 노드로 전달합니다.
- 자식 노드가 없으면, 노드에 충돌체를 추가합니다.
- 충돌체 수가 기준 이상이면 노드를 분할하고, 기존 충돌체를 자식 노드로 이동시킵니다.
- 부모 노드에서 전달된 충돌체가 자신의 범위 안에 있는지 확인합니다.
- void Update(float deltaTime):
물리 관련 클래스 목록 및 설명
1. CPhysicsManager 클래스 | 저장소 보기
class CPhysicsManager { friend class CScene; private: CPhysicsManager(); ~CPhysicsManager(); private: static CPhysicsManager* mInst; const float CONST_MinMtvLSq = 0.5f; public: void ResolveOverlapIfPushable(CCollider* collider1, CCollider* collider2); private: void ResolveOverlap(CCollider* collider1, CCollider* collider2, bool pushObj1, bool pushObj2); void ResolveAABBOverlap(CBoxCollider* collider1, CBoxCollider* collider2, bool pushObj1, bool pushObj2); void ResolveCircleCircleOverlap(CCircleCollider* collider1, CCircleCollider* collider2, bool pushObj1, bool pushObj2); void ResolveAABBCircleOverlap(CBoxCollider* collider1, CCircleCollider* collider2, bool pushObj1, bool pushObj2); void MoveBy(CCollider* collider, const FVector2D& mtv); public: static CPhysicsManager* GetInst(); static void DestroyInst(); };
- 역할:
- 씬 내 충돌체 간 물리적 상호작용을 관리하며, 상호작용이 물리적 처리 조건을 만족하면
CRigidbody타입에 따라 최소 이동 벡터(MTV)를 계산하여 충돌체를 이동시킵니다.
- 씬 내 충돌체 간 물리적 상호작용을 관리하며, 상호작용이 물리적 처리 조건을 만족하면
- 핵심 변수들:
CONST_MinMtvLSq:- MTV 값이 작으면 무시하도록 설정하는 최소 제곱 길이입니다.
- 핵심 함수:
- void ResolveOverlapIfPushable(CCollider* collider1, CCollider* collider2):
- 충돌체들의 물리 상호작용이 필요할 경우 호출되며, 두 충돌체 중
CRigidbody가 있고 DYNAMIC 타입이며 BLOCK 상호작용을 가진 경우, 해당 충돌체를 밀어냅니다.
- 충돌체들의 물리 상호작용이 필요할 경우 호출되며, 두 충돌체 중
- void ResolveOverlapIfPushable(CCollider* collider1, CCollider* collider2):
2. CRigidbody 클래스 | 저장소 보기
class CRigidbody : public CComponent { public: CRigidbody(); virtual ~CRigidbody(); private: ERigidbodyType mType; float mMass; float mGravityScale; FVector2D mVelocity; FVector2D mAcceleration; FVector2D mAccumulatedForce; private: virtual void Update(float deltaTime) final; virtual void Release() final; public: void AddForce(const FVector2D& force); void AddImpulse(const FVector2D& impulse); const FVector2D& GetVelocity() const { return mVelocity; } ERigidbodyType GetType() const { return mType; } float GetMass() const { return mMass; } void SetVelocity(const FVector2D& velocity) { mVelocity = velocity; } void SetType(ERigidbodyType type) { mType = type; } void SetMass(float mass) { mMass = mass; } private: void ApplyGravity(); void ApplyForces(); void ApplyAcceleration(float deltaTime); void ApplyDrag(float deltaTime); void UpdateObjectPos(float deltaTime); void ClearForces(); };
- 역할:
- 충돌체에 물리적 속성을 부여하며,
CRigidbody의 타입에 따라 물리 반응을 제어합니다. CPhysicsManager가 계산한 이동량을 적용받아 오브젝트를 이동시킵니다.
- 충돌체에 물리적 속성을 부여하며,
- 핵심 변수들:
mType:CRigidbody의 타입(DYNAMIC/STATIC/KINEMATIC)을 나타냅니다.
mMass,mGravityScale,mVelocity,mAcceleration,mAccumulatedForce:- 질량과 중력 영향을 포함한 운동 상태와 누적 힘을 나타내며, 충돌 처리와 물리에 사용됩니다.
- 핵심 함수:
- void Update(float deltaTime):
CRigidbody가 DYNAMIC 타입일 경우, 질량을 고려하여 중력, 누적 힘, 가속도, 속도, 위치 업데이트를 순차적으로 처리합니다.
- void Update(float deltaTime):
맺는 말
본 충돌 및 물리 시스템은 공간 분할 알고리즘과 충돌 채널을 기반으로 불필요한 충돌 검사를 줄이고, 충돌 판정과 물리 반응을 안정적으로 처리합니다. 이를 통해 성능을 효율적으로 최적화하고, 충돌 제어를 유연하게 설계할 수 있으며, 다양한 오브젝트 간 상호작용을 보다 원활하게 구현할 수 있습니다.