-
[DirectX11] AABBCollision과 OBBCollision2024년 03월 13일
- 유니얼
-
작성자
-
2024.03.13.:35
728x90DirectX 11로 게임 엔진 아키텍처 만들기
게임 개발에 있어서 충돌 감지는 플레이어와 환경 간의 상호작용, 적과의 전투, 물리 기반의 퍼즐 해결 등 다양한 면에서 중요한 역할을 합니다. 본 블로그 포스트에서는 AABB(Axis-Aligned Bounding Box)와 OBB(Oriented Bounding Box)에 기반한 충돌 감지 방법에 대해 소개하고, DirectX11을 사용하여 이를 어떻게 구현할 수 있는지에 대해 탐색해보겠습니다.
참고강의 링크:
충돌 감지의 기본
충돌 감지는 두 객체가 서로 접촉하거나 겹치는지를 판별하는 과정입니다. 게임 내에서 객체들은 그들의 형태와 위치를 대표하는 경계 박스(bounding boxes)를 통해 간소화하여 표현됩니다. AABB와 OBB는 이러한 경계 박스의 두 가장 일반적인 형태입니다.
AABB(Axis-Aligned Bounding Box)
AABB는 각축에 정렬된 경계 박스로, 회전하지 않는 박스 형태를 가집니다. 각축에 정렬되어 있기 때문에 계산이 단순하고 빠르다는 장점이 있습니다. AABB는 주로 정적인 객체나, 상대적으로 적은 양의 변형을 겪는 객체에 사용됩니다.
#pragma once #include "BaseCollider.h" // 기본 충돌 감지기 클래스를 상속받기 위해 포함 // AABB 충돌 감지기 클래스 정의 class AABBBoxCollider : public BaseCollider { public: // 기본 생성자 AABBBoxCollider() : BaseCollider(ColliderType::AABB) // BaseCollider의 생성자에 ColliderType을 AABB로 설정 { // 필요한 초기화 작업 수행 } // 소멸자 virtual ~AABBBoxCollider(); // 매 프레임마다 호출되어 객체 상태를 업데이트하는 메소드 virtual void Update() override; // 레이와의 충돌 검출 virtual bool Intersects(Ray& ray, OUT float& distance) override; // 다른 충돌 감지기와의 충돌 검출 virtual bool Intersects(shared_ptr<BaseCollider>& other) override; // 경계 상자 가져오기 BoundingBox& GetBoundingBox() { return _boundingBox; } // 현재 충돌 감지기의 AABB 반환 private: BoundingBox _boundingBox; // 내부적으로 사용하는 AABB };
#include "pch.h" #include "AABBBoxCollider.h" #include "SphereCollider.h" #include "OBBBoxCollider.h" // 생성자: 축에 정렬된 경계 상자(AABB) 충돌 감지기로 초기화합니다. AABBBoxCollider::AABBBoxCollider() : BaseCollider(ColliderType::AABB) { // 초기화 코드를 여기에 추가할 수 있습니다. } // 소멸자: 객체가 파괴되기 전에 정리 작업을 처리합니다. AABBBoxCollider::~AABBBoxCollider() { // 정리 코드를 여기에 추가할 수 있습니다. } // 업데이트: 충돌 감지를 위해 충돌 감지기를 준비합니다. 일반적으로 프레임마다 한 번씩 호출됩니다. void AABBBoxCollider::Update() { // 객체의 현재 상태에 기반하여 경계 상자의 크기 또는 위치를 업데이트합니다. } // 교차 (Ray): 레이가 이 충돌 감지기와 교차하는지 여부를 결정합니다. bool AABBBoxCollider::Intersects(Ray& ray, OUT float& distance) { // BoundingBox 클래스의 Intersects 메서드를 사용하여 레이와의 교차를 확인합니다. // 교차가 발생하면 'distance' 매개변수에 교차 지점까지의 거리가 저장됩니다. return _boundingBox.Intersects(ray.position, ray.direction, OUT distance); } // 교차 (Collider): 이 충돌 감지기가 다른 충돌 감지기와 교차하는지 여부를 결정합니다. bool AABBBoxCollider::Intersects(shared_ptr<BaseCollider>& other) { // 다른 충돌 감지기의 타입을 확인하고 적절한 메서드를 호출하여 교차 검사를 수행합니다. ColliderType type = other->GetColliderType(); switch (type) { case ColliderType::Sphere: // 다른 충돌 감지기가 Sphere인 경우, 구와의 교차를 확인합니다. return _boundingBox.Intersects(dynamic_pointer_cast<SphereCollider>(other)->GetBoundingSphere()); case ColliderType::AABB: // 다른 충돌 감지기가 또한 AABB인 경우, 다른 AABB와의 교차를 확인합니다. return _boundingBox.Intersects(dynamic_pointer_cast<AABBBoxCollider>(other)->GetBoundingBox()); case ColliderType::OBB: // 다른 충돌 감지기가 OBB(방향이 있는 경계 상자)인 경우, OBB와의 교차를 확인합니다. return _boundingBox.Intersects(dynamic_pointer_cast<OBBBoxCollider>(other)->GetBoundingBox()); default: return false; } }
OBB(Oriented Bounding Box)
OBB는 객체의 회전을 반영할 수 있는 경계 박스로, AABB보다 더 복잡한 형태의 객체나 동적인 객체에 적합합니다. OBB는 회전을 고려하기 때문에 AABB에 비해 충돌 감지 계산이 복잡하고 무거워질 수 있습니다.
#pragma once #include "BaseCollider.h" // 기본 충돌 감지기 클래스 포함 // 객체 정렬 경계 상자를 나타내는 클래스 class OBBBoxCollider : public BaseCollider { public: // 생성자 OBBBoxCollider() : BaseCollider(ColliderType::OBB) // 기본 충돌 감지기에 OBB 타입 지정 { // OBB 초기화 또는 기타 필요한 설정을 여기서 수행 } // 소멸자 virtual ~OBBBoxCollider(); // 매 프레임마다 충돌 감지기 상태 업데이트 virtual void Update() override; // 레이와 충돌 감지 여부 판단 virtual bool Intersects(Ray& ray, OUT float& distance) override; // 다른 충돌 감지기와의 교차 여부 판단 virtual bool Intersects(shared_ptr<BaseCollider>& other) override; // 경계 상자 가져오기 BoundingOrientedBox& GetBoundingBox() { return _boundingBox; } private: BoundingOrientedBox _boundingBox; // 경계 상자 };
#include "pch.h" // 프리 컴파일 헤더 #include "OBBBoxCollider.h" // OBBBoxCollider 클래스 선언 헤더 #include "SphereCollider.h" // SphereCollider 클래스 선언 헤더 #include "AABBBoxCollider.h" // AABBBoxCollider 클래스 선언 헤더 // 기본 생성자, ColliderType을 OBB로 설정 OBBBoxCollider::OBBBoxCollider() : BaseCollider(ColliderType::OBB) { // 여기에 초기화 코드 작성 } // 소멸자 OBBBoxCollider::~OBBBoxCollider() { // 필요한 경우 리소스 해제 등의 정리 작업 수행 } // 매 프레임마다 호출되어 객체 상태를 업데이트하는 메소드 void OBBBoxCollider::Update() { // 여기에서 OBB 상태를 업데이트하는 로직 구현 // 예: 변환 행렬을 사용하여 위치, 회전, 크기 변경 반영 } // 레이와의 충돌 검출 bool OBBBoxCollider::Intersects(Ray& ray, OUT float& distance) { // _boundingBox를 사용하여 레이와의 충돌을 검출하고 결과를 반환 return _boundingBox.Intersects(ray.position, ray.direction, OUT distance); } // 다른 충돌 감지기와의 충돌 검출 bool OBBBoxCollider::Intersects(shared_ptr<BaseCollider>& other) { // 다른 충돌 감지기의 타입을 확인 ColliderType type = other->GetColliderType(); // 타입에 따라 적절한 충돌 검출 메소드 호출 switch (type) { case ColliderType::Sphere: // 구와의 충돌 return _boundingBox.Intersects(dynamic_pointer_cast<SphereCollider>(other)->GetBoundingSphere()); case ColliderType::AABB: // AABB와의 충돌 return _boundingBox.Intersects(dynamic_pointer_cast<AABBBoxCollider>(other)->GetBoundingBox()); case ColliderType::OBB: // 다른 OBB와의 충돌 return _boundingBox.Intersects(dynamic_pointer_cast<OBBBoxCollider>(other)->GetBoundingBox()); } return false; // 지원되지 않는 충돌 감지기 타입의 경우 false 반환 }
AABB와 OBB의 사용 시나리오
- AABB: 정적 환경, 단순한 형태의 객체, 빠른 충돌 감지가 필요한 경우
- OBB: 동적 객체, 복잡한 형태의 객체, 정밀한 충돌 감지가 필요한 경우
프로젝트 호출
CollisionDemo.h
#pragma once class CollisionDemo : public IExecute { public: void Init() override; void Update() override; void Render() override; private: shared_ptr<Shader> _shader; }; #include "MonoBehaviour.h" class MoveScript : public MonoBehaviour { public: virtual void Update() override; };
CollisionDemo.cpp
#include "pch.h" #include "CollisionDemo.h" #include "RawBuffer.h" #include "TextureBuffer.h" #include "Material.h" #include "SceneDemo.h" #include "GeometryHelper.h" #include "Camera.h" #include "GameObject.h" #include "CameraScript.h" #include "MeshRenderer.h" #include "Mesh.h" #include "Material.h" #include "Model.h" #include "ModelRenderer.h" #include "ModelAnimator.h" #include "Mesh.h" #include "Transform.h" #include "VertexBuffer.h" #include "IndexBuffer.h" #include "Light.h" #include "Graphics.h" #include "SphereCollider.h" #include "Scene.h" #include "AABBBoxCollider.h" #include "OBBBoxCollider.h" #include "Terrain.h" void CollisionDemo::Init() { _shader = make_shared<Shader>(L"23. RenderDemo.fx"); // Camera { auto camera = make_shared<GameObject>(); camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f, 0.f, -5.f }); camera->AddComponent(make_shared<Camera>()); camera->AddComponent(make_shared<CameraScript>()); CUR_SCENE->Add(camera); } // Light { auto light = make_shared<GameObject>(); light->AddComponent(make_shared<Light>()); LightDesc lightDesc; lightDesc.ambient = Vec4(0.4f); lightDesc.diffuse = Vec4(1.f); lightDesc.specular = Vec4(0.1f); lightDesc.direction = Vec3(1.f, 0.f, 1.f); light->GetLight()->SetLightDesc(lightDesc); CUR_SCENE->Add(light); } // Material { shared_ptr<Material> material = make_shared<Material>(); material->SetShader(_shader); auto texture = RESOURCES->Load<Texture>(L"UnityLogo", L"..\\Resources\\Textures\\UnityLogo.png"); material->SetDiffuseMap(texture); MaterialDesc& desc = material->GetMaterialDesc(); desc.ambient = Vec4(1.f); desc.diffuse = Vec4(1.f); desc.specular = Vec4(1.f); RESOURCES->Add(L"UnityLogo", material); } // Terrain { auto obj = make_shared<GameObject>(); obj->AddComponent(make_shared<Terrain>()); obj->GetTerrain()->Create(10, 10, RESOURCES->Get<Material>(L"UnityLogo")); CUR_SCENE->Add(obj); } //{ // auto obj = make_shared<GameObject>(); // obj->GetOrAddTransform()->SetLocalPosition(Vec3(0.f)); // obj->AddComponent(make_shared<MeshRenderer>()); // { // auto mesh = make_shared<Mesh>(); // mesh->CreateGrid(10, 10); // obj->GetMeshRenderer()->SetMesh(mesh); // obj->GetMeshRenderer()->SetPass(0); // } // { // obj->GetMeshRenderer()->SetMaterial(RESOURCES->Get<Material>(L"Veigar")); // } // CUR_SCENE->Add(obj); //} // Mesh { auto obj = make_shared<GameObject>(); obj->GetOrAddTransform()->SetLocalPosition(Vec3(0.f)); obj->AddComponent(make_shared<MeshRenderer>()); { obj->GetMeshRenderer()->SetMaterial(RESOURCES->Get<Material>(L"UnityLogo")); } /*{ auto mesh = RESOURCES->Get<Mesh>(L"Sphere"); obj->GetMeshRenderer()->SetMesh(mesh); obj->GetMeshRenderer()->SetPass(0); }*/ /*{ auto collider = make_shared<SphereCollider>(); collider->SetRadius(0.5f); obj->AddComponent(collider); }*/ { auto mesh = RESOURCES->Get<Mesh>(L"Cube"); obj->GetMeshRenderer()->SetMesh(mesh); obj->GetMeshRenderer()->SetPass(0); } { auto collider = make_shared<AABBBoxCollider>(); collider->GetBoundingBox().Extents = Vec3(0.5f); obj->AddComponent(collider); } /*{ obj->GetOrAddTransform()->SetRotation(Vec3(0, 45, 0)); auto collider = make_shared<OBBBoxCollider>(); collider->GetBoundingBox().Extents = Vec3(0.5f); collider->GetBoundingBox().Orientation = Quaternion::CreateFromYawPitchRoll(45, 0, 0); obj->AddComponent(collider); }*/ CUR_SCENE->Add(obj); } { auto obj = make_shared<GameObject>(); obj->GetOrAddTransform()->SetLocalPosition(Vec3(3.f, 0.f, 0.f)); obj->AddComponent(make_shared<MeshRenderer>()); { obj->GetMeshRenderer()->SetMaterial(RESOURCES->Get<Material>(L"UnityLogo")); } { auto mesh = RESOURCES->Get<Mesh>(L"Sphere"); obj->GetMeshRenderer()->SetMesh(mesh); obj->GetMeshRenderer()->SetPass(0); } { auto collider = make_shared<SphereCollider>(); collider->SetRadius(0.5f); obj->AddComponent(collider); } { obj->AddComponent(make_shared<MoveScript>()); } CUR_SCENE->Add(obj); } } void CollisionDemo::Update() { if (INPUT->GetButtonDown(KEY_TYPE::LBUTTON)) { int32 mouseX = INPUT->GetMousePos().x; int32 mouseY = INPUT->GetMousePos().y; // Picking auto pickObj = CUR_SCENE->Pick(mouseX, mouseY); if (pickObj) { CUR_SCENE->Remove(pickObj); } } } void CollisionDemo::Render() { } void MoveScript::Update() { auto pos = GetTransform()->GetPosition(); pos.x -= DT * 1.0f; GetTransform()->SetPosition(pos); }
결론
AABB와 OBB는 각각의 사용 사례와 요구 사항에 따라 선택될 수 있으며, 게임 내에서의 충돌 감지 방법을 결정할 때 중요한 고려사항입니다. DirectX11과 같은 그래픽 API를 사용하면, 이러한 경계 박스 기반의 충돌 감지 시스템을 효과적으로 구현할 수 있으며, 게임의 실제감과 플레이어의 몰입감을 향상시킬 수 있습니다.
반응형다음글이전글이전 글이 없습니다.댓글