DirectX

[DirectX11] Collision과 SphereCollider

유니얼 2024. 3. 13. 22:47
728x90

DirectX 11로 게임 엔진 아키텍처 만들기

게임 개발 과정에서 충돌 감지는 플레이어의 상호작용, 물리 엔진의 정확성, 그리고 게임의 전반적인 몰입감에 핵심적인 역할을 합니다. DirectX11을 사용하여 개발하는 게임 엔진 아키텍처 내에서, 충돌 감지 메커니즘은 게임 월드 내 객체들 간의 상호작용을 정의하고 관리하는 데 중요한 기능입니다. 본 블로그 포스트에서는 충돌 감지의 기본적인 개념과 함께, 구체적인 충돌 감지 방식 중 하나인 SphereCollider의 구현 방법과 예제 코드를 다룰 예정입니다.

 

Result

참고강의 링크:

https://www.inflearn.com/course/directx11-%EA%B2%8C%EC%9E%84%EA%B0%9C%EB%B0%9C-%EB%8F%84%EC%95%BD%EB%B0%98/dashboard

 

[게임 프로그래머 도약반] DirectX11 입문 강의 - 인프런

게임 프로그래머 공부에 있어서 필수적인 DirectX 11 지식을 초보자들의 눈높이에 맞춰 설명하는 강의입니다., [사진][사진] [사진] 게임 개발자는 Unreal, Unity만 사용할 줄 알면 되는 거 아닌가요? 엔

www.inflearn.com

충돌 감지의 기본 개념

충돌 감지(Collision Detection)는 물리적 공간 내에서 두 객체가 접촉하거나 교차하는지를 판별하는 과정입니다. 게임 개발에서 충돌 감지는 물리 엔진의 핵심 구성 요소 중 하나로, 객체 간의 상호작용, 물리적 반응, 그리고 게임 로직의 흐름 제어 등에 사용됩니다.

SphereCollider의 이해

SphereCollider는 가장 단순하면서도 효율적인 충돌 감지 형태 중 하나로, 중심점과 반지름을 가진 구(Sphere)를 사용하여 충돌을 판별합니다. SphereCollider는 계산 비용이 낮고 구현이 간단하여, 실시간 충돌 감지가 필요한 다양한 게임 장르에서 널리 사용됩니다.

DirectX11에서의 SphereCollider 구현

BaseCollider

BaseCollider 클래스는 모든 충돌 감지기의 기본이 되는 추상 클래스입니다. 게임 개발에서 충돌 감지는 게임 오브젝트 간 상호 작용을 결정하는 중요한 부분입니다. 이 클래스는 충돌 감지기의 공통 인터페이스를 정의하며, 다형성을 활용하여 다양한 형태의 충돌 감지기를 구현할 수 있는 기반을 제공합니다.

#pragma once 
#include "Component.h" 

// 충돌 감지기의 타입을 나타내는 열거형
enum class ColliderType
{
	Sphere, // 구체
	AABB,   // 축 정렬 경계 상자(Axis-Aligned Bounding Box)
	OBB,    // 객체 정렬 경계 상자(Object-Oriented Bounding Box)
};

// 모든 충돌 감지기의 기본 클래스
class BaseCollider : public Component // Component 클래스를 상속받음
{
public:
	// 생성자. 충돌 감지기의 타입을 인자로 받음
	BaseCollider(ColliderType colliderType);
	virtual ~BaseCollider(); // 가상 소멸자

	// 레이와의 교차 여부를 판단하는 순수 가상 함수. 하위 클래스에서 구현 필요
	virtual bool Intersects(Ray& ray, OUT float& distance) = 0;
	// 다른 충돌 감지기와의 교차 여부를 판단하는 순수 가상 함수. 하위 클래스에서 구현 필요
	virtual bool Intersects(shared_ptr<BaseCollider>& other) = 0;

	// 충돌 감지기의 타입을 반환하는 함수
	ColliderType GetColliderType() { return _colliderType; }

protected:
	ColliderType _colliderType; // 충돌 감지기의 타입을 저장하는 멤버 변수
};

SphereCollider

SphereCollider 클래스는 구체 형태의 충돌 감지기를 나타내며, 다른 충돌 감지기와의 교차 여부를 판단하거나 레이와의 교차를 판단하는 기능을 포함합니다. Update 메서드는 매 프레임마다 호출되어, 해당 구체 충돌 감지기의 경계 구체 정보(중심 위치와 반지름)를 게임 오브젝트의 현재 상태에 맞게 업데이트합니다. Intersects 메서드는 구체 충돌 감지기가 다른 충돌 감지기나 레이와 교차하는지 여부를 판단하여, 충돌 처리 로직에 필요한 정보를 제공합니다.

#pragma once
#include "BaseCollider.h" // 기본 충돌 감지기 클래스를 포함
#include "Component.h" // 게임 오브젝트의 구성요소를 나타내는 클래스 포함

// SphereCollider 클래스는 BaseCollider로부터 상속받아 구현됨
class SphereCollider : public BaseCollider
{
public:
	SphereCollider(); // 기본 생성자
	virtual ~SphereCollider(); // 소멸자

	// 매 프레임마다 호출되어 충돌 감지기 상태를 업데이트
	virtual void Update() override;

	// 특정 레이와 이 충돌 감지기가 교차하는지 판단
	virtual bool Intersects(Ray& ray, OUT float& distance) override;

	// 다른 충돌 감지기와의 교차 여부를 판단
	virtual bool Intersects(shared_ptr<BaseCollider>& other) override;

	// 이 구체 충돌 감지기의 경계 구체를 반환
	BoundingSphere& GetBoundingSphere() { return _boundingSphere; }

	// 구체의 반지름을 설정
	void SetRadius(float radius) { _radius = radius; }
private:
	float _radius = 1.0; // 구체의 기본 반지름
	BoundingSphere _boundingSphere; // 구체 형태의 충돌 감지를 위한 경계 구체
};
#include "pch.h"
#include "SphereCollider.h" // SphereCollider 헤더 파일 포함
#include "AABBBoxCollider.h" // 축 정렬 경계 상자(AABB) 충돌 감지기 헤더 파일 포함
#include "OBBBoxCollider.h" // 객체 정렬 경계 상자(OBB) 충돌 감지기 헤더 파일 포함

// SphereCollider 클래스의 생성자
SphereCollider::SphereCollider()
	: BaseCollider(ColliderType::Sphere) // 기본 충돌 감지기에 구체 충돌 감지기 타입 지정
{
}

// SphereCollider 클래스의 소멸자
SphereCollider::~SphereCollider()
{
}

// 매 프레임마다 충돌 감지기 상태를 업데이트하는 함수
void SphereCollider::Update()
{
	// 게임 오브젝트의 월드 위치를 경계 구체의 중심으로 설정
	_boundingSphere.Center = GetGameObject()->GetTransform()->GetWorldPosition();

	// 게임 오브젝트의 월드 스케일을 기반으로 경계 구체의 반지름 계산
	Vec3 scale = GetGameObject()->GetTransform()->GetWorldScale();
	_boundingSphere.Radius = _radius * max(max(scale.x, scale.y), scale.z);
}

// 레이와 충돌 감지기의 교차 여부 판단 함수
bool SphereCollider::Intersects(Ray& ray, OUT float& distance)
{
	// DirectXMath 라이브러리의 교차 함수 사용하여 교차 판단
	return _boundingSphere.Intersects(ray.position, ray.direction, OUT distance);
}

// 다른 충돌 감지기와의 교차 여부 판단 함수
bool SphereCollider::Intersects(shared_ptr<BaseCollider>& other)
{
	// 다른 충돌 감지기의 타입을 가져옴
	ColliderType type = other->GetColliderType();

	// 타입에 따라 적절한 교차 판단 로직 수행
	switch (type)
	{
	case ColliderType::Sphere:
		// 다른 충돌 감지기가 구체인 경우
		return _boundingSphere.Intersects(dynamic_pointer_cast<SphereCollider>(other)->GetBoundingSphere());
	case ColliderType::AABB:
		// 다른 충돌 감지기가 AABB인 경우
		return _boundingSphere.Intersects(dynamic_pointer_cast<AABBBoxCollider>(other)->GetBoundingBox());
	case ColliderType::OBB:
		// 다른 충돌 감지기가 OBB인 경우
		return _boundingSphere.Intersects(dynamic_pointer_cast<OBBBoxCollider>(other)->GetBoundingBox());
	}

	// 위의 경우에 해당하지 않는 경우, 교차하지 않는 것으로 처리
	return false;
}

프로젝트 호출

CollisionDemo.h

#pragma once
#include "IExecute.h"
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()->SetWorldPosition(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\\Texture\\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);
	}

	// 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"Cube");
			obj->GetMeshRenderer()->SetMesh(mesh);
			obj->GetMeshRenderer()->SetPass(0);
		}
		{
			auto collider = make_shared<AABBBoxCollider>();
			collider->GetBoundingBox().Extents = Vec3(0.5f);
			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()->GetWorldPosition();
	pos.x -= DT * 1.0f;
	GetTransform()->SetWorldPosition(pos);
}

결론

SphereCollider를 활용한 충돌 감지는 게임 개발에서 물리적 상호작용의 기본을 다루는 방법 중 하나입니다. DirectX11을 사용하는 게임 엔진 아키텍처 내에서, SphereCollider의 간단하면서도 효율적인 구현은 다양한 게임 시스템과 원활하게 통합될 수 있습니다.

반응형