WIN-B02 | WinAPI-Isaac — Scene Change와 Object Lifecycle을 처음 직접 구현하며 배운 것
이 글에서 확인할 내용
WinAPI-Isaac을 만들면서 처음으로
Scene이 바뀌고, 그 안의 Object가 생성되고, 갱신되고, 사라지는 흐름을 직접 다뤘습니다.
처음에는 Logo가 나오고 Menu로 넘어가고 Stage가 열리면
그냥 “화면이 바뀌었다”고 생각했습니다.
하지만 코드를 다시 따라가 보니 Scene 전환은 단순한 이미지 교체가 아니었습니다.
현재 Scene이 바뀌면 그 Scene에서 사용할 Player, Monster, Item, Door 같은 Object도 새로 준비되고,
각 Object는 Manager에 등록된 뒤 매 Frame Update / Late_Update / Render 흐름에 들어갑니다.
이 프로젝트를 통해 처음으로
게임 화면 뒤에서 Object가 어떤 생명주기로 움직이는지를 경험했습니다.
Text
CMainGame
↓
CSceneMgr
↓
현재 Scene
↓
Object 생성
↓
Manager 등록
↓
Update / Late_Update / Render
↓
상태 변화
↓
Object 제거Scene은 단순한 화면 이름이 아니었습니다
WinAPI-Isaac에서는 CSceneMgr가 현재 Scene을 관리했습니다.
Logo, Menu, Stage는 모두 같은 Game Loop 안에서 실행되지만,
현재 어떤 Scene이 선택되어 있는지에 따라 실제로 Update되는 내용이 달라집니다.
Text
CMainGame
↓
CSceneMgr
↓
현재 Scene
↓
Scene 안의 Object / Manager
↓
Update / Late_Update / Render처음에는 Scene을 배경 이미지나 화면 구분 정도로 생각했습니다.
그런데 Stage로 들어가면서 생각이 조금 바뀌었습니다.
Stage에서는 Player, Monster, Item, Door 같은 Gameplay Object가 실제로 만들어지고,
각자 필요한 Manager에 등록됩니다.
즉 Scene은 화면 이름이라기보다
“지금 어떤 Object와 Gameplay 흐름이 살아 있는가”를 결정하는 단위에 가까웠습니다.
Scene Change는 기존 Scene을 정리하고 새 Runtime을 준비하는 과정이었습니다
CSceneMgr::Scene_Change()는
단순히 배경 이미지나 Scene 이름만 바꾸는 함수가 아니었습니다.
기존 Scene을 정리하고 삭제한 뒤,
다음 Scene을 만들고 Initialize()를 호출하는 흐름이었습니다.
Text
기존 Scene Release
↓
기존 Scene delete
↓
새 Scene 생성
↓
새 Scene Initialize
↓
현재 Scene으로 사용Logo에서 Menu로 넘어갈 때와
Menu에서 Stage로 넘어갈 때 화면 결과는 단순해 보입니다.
하지만 Stage가 시작되면 그 안에서 사용할 Player, Monster, Item, Door, Block 같은 Object가 준비되고,
이 Object들이 다시 Frame Update 대상이 됩니다.
처음 만들 당시에는 “장면 전환이 된다”는 결과가 중요했습니다.
지금 다시 보면 Scene Change는
현재 Runtime Context를 정리하고 다음 Context를 준비하는 과정으로 이해할 수 있습니다.
다만 이것을 현재 Engine의 Level Streaming이나 복잡한 World Lifecycle과 같은 수준으로 확대해서 보지는 않습니다.
WinAPI-Isaac에서 제가 직접 다룬 것은 비교적 단순한 Scene 객체 교체와 초기화 흐름입니다.
Object는 CObj를 기준으로 Runtime에 들어갔습니다
Player, Monster, Bullet, Item, UI, Door 같은 Runtime Object는
공통적으로 CObj를 기반으로 위치와 크기, Rect, Dead 상태 같은 값을 가지고 있었습니다.
Object가 움직이면 위치 값만 바뀌는 것으로 끝나지 않았습니다.
Collision과 Render에서 사용할 Rect도 함께 갱신되어야 했습니다.
Text
Object 위치 변경
↓
Update_Rect
↓
Collision 기준 갱신
↓
Render 위치 반영처음에는 Rect를 단순한 충돌 박스 정도로만 봤습니다.
하지만 직접 Player와 Monster, Bullet, Item을 움직여보면서
Rect는 Object가 다른 Gameplay 흐름과 만나는 기준 중 하나라는 것을 알게 됐습니다.
즉 Object는 화면에 보이는 이미지 하나가 아니라
위치와 상태를 가지고 매 Frame 갱신되며 다른 Object와 상호작용하는 Runtime 대상이었습니다.
Object는 생성된 뒤 Manager 흐름에 들어갔습니다
Object는 생성됐다고 바로 모든 기능이 끝나는 것이 아니었습니다.
각 Object는 자신을 관리할 Manager에 등록되어야 했습니다.
프로젝트에는 CObjMgr, CBulletMgr, CItemMgr, CBlockMgr, CFurnitureMgr, CUiMgr처럼
역할에 따라 여러 Manager가 있었습니다.
Text
Object 생성
↓
Manager 등록
↓
Update
↓
Late_Update
↓
Render예를 들어 Bullet은 Player 입력으로 생성되지만,
생성 이후에는 Bullet Manager에 들어가 자기 Update 흐름을 갖습니다.
Item 역시 Stage에 배치된 뒤 Item Manager에 등록되고,
Player와 충돌하기 전까지 Runtime에 남아 있습니다.
Door는 일반 Object와 조금 다르게 CFurnitureMgr 쪽에 등록되어
Stage의 Room Transition 흐름에 사용됐습니다.
처음에는 Manager 종류가 많다는 사실 자체가 복잡하게 느껴졌습니다.
하지만 다시 보면 공통된 역할은 비교적 단순했습니다.
Text
Object를 등록한다
↓
매 Frame 갱신한다
↓
필요한 Object를 Render한다
↓
죽거나 Scene이 끝난 Object를 정리한다이때부터 Object를 볼 때
“무엇을 하는가?”뿐 아니라
- *“누가 가지고 있고, 누가 매 Frame 호출하는가?”**를 같이 보게 됐습니다.
Object가 사라지는 것도 하나의 Lifecycle이었습니다
처음에는 Object가 죽거나 사용되면
그 자리에서 바로 delete하면 된다고 생각하기 쉽습니다.
WinAPI-Isaac에서는 Object가 자기 자신을 즉시 삭제하는 방식보다,
Dead 상태를 만들고 Manager가 제거하는 흐름을 사용했습니다.
Text
Collision 또는 상태 변화
↓
m_bDead 변경
↓
Update에서 OBJ_DEAD 반환
↓
Manager가 list에서 erase
↓
delete예를 들어 Monster가 Bullet에 맞아 죽거나,
Item을 Player가 획득하면 Object 상태가 먼저 바뀝니다.
그 다음 Manager의 Update 흐름에서 OBJ_DEAD를 확인하고
해당 Object를 list에서 제거합니다.
처음 만들 때는 “Monster가 사라졌다”는 화면 결과만 중요했습니다.
하지만 지금 다시 보면 그 결과는:
Text
Gameplay 상태 변화
≠
즉시 메모리 제거였습니다.
상태가 먼저 바뀌고,
그 상태를 Manager가 다음 처리 단계에서 확인한 뒤 실제 제거가 일어났습니다.
이 경험을 통해 처음으로
Object의 Gameplay 상태 변화와 실제 Lifetime 종료 시점은 구분해서 볼 수 있다는 감각을 얻었습니다.
Stage는 Object를 배치하는 곳이면서 Runtime 관계를 연결하는 곳이었습니다
Stage의 Initialize()에서는
그 방에서 사용할 Object를 만들고 각 Manager에 등록했습니다.
Text
Stage Initialize
↓
Player 생성
↓
Monster 생성
↓
Item 생성
↓
Door 생성
↓
Block 생성
↓
각 Manager에 등록그리고 Late_Update()에서는
어떤 Object끼리 Collision을 확인할지 직접 연결했습니다.
Text
Player ↔ Monster
Bullet ↔ Monster
Player ↔ Item
Player ↔ Block
Player ↔ Door처음 만들 때는 이 방식이 가장 이해하기 쉬웠습니다.
Stage 파일을 보면
“이 방에서 어떤 Object가 있고, 무엇과 충돌하는지”를 바로 확인할 수 있었기 때문입니다.
하지만 다시 보면 Stage는 단순 배경이나 Object 배치만 담당한 것이 아니라
Object 간 Runtime 관계까지 많이 연결하고 있었습니다.
Object가 Manager에 존재해도
Stage에서 필요한 Collision 관계를 연결하지 않으면 Gameplay 결과로 이어지지 않습니다.
이 경험 덕분에 이후에는:
Text
Object가 존재한다와
Text
Object가 Runtime에서 실제로 상호작용한다를 구분해서 보게 됐습니다.
Door를 보며 Scene과 Object Lifecycle이 함께 연결된다는 것을 알았습니다
Door는 이 구조를 이해하기 좋은 예였습니다.
WinAPI-Isaac의 CMyDoor는
화면에 보이는 큰 Door Sprite라기보다 10×10 크기의 Invisible Trigger에 가까웠습니다.
Door 자체가 Scene을 직접 교체하는 구조는 아니었습니다.
Stage에서 Door를 생성해 CFurnitureMgr의 특정 Bucket에 등록하고,
Late_Update()에서 Player와 Door List를 Collision 대상으로 연결해야 했습니다.
Text
Stage Initialize
↓
Door 생성
↓
CFurnitureMgr 등록
↓
Player 이동
↓
Door Rect와 Collision
↓
CSceneMgr::Scene_Change
↓
다음 Stage Initialize즉 “문에 닿으면 다음 방으로 간다”는 짧은 기능 뒤에는
Object 생성, Manager 등록, Rect 갱신, Collision, Scene Change가 연결되어 있었습니다.
처음에는 이것을 하나의 Door 기능으로 봤습니다.
지금 다시 보면
Object의 상호작용이 현재 Scene의 Lifetime 종료와 다음 Scene의 시작으로 이어지는 사례였습니다.
Object를 만드는 것만으로 기능이 완성되는 것은 아니었습니다
Door 흐름을 다시 보면서 특히 선명하게 보였던 점이 있습니다.
Object를 생성했다고 해서 Runtime 기능이 자동으로 완성되는 것은 아니었습니다.
Door의 경우 다음 흐름이 서로 맞아야 했습니다.
Text
어떤 Bucket에 Spawn했는가?
↓
Collision에서는 어떤 Bucket을 읽는가?
↓
Collision 함수가 실제 Scene Change로 이어지는가?
↓
Scene 종료 시 같은 Bucket을 정리하는가?Stage가 늘어나면
Spawn Bucket, Collision Getter, Release 시 Delete_ID가 서로 어긋날 가능성도 있었습니다.
당시에는 우선 동작하는 결과를 만드는 데 집중했지만,
지금 다시 보면 이런 구조는 Room이 많아질수록 누락이나 불일치가 생기기 쉬웠습니다.
이 경험은 이후 Runtime 문제를 볼 때
한 Class만 보는 대신:
Text
생성
→ 등록
→ 연결
→ 사용
→ 정리전체 Lifecycle을 따라가는 습관으로 이어졌습니다.
Scene 종료 시 Manager 정리도 같이 봐야 했습니다
새 Scene을 만드는 것만큼
기존 Scene에서 사용하던 Object를 정리하는 것도 중요했습니다.
Stage가 끝날 때는
해당 Stage에서 사용하던 Manager Bucket을 Delete_ID나 Release 흐름으로 정리했습니다.
즉 Scene Lifecycle과 Object Lifecycle은 완전히 따로 떨어져 있지 않았습니다.
Text
Scene 종료
↓
Stage가 사용하던 Object 정리
↓
Manager Bucket 정리
↓
Scene Release / delete
↓
다음 Scene Initialize이 구조를 직접 경험하면서
Scene Change를 볼 때 단순히 “다음 Scene을 만들었는가?”만 볼 것이 아니라
이전 Scene에서 살아 있던 Object를 누가 정리하는가도 함께 봐야 한다는 점을 배웠습니다.
Runtime
실제 영상에서는 Logo와 Menu를 거쳐 Stage에 들어가고,
Player와 Monster, Projectile, Item이 등장한 뒤 Room이 바뀌는 흐름을 확인할 수 있습니다.
Stage에 들어갔다는 것은 단순히 배경이 바뀐 것이 아니라,
새 Scene의 Initialize() 이후 Object들이 생성되고 Manager의 Update 흐름에 들어갔다는 의미였습니다.
영상에서 Monster나 Item이 등장하고 사라지는 장면 역시
Object가 Runtime에 등록되고 상태 변화 이후 제거되는 Lifecycle의 결과입니다.
Room이 바뀌는 장면은
Door Trigger와 Collision, Scene Change, 다음 Stage 초기화가 연결된 결과입니다.
지금 다시 보면
지금 다시 만든다면 Scene과 Object의 책임을 더 명확하게 나누고 싶습니다.
당시에는 Stage가:
- Object 생성
- Collision 관계 연결
- Door Transition
- Manager 정리
까지 여러 책임을 가지고 있었습니다.
Manager도 Object 종류에 따라 여러 곳으로 나뉘어 있어
전체 Lifecycle을 한눈에 보기 어려운 부분이 있었습니다.
Door Transition 역시 Destination Scene ID와 Bucket 선택이 코드 여러 곳에 나뉘어 있어
지금이라면 Door Trigger와 목적지 정보, Spawn 위치 같은 값을 Data로 더 모아 관리하는 방법을 먼저 고민할 것 같습니다.
하지만 당시 단계에서는
직접 Object를 만들고, Manager에 넣고, Update하고, Dead 상태로 만들고, 다시 제거해보는 경험 자체가 중요했습니다.
이 경험이 있었기 때문에 이후 DX11이나 Unreal을 볼 때도
같은 질문을 먼저 하게 됐습니다.
Text
누가 생성하는가?
누가 소유하는가?
누가 Update하는가?
누가 제거하는가?
Scene이 바뀌면 무엇이 같이 정리되는가?
다음 Runtime은 언제 준비되는가?지금 보이는 구조적인 아쉬움은
당시 구현을 지금 기준의 Engine Architecture처럼 과장하기 위한 것이 아닙니다.
오히려 Object Lifecycle과 책임 경계를 처음 몸으로 경험했고, 이후 무엇을 더 구조적으로 보게 됐는지를 보여주는 지점에 가깝습니다.
다음 프로젝트에서 이어진 부분
WinAPI-Isaac 이후 DX9과 DX11 프로젝트에서는
Object Lifecycle이 더 큰 3D Runtime 구조로 확장됐습니다.
DX11-SpongeBob-BFBB에서는 Object와 Layer, Prototype, Runtime Clone, Renderer Group처럼
더 많은 단계가 연결된 구조를 보게 됐습니다.
Unreal에서는 이름과 구조가 완전히 달라졌지만
Actor, Component, World, GameMode, Subsystem 같은 요소를 볼 때도 비슷한 질문을 하게 됐습니다.
Text
누가 만든다?
누가 가진다?
언제 Gameplay에 참여한다?
언제 제거된다?
Context가 바뀌면 무엇이 같이 바뀐다?WinAPI-Isaac은 이 질문을 가장 단순한 형태로 처음 경험한 프로젝트였습니다.
정리
WinAPI-Isaac에서 Scene과 Object를 다루며 배운 것은
게임 화면이 단순히 한 장의 결과 이미지가 아니라는 점이었습니다.
Logo, Menu, Stage가 바뀌는 것은 Scene 객체가 교체되는 흐름이었고,
Stage 안에서 Player와 Monster가 보이는 것은 Object가 생성되어 Manager에 등록되고 매 Frame 갱신된 결과였습니다.
Object가 사라지는 것도:
Text
상태 변화
↓
OBJ_DEAD
↓
Manager 제거
↓
delete라는 Lifetime 흐름을 가지고 있었습니다.
Door를 통한 Room Transition 역시:
Text
Door 생성
↓
Manager 등록
↓
Collision
↓
Scene Change
↓
기존 Runtime 정리
↓
다음 Stage Initialize로 이어졌습니다.
처음 만들 당시에는 이 구조를 지금처럼 말로 설명하지 못했습니다.
하지만 직접 Scene을 바꾸고, Object를 등록하고, Manager가 Update / Late_Update / Render를 돌리고,
필요한 Object를 제거하는 과정을 구현하면서
Scene Flow와 Object Lifecycle이 Game Runtime 안에서 어떻게 연결되는지 처음 몸으로 익혔습니다.