충돌 및 물리 시스템 구조


개요

(1) 게임 내 충돌 및 물리 시스템은 CQuadTree 기반 공간 분할 알고리즘 안에서 충돌 채널 및 프로필을 함께 활용하여 불필요한 충돌 검사를 최소화합니다. CCollisionManager충돌 알고리즘을 제공하며, 이를 통해 선별된 충돌체들의 실제 충돌 여부를 검사합니다.


(2) 충돌체 쌍 단위로 충돌을 관리하며, 충돌 이벤트(ENTER/STAY/EXIT)를 전달합니다. 또한 물리 처리가 필요한 경우에는 CPhysicsManagerCRigidbody가 겹침을 해소하고 물리적 반응을 처리하여 안정적인 충돌 처리를 보장합니다.


충돌 및 물리 시스템 흐름도

1. 초기화 흐름도 | 자세히 보기

본 흐름도는 충돌 및 물리 시스템의 초기화 과정을 간단히 보여줍니다.


2. 업데이트 흐름도 | 자세히 보기

본 흐름도는 충돌 및 물리 시스템의 업데이트 과정을 간단히 보여줍니다.


충돌 관련 클래스 목록 및 설명

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();
};


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에서 해당 충돌체 쌍을 제거합니다.

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의 업데이트 함수를 호출합니다.

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):
      • 부모 노드에서 전달된 충돌체가 자신의 범위 안에 있는지 확인합니다.
        • 범위 밖에 있을 경우, 함수를 종료합니다.
        • 범위 안에 있을 경우:
          • 자식 노드가 있으면, 해당 충돌체를 속하는 자식 노드로 전달합니다.
          • 자식 노드가 없으면, 노드에 충돌체를 추가합니다.
            • 충돌체 수가 기준 이상이면 노드를 분할하고, 기존 충돌체를 자식 노드로 이동시킵니다.

물리 관련 클래스 목록 및 설명

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 값이 작으면 무시하도록 설정하는 최소 제곱 길이입니다.
  • 핵심 함수:

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 타입일 경우, 질량을 고려하여 중력, 누적 힘, 가속도, 속도, 위치 업데이트를 순차적으로 처리합니다.

맺는 말

본 충돌 및 물리 시스템은 공간 분할 알고리즘충돌 채널을 기반으로 불필요한 충돌 검사를 줄이고, 충돌 판정과 물리 반응을 안정적으로 처리합니다. 이를 통해 성능을 효율적으로 최적화하고, 충돌 제어를 유연하게 설계할 수 있으며, 다양한 오브젝트 간 상호작용을 보다 원활하게 구현할 수 있습니다.