// BoxCollider.cpp
case ColliderType::Box:
return _boundingBox.Intersects(dynamic_pointer_cast<BoxCollider>(other)->GetBoundingBox());
// SphereCollider.cpp
case ColliderType::Sphere:
return _boundingSphere.Intersects(dynamic_pointer_cast<SphereCollider>(other)->GetBoundingSphere());
// BoxCollider.cpp
case ColliderType::Sphere:
return _boundingBox.Intersects(dynamic_pointer_cast<SphereCollider>(other)->GetBoundingSphere());
// SphereCollider.cpp
case ColliderType::Box:
return _boundingSphere.Intersects(dynamic_pointer_cast<BoxCollider>(other)->GetBoundingBox());
void BoxCollider::Update()
{
shared_ptr<Transform> transform = GetTransform();
// 중심점 업데이트
_boundingBox.Center = transform->GetWorldPosition() + _center;
// 크기 업데이트
Vec3 scale = transform->GetWorldScale() * _scale;
_boundingBox.Extents = Vec3(scale.x * 0.5f, scale.y * 0.5f, scale.z * 0.5f);
// 회전 업데이트
_boundingBox.Orientation = transform->GetQTRotation();
}
vector<shared_ptr<BaseCollider>> colliders;
const auto& gameObjects = GetGameObjects();
for (shared_ptr<GameObject> gameObject : gameObjects)
{
if (gameObject->GetComponent<BaseCollider>() == nullptr)
continue;
colliders.push_back(gameObject->GetComponent<BaseCollider>());
}
// 최적 축 선택
int bestAxis = 0; // 기본값은 x축
if (colliders.size() > 1)
{
float xSpread = CalculateSpread(colliders, 0);
float ySpread = CalculateSpread(colliders, 1);
float zSpread = CalculateSpread(colliders, 2);
// 가장 분산이 큰 축 선택 (객체 간격이 가장 넓은 축)
if (ySpread > xSpread && ySpread > zSpread)
bestAxis = 1; // y축
else if (zSpread > xSpread && zSpread > ySpread)
bestAxis = 2; // z축
}
float Scene::CalculateSpread(const vector<shared_ptr<BaseCollider>>& colliders, int axis)
{
if (colliders.empty())
return 0.0f;
float minValue = FLT_MAX;
float maxValue = -FLT_MAX;
// 해당 축에서 최소값과 최대값 찾기
for (const auto& collider : colliders)
{
float min = GetMinBound(collider, axis);
float max = GetMaxBound(collider, axis);
if (min < minValue) minValue = min;
if (max > maxValue) maxValue = max;
}
// 분산 계산 (최대-최소 범위)
return maxValue - minValue;
}
// 선택된 최적 축으로 정렬
SortAndSweep(colliders, bestAxis);
void Scene::SortAndSweep(vector<shared_ptr<BaseCollider>>& colliders, int axis)
{
// 지정된 축을 기준으로 충돌체들을 정렬
std::sort(colliders.begin(), colliders.end(), [axis](const shared_ptr<BaseCollider>& a, const shared_ptr<BaseCollider>& b) {
float aMin = 0.0f;
float bMin = 0.0f;
// 각 충돌체 타입에 따라 해당 축의 최소값 구하기
if (a->GetColliderType() == ColliderType::Box)
{
shared_ptr<BoxCollider> boxCollider = dynamic_pointer_cast<BoxCollider>(a);
BoundingOrientedBox box = boxCollider->GetBoundingBox();
// 중심 위치와 크기 값 가져오기
Vec3 center = Vec3(box.Center.x, box.Center.y, box.Center.z);
Vec3 extents = Vec3(box.Extents.x, box.Extents.y, box.Extents.z);
// 해당 축의 최소값 계산
if (axis == 0) aMin = center.x - extents.x; // x축
else if (axis == 1) aMin = center.y - extents.y; // y축
else aMin = center.z - extents.z; // z축
}
else if (a->GetColliderType() == ColliderType::Sphere)
{
// ... 구 충돌체 처리 코드 ...
}
// 두 번째 충돌체에 대해서도 동일한 계산
// ... 생략 ...
// 최소값을 기준으로 정렬
return aMin < bMin;
});
}
// 스윕 과정 - 선택된 축을 기준으로 진행
vector<shared_ptr<BaseCollider>> activeList;
for (int i = 0; i < colliders.size(); i++)
{
float currentMax = GetMaxBound(colliders[i], bestAxis);
for (int j = 0; j < activeList.size(); j++)
{
float activeMin = GetMinBound(activeList[j], bestAxis);
if (activeMin > currentMax)
{
activeList.erase(activeList.begin() + j);
j--;
}
else
{
potentialCollisions.push_back({
activeList[j]->GetGameObject(),
colliders[i]->GetGameObject()
});
}
}
activeList.push_back(colliders[i]);
}
// 추가 필터링 - 다른 두 축에서도 겹치는지 확인하여 후보 더 줄이기
vector<pair<shared_ptr<GameObject>, shared_ptr<GameObject>>> filteredCollisions;
for (const auto& pair : potentialCollisions)
{
shared_ptr<BaseCollider> colliderA = pair.first->GetComponent<BaseCollider>();
shared_ptr<BaseCollider> colliderB = pair.second->GetComponent<BaseCollider>();
// 다른 두 축에서도 겹치는지 확인
bool overlapsOnAllAxes = true;
for (int axis = 0; axis < 3; axis++)
{
if (axis == bestAxis)
continue; // 이미 검사한 축은 건너뜀
if (GetMaxBound(colliderA, axis) < GetMinBound(colliderB, axis) ||
GetMinBound(colliderA, axis) > GetMaxBound(colliderB, axis))
{
overlapsOnAllAxes = false;
break;
}
}
if (overlapsOnAllAxes)
filteredCollisions.push_back(pair);
}
// 최종 충돌 검사 수행
for (const auto& pair : filteredCollisions)
{
shared_ptr<BaseCollider> colliderA = pair.first->GetComponent<BaseCollider>();
shared_ptr<BaseCollider> colliderB = pair.second->GetComponent<BaseCollider>();
if (colliderA->Intersects(colliderB))
{
// 예시(충돌체 제거)
RemoveGameObject(pair.first);
RemoveGameObject(pair.second);
}
}
// BoxCollider.cpp
bool BoxCollider::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::Box:
return _boundingBox.Intersects(dynamic_pointer_cast<BoxCollider>(other)->GetBoundingBox());
}
return false;
}
void Scene::LateUpdate()
{
for (const shared_ptr<GameObject>& gameObject : _gameObjects)
{
gameObject->LateUpdate();
}
Picking();
UIPicking();
// 물리 업데이트 빈도 제어 (예: 60FPS에서 20FPS로)
static float accumulatedTime = 0.0f;
accumulatedTime += TIME.GetDeltaTime();
if (accumulatedTime >= 0.05f) // 20Hz로 제한
{
CheckCollision();
accumulatedTime = 0.0f;
}
}
void Scene::Picking()
{
if (!GUI.isSceneView())
return;
// ImGui 윈도우가 마우스 입력을 캡처하고 있는지 확인
ImGuiIO& io = ImGui::GetIO();
if (io.WantCaptureMouse)
return;
Matrix worldMatrix;
shared_ptr<Camera> cameraComponent = _mainCamera->GetComponent<Camera>();
Matrix projectionMatrix = cameraComponent->GetProjectionMatrix();
Matrix viewMatrix = cameraComponent->GetViewMatrix();
// ...
}
if (INPUT.GetPublicButtonDown(KEY_TYPE::LBUTTON) && !ImGuizmo::IsUsing() && !ImGuizmo::IsOver())
{
firstClickedMouseX = INPUT.GetPublicMousePos().x;
firstClickedMouseY = INPUT.GetPublicMousePos().y;
const auto& gameObjects = GetGameObjects();
float minDistance = FLT_MAX;
picked = nullptr;
// ...게임 오브젝트 순회 및 충돌 검사...
}