전체 코드 : https://github.com/HyangRim/DirectX11-Engine-Client
GitHub - HyangRim/DirectX11-Engine-Client
Contribute to HyangRim/DirectX11-Engine-Client development by creating an account on GitHub.
github.com
Scene 시스템 개발기
1. Scene 아키텍쳐 개요
1.1 전체 Scene 구조
Scene (기본 클래스)
├── StartScene (게임 시작 화면)
├── CharacterSelectScene (캐릭터 선택)
└── LumiaIsland (실제 플레이 Scene)
SceneManager (Scene 전환 관리)
└── SceneObjectManager (객체 분리 관리)
├── 일반 객체 (unordered_set)
├── UI 객체 (unordered_set)
└── QuadTree 기반 피킹 시스템
1.2 Scene 전환 시스템
Scene 전환은 SceneManager를 통해 안전하게 처리
class SceneManager {
public:
template<typename T>
void ChangeScene(shared_ptr<T> _scene) {
m_nextScene = _scene;
m_sceneChangeRequested = true; // 즉시 전환하지 않고 플래그만 설정
}
void ProcessSceneChange() {
if (m_nextScene == nullptr) {
m_sceneChangeRequested = false;
return;
}
// 현재 Scene 안전하게 정리
if (m_curScene) {
m_curScene->SetDestroying(true); // 소멸 플래그 설정
// 모든 GameObject들의 OnDestroy 호출
auto& objectManager = m_curScene->GetObjectManager();
for (auto& obj : objectManager->GetObjects()) {
if (obj) obj->OnDestroy();
}
for (auto& obj : objectManager->GetUIObjects()) {
if (obj) obj->OnDestroy();
}
}
// 새로운 Scene으로 안전하게 전환
m_curScene = m_nextScene;
m_nextScene = nullptr;
m_sceneChangeRequested = false;
if (m_curScene) {
m_curScene->Start();
}
}
};
2. 멀티스레드 로딩 시스템 구현
백그라운드 로딩의 필요성
게임에서 대용량 모델, 텍스쳐, 사운드 파일을 로딩할 때 메인 스레드가 블록되면 게임이 멈춘 것처럼 보인다. 이를 위해 백그라운드 로딩을 구현했다.
멀티 스레드 로딩시스템 설계
class LumiaIsland : public Scene
{
private:
// 멀티스레드 관련 변수
CRITICAL_SECTION m_loadingCS;
HANDLE m_loadingThread;
atomic<bool> m_loadingComplete{false};
// 메인 스레드 작업 큐
queue<function<void()>> m_mainThreadTasks;
CRITICAL_SECTION m_mainThreadTasksCS;
atomic<bool> m_objectsCreated{false};
};
백그라운드 로딩 스레드 구현
DWORD WINAPI LumiaIsland::BackgroundLoadingThread(LPVOID _param)
{
LumiaIsland* scene = static_cast<LumiaIsland*>(_param);
try {
// 백그라운드에서 안전하게 로딩할 수 있는 작업들
EnterCriticalSection(&scene->m_loadingCS);
// 리소스 로딩 작업
// 예: 모델 파일 읽기, 텍스처 로딩 등
LeaveCriticalSection(&scene->m_loadingCS);
// 메인 스레드에서 실행해야 할 작업들을 큐에 추가
EnterCriticalSection(&scene->m_mainThreadTasksCS);
scene->m_mainThreadTasks.push([scene]() {
// GPU 리소스 생성 등 메인 스레드에서만 가능한 작업
scene->CreateNavMesh();
scene->m_objectsCreated = true;
});
LeaveCriticalSection(&scene->m_mainThreadTasksCS);
scene->m_loadingComplete = true;
} catch (...) {
// 예외 처리
scene->m_loadingComplete = true;
}
return 0;
}
메인 스레드 동기화
void LumiaIsland::ProcessMainThreadTasks()
{
EnterCriticalSection(&m_mainThreadTasksCS);
while (!m_mainThreadTasks.empty()) {
auto task = m_mainThreadTasks.front();
m_mainThreadTasks.pop();
LeaveCriticalSection(&m_mainThreadTasksCS);
// 메인 스레드에서 작업 실행
task();
EnterCriticalSection(&m_mainThreadTasksCS);
}
LeaveCriticalSection(&m_mainThreadTasksCS);
}
void LumiaIsland::Update()
{
// 매 프레임마다 메인 스레드 작업 처리
ProcessMainThreadTasks();
// 로딩 완료 후 게임 로직 실행
if (m_objectsCreated) {
m_uiManager->Update();
CheckPickedItemBox();
ControlPlayerStatus();
HandleSkillLevelUpInput();
}
Scene::Update();
}
3. UI / 일반 객체 분리 관리 시스템
게임 성능 최적화를 위해 UI 객체와 일반 객체를 완전히 분리하여 관리
class SceneObjectManager {
private:
unordered_set<shared_ptr<GameObject>> m_gameObjects; // 일반 객체
unordered_set<shared_ptr<GameObject>> m_uiObjects; // UI 객체
vector<shared_ptr<GameObject>> m_uiParents; // UI 부모들
vector<shared_ptr<GameObject>> m_uiChildren; // UI 자식들
public:
void AddUIObject(shared_ptr<GameObject> _object, bool isParent = false) {
m_uiObjects.insert(_object);
if (isParent) {
m_uiParents.push_back(_object);
} else {
m_uiChildren.push_back(_object);
}
}
// 계층적 UI 삭제
void MarkUIObjectForDestroyWithChildren(shared_ptr<GameObject> obj) {
if (!obj || CURSCENE->IsDestroying()) return;
// 자식 객체들 먼저 수집
vector<shared_ptr<GameObject>> allChildren;
CollectUIChildren(obj, allChildren);
// 자식들부터 삭제 마크 (역순으로)
for (auto it = allChildren.rbegin(); it != allChildren.rend(); ++it) {
MarkUIObjectForDestroy(*it);
}
// 부모 객체 삭제 마크
MarkUIObjectForDestroy(obj);
}
};
4. QuadTree 기반 피킹 시스템
수백 개의 객체 중에서 마우스로 선택할 객체를 효율적으로 찾는 시스템
shared_ptr<GameObject> SceneObjectManager::PickObjectOrUI() {
if (INPUT->GetButtonDown(KEY_TYPE::LBUTTON) == false)
return nullptr;
POINT screenPt = INPUT->GetMousePos();
shared_ptr<Camera> camera = GetMainCamera()->GetCamera();
// Ray 생성
Ray ray = CreateRayFromScreen(Vec2(screenPt.x, screenPt.y), camera);
// QuadTree로 후보 객체 수집 (O(log n))
vector<shared_ptr<GameObject>> candidates = m_quadTree->Query(ray, camera);
// 음수 좌표 필터링 (화면 밖 객체 제외)
vector<shared_ptr<GameObject>> validCandidates;
for (auto& obj : candidates) {
RECT objBounds = m_quadTree->GetObjectScreenBounds(obj, camera);
int screenCenterX = (objBounds.left + objBounds.right) / 2;
int screenCenterY = (objBounds.top + objBounds.bottom) / 2;
if (screenCenterX < 0 || screenCenterY < 0) continue;
validCandidates.push_back(obj);
}
// 유효한 후보들만 대상으로 Ray 교차 검사
float minDistance = FLT_MAX;
shared_ptr<GameObject> picked;
for (auto& gameObject : validCandidates) {
if (camera->IsCulled(gameObject->GetLayerIndex())) continue;
if (gameObject->GetCollider() == nullptr) continue;
float distance = 0.f;
if (gameObject->GetCollider()->Intersects(ray, OUT distance) == false) continue;
if (distance < minDistance) {
minDistance = distance;
picked = gameObject;
}
}
return picked;
}
5. StartScene - 게임 시작 화면
동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.
6. Character Select Scene - 캐릭터 선택 화면
동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.
7. LumiaIsland - 플레이 Scene
동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.
8. Scene 전환 플로우
graph TD
A[StartScene] -->|게임 시작| B[CharacterSelectScene]
B -->|캐릭터 선택| C[LumiaIsland]
C -->|멀티스레드 로딩| D[백그라운드 리소스 로딩]
C -->|메인 스레드| E[맵/환경 생성]
D -->|완료| F[UI 시스템 초기화]
E -->|완료| F
F --> G[게임플레이 시작]
결론
이 시스템을 통해
- 게일 실행 중 끊김 살짝 있는 리소스 로딩 ( 아직 최적화가 덜 된거 같긴함.... 아직 끊김 ) 약 7% 성능 향상
- 메모리 사용량 최적화
- 안정적인 Scene 전환
- 피킹 성능 : QuadTree로 O(n) -> O(log n)
'DirectX11 > Eternal Return 모작' 카테고리의 다른 글
| [DirectX 11 Eternal Return 모작] 8. State Machine & Delegate (0) | 2025.09.03 |
|---|---|
| [DirectX 11 Eternal Return 모작] 7. ResourceManager (0) | 2025.09.03 |
| [DirectX 11 Eternal Return 모작] 5. Component (0) | 2025.09.02 |
| [DirectX 11 Eternal Return 모작] 4. Shader & Material (0) | 2025.09.02 |
| [DirectX 11 Eternal Return 모작] 3. 인스턴싱 (0) | 2025.09.02 |