Youngki Jeong | Game Dev Lab
HomeProjectsEvidenceJourneyAbout
SearchGitHub글쓰기

Youngki Jeong | Game Dev Lab

Unreal C++ 게임개발 포트폴리오 & 개발 기록

Projects·Evidence·Journey·About·GitHub Portfolio·GitHub

빠른 검색

프로젝트, Evidence, 디버깅 노트, 학습 기록을 검색합니다.

← 목록으로
← 목록으로
WIN-B03

Player Input에서 Projectile·Collision·Room Transition까지

2026년 9월 17일
·
JEONGYOUNGKI
Description

Player Input이 이동과 Projectile 생성으로 이어지고, Stage의 Collision 연결을 거쳐 Monster·Item·Door의 상태 변화와 Room Transition으로 이어지는 Gameplay 흐름을 정리한 기록

Project

WinAPI-Isaac

ArticleType

Technical

SourceBoundary

My Extension

EvidenceLevel

Runtime

Priority

B

Tags

C++WinAPI

ProofType

Runtime

Audience

Recruiter

CareerTrack

Gameplay

목차

WIN-B03 | WinAPI-Isaac — Player Input에서 Projectile·Collision·Room Transition까지

이 글에서 확인할 내용

WinAPI-Isaac을 만들 때 처음에는

키를 누르면 캐릭터가 움직이고, 총알이 나가면 된다고 생각했습니다.

그런데 직접 구현해보니 입력은 화면에서 보이는 이동으로 끝나지 않았습니다.

Player 위치가 바뀌고, Collision에 사용할 Rect가 갱신되고,

Projectile이 새로운 Object로 생성되어 Manager에 등록되고,

이후 Monster나 Item, Door와의 상호작용으로 이어졌습니다.

이 프로젝트에서 처음으로

하나의 입력이 여러 Runtime 단계를 거쳐 실제 Gameplay 결과가 되는 과정을 경험했습니다.

Text
Input
↓
Player 상태 변경
↓
Rect 갱신
↓
Projectile 생성
↓
Manager 등록
↓
Collision
↓
Object / Player 상태 변화
↓
Render

입력은 Gameplay 흐름의 시작점이었습니다

Player는 매 Frame 자신의 Update() 흐름에서 입력을 확인했습니다.

기본적으로는 Key_Input() 또는 상태에 따라 TripleKey_Input() 같은 입력 처리가 이어지고,

그 결과를 기준으로 Player 위치나 Projectile 생성 방식이 달라졌습니다.

Text
CPlayer::Update
↓
Key_Input / TripleKey_Input
↓
위치 또는 공격 상태 변경
↓
Update_Rect
↓
다음 Gameplay 처리

처음에는 아래 두 기능을 별개로 생각했습니다.

Text
이동 키
→ Player가 움직임
 
발사 키
→ Bullet이 나감

하지만 실제 Runtime에서는 둘 다 입력을 현재 Gameplay State로 바꾸는 과정이었습니다.

이 흐름을 직접 따라가면서

입력은 단순히 키 값을 읽는 코드가 아니라

Gameplay 결과가 시작되는 지점이라는 것을 알게 됐습니다.


이동한 뒤에는 Collision 기준도 같이 바뀌어야 했습니다

WASD 입력이 들어오면 Player의 위치 값이 바뀌었습니다.

그런데 캐릭터가 화면에서 움직이는 것만으로는 충분하지 않았습니다.

Player의 위치가 바뀌면

Collision에 사용하는 Rect도 같이 갱신되어야 했습니다.

Text
입력
↓
m_tInfo.fX / fY 변경
↓
Update_Rect
↓
새 위치를 기준으로 Collision
↓
Render 위치 반영

처음에는 캐릭터가 화면에서 움직이면 구현이 끝난 것처럼 보였습니다.

하지만 Rect가 따라오지 않으면

보이는 위치와 실제 Collision 위치가 달라질 수 있습니다.

이 경험을 통해 처음으로

화면에 보이는 위치와 Gameplay에서 사용하는 충돌 기준이 함께 움직여야 한다는 점을 배웠습니다.


Projectile은 입력 이후 독립된 Runtime Object가 됐습니다

발사 입력이 들어오면 Bullet을 생성하고,

방향과 위치를 설정한 뒤 Bullet Manager에 등록했습니다.

프로젝트에서는 CAbstractFactory<CBullet>를 이용해 Bullet Object를 만드는 흐름을 사용했습니다.

Text
발사 입력
↓
Bullet 생성
↓
Direction / 위치 설정
↓
CBulletMgr 등록
↓
Bullet Update

여기서 중요했던 점은

Bullet이 Player 안에 잠깐 존재하는 값으로 끝나지 않았다는 것입니다.

한 번 생성된 뒤에는 별도의 Runtime Object가 되어

자기 Update 흐름에서 이동했습니다.

Player는 입력을 받고 Bullet을 만드는 시작점에 가깝고,

생성 이후 Bullet의 이동과 관리까지 Player가 전부 맡는 구조는 아니었습니다.

Text
CPlayer
→ 입력 / Bullet 생성
 
CBullet
→ 자신의 방향으로 이동
 
CBulletMgr
→ Bullet List 관리
 
Stage
→ Collision 관계 연결

처음 만들 때는 “총알이 나간다”는 화면 결과만 봤지만,

지금 다시 보면 이 기능은 입력이 새로운 Runtime Object를 만들어내는 첫 경험이었습니다.


Bullet이 만들어졌다고 공격이 끝나는 것은 아니었습니다

Bullet이 화면에 보이고 이동한다고 해서

Monster에게 실제 영향을 주는 Gameplay가 자동으로 완성되는 것은 아니었습니다.

Bullet이 Monster와 상호작용하려면

Stage에서 두 Object List가 Collision 대상으로 연결되어야 했습니다.

Text
Player Input
↓
Bullet 생성
↓
CBulletMgr 등록
↓
Bullet Update
↓
Stage Late_Update
↓
Bullet ↔ Monster Collision
↓
Object 상태 변화

이 과정에서 처음으로

Object 생성과 Object 상호작용은 별도의 단계라는 점을 체감했습니다.

Bullet이 존재하는 것과,

그 Bullet이 Monster에게 영향을 주는 것은 다른 문제였습니다.


Collision은 Stage가 Object 사이의 관계를 연결했습니다

Player, Monster, Bullet, Item은 각자 존재한다고 해서

자동으로 서로 영향을 주는 것은 아니었습니다.

각 Stage의 Late_Update()에서

그 방에 필요한 Object 조합을 직접 Collision 흐름에 연결했습니다.

대표적인 관계는 다음과 같았습니다.

Text
Bullet ↔ Monster
Player ↔ Monster
Player ↔ Item
Player ↔ Block
Player ↔ Door

처음 구현할 때는 이 방식이 직관적이었습니다.

Stage를 보면

“이 방에서 어떤 Object끼리 충돌하는가?”를 바로 확인할 수 있었기 때문입니다.

하지만 반대로 말하면,

Object가 실제로 존재하더라도 Stage에서 해당 Collision 호출이 빠지면

Gameplay 결과로 이어지지 않을 수 있었습니다.

이 경험을 통해 처음으로:

Text
Object가 존재한다
≠
Object가 Runtime에서 상호작용한다

는 점을 분명하게 보게 됐습니다.


Monster가 사라지는 것도 한 번의 처리로 끝나지 않았습니다

Bullet과 Monster가 충돌했을 때

화면에서 Monster가 사라지는 결과는 단순해 보였습니다.

하지만 내부 흐름은 여러 단계로 이어졌습니다.

Text
Bullet ↔ Monster Collision
↓
Set_Dead 또는 상태 변경
↓
Dead Flag 변경
↓
Update에서 OBJ_DEAD 반환
↓
Manager가 List에서 제거
↓
다음 Render부터 보이지 않음

처음에는 Collision이 발생하면 Object가 바로 없어지는 것처럼 생각했습니다.

직접 구현하면서

Collision 판정, Object 상태 변경, Manager 제거, 화면 반영이

서로 다른 단계라는 것을 경험했습니다.

즉:

Text
Hit
≠
즉시 delete

였습니다.

이 과정은 이후 다른 프로젝트에서

Damage나 상태 변화가 어디에서 발생하고

언제 실제 Runtime 결과로 이어지는지를 나눠서 보는 데 도움이 됐습니다.


Player와 Monster 충돌도 Update 이후 관계를 확인하는 흐름이었습니다

Player와 Monster는 각자 Update를 통해

위치와 Rect가 갱신됩니다.

그 뒤 Stage의 Late_Update()에서

둘의 Collision 관계를 확인했습니다.

Text
Player Update
+
Monster Update
↓
각 Rect 갱신
↓
Stage Late_Update
↓
Collision
↓
위치 보정 또는 상태 변화

당시에는 “몬스터와 닿으면 반응한다”는 결과 자체가 중요했습니다.

지금 다시 보면 이 흐름은

두 Runtime Object의 현재 상태를 Update한 뒤 상호작용 결과를 결정하는 구조였습니다.

이 경험은 이후 더 복잡한 전투 시스템을 볼 때도

“먼저 상태를 갱신하고, 어떤 단계에서 상호작용 결과를 결정하는가?”를 구분해서 보게 한 기초가 됐습니다.


Item은 Player의 상태와 이후 행동을 바꾸는 Trigger였습니다

Item도 단순히 맵 위에 놓인 이미지가 아니었습니다.

Item은 CItemMgr에 등록되고,

Player와의 Collision을 통해 획득과 상태 변화로 이어졌습니다.

프로젝트에서는 Key, Coin, Bomb, Triple Tear 같은 Item 흐름을 다뤘습니다.

Text
Item 생성
↓
CItemMgr 등록
↓
Player ↔ Item Collision
↓
Item 상태 변경
↓
Player 상태 또는 진행 흐름 변화

처음에는 Item을

“먹으면 사라지는 Object” 정도로 생각했습니다.

하지만 기능을 붙이면서

Item이 Player의 상태나 이후 Gameplay 결과를 바꿀 수 있다는 것을 경험했습니다.

Key나 Bomb처럼 Stage 진행이나 다른 Object와의 상호작용에 연결되는 Item도 있었고,

Triple Tear처럼 Player의 공격 방식 자체에 영향을 주는 Item도 있었습니다.

이때부터 Item을 단순한 화면 Object라기보다

Gameplay State를 바꾸는 Trigger로 조금씩 보게 됐습니다.


Triple Tear에서는 같은 입력의 결과가 달라졌습니다

Triple Tear는 이 프로젝트에서

Player 상태와 입력 결과가 연결되는 과정을 이해하기 좋은 예였습니다.

Item을 획득해 특정 상태가 되면

일반적인 발사 입력과 다른 Projectile 생성 흐름을 사용했습니다.

Text
Triple Tear 획득
↓
Player 상태 변화
↓
입력 처리 분기
↓
TripleKey_Input
↓
Projectile 생성 방식 변화

처음에는 단순히

“아이템을 먹으면 총알이 여러 개 나간다”는 기능이었습니다.

지금 다시 보면 중요한 것은

같은 종류의 입력이라도 현재 Player 상태에 따라 결과가 달라질 수 있다는 점이었습니다.

지금이라면 입력 함수 안의 분기를 계속 늘리기보다

상태나 데이터, 공격 규칙을 더 분리하는 방법을 고민할 것 같습니다.

하지만 당시에는 직접 상태를 바꾸고

입력 결과가 달라지는 것을 화면에서 확인한 경험 자체가 의미 있었습니다.


Door는 Collision이 Scene Change까지 이어지는 예시였습니다

Door는 화면에 보이는 큰 Door Object라기보다

Room Transition을 위한 10×10 Invisible Trigger에 가까웠습니다.

CMyDoor가 직접 목적지 Scene을 모두 처리하는 구조는 아니었습니다.

Stage에서 Door를 생성하고 CFurnitureMgr의 Bucket에 등록한 뒤,

Late_Update()에서 Player와 Door List를 Collision 대상으로 연결했습니다.

Text
Player 이동
↓
Player Rect 갱신
↓
Door Trigger 접촉
↓
Stage Late_Update
↓
CollisionMgr Door Collision
↓
CSceneMgr::Scene_Change / PreScene_Change
↓
다음 Stage Initialize

처음에는 “문에 닿으면 다음 방으로 이동한다”는 기능 하나로 생각했습니다.

하지만 실제로는 Player 이동, Rect 갱신, Manager 등록, Collision, Scene 전환까지

여러 Runtime 단계가 연결되어야 결과가 나왔습니다.

즉 Door는

하나의 Object Collision이 현재 Stage를 넘어 Scene Lifecycle까지 영향을 주는 사례였습니다.


Door는 Spawn·Collision·정리 흐름이 서로 맞아야 했습니다

Door 흐름을 다시 보면서

Object 하나를 만드는 것만으로 Gameplay가 완성되는 것이 아니라는 점이 더 선명하게 보였습니다.

정상적인 Room Transition을 위해서는:

Text
어떤 Door Bucket에 Spawn했는가?
↓
Collision에서 같은 Bucket을 보고 있는가?
↓
Collision 함수가 실제 Scene Change로 이어지는가?
↓
Stage 종료 시 같은 Bucket을 정리하는가?

가 서로 맞아야 했습니다.

ReStage 계열처럼 Door 종류와 전환 흐름이 많아질수록

Spawn Bucket, Collision Getter, Release 대상이 어긋날 수 있는 부분도 보였습니다.

당시에는 기능을 먼저 완성하는 데 집중했지만,

지금 다시 보면 이런 구조는 Room 수가 늘어날수록

누락이나 불일치가 생기기 쉬웠습니다.

이 경험 덕분에 이후에는 문제가 생겼을 때

Door Class 하나만 보는 것이 아니라:

Text
Spawn
→ Manager 등록
→ Collision 연결
→ 결과 처리
→ Cleanup

순서 전체를 따라가게 됐습니다.


Runtime

실제 영상에서는 Player 이동과 Projectile 발사, Monster와의 상호작용,

Item 획득과 Room 이동 같은 흐름을 확인할 수 있습니다.

WinAPI C++ Isaac

영상에서 보이는 한 번의 이동이나 한 번의 충돌 뒤에는

입력, 위치와 Rect 갱신, Object 생성, Manager 등록, Collision, 상태 변화, Render가 계속 이어지고 있습니다.

Text
Input
↓
Player / Projectile 상태 변화
↓
Manager Update
↓
Stage Collision
↓
Monster / Item / Door 결과
↓
Render

처음 발표할 때는 이 흐름을 지금처럼 나눠 설명하지 못했습니다.

당시에는 “움직인다”, “총알이 나간다”, “아이템을 먹는다”, “방을 이동한다”는 기능 결과를 보여주는 데 더 집중했습니다.

지금은 같은 영상을 보더라도

그 뒤에서 어떤 Runtime 단계가 연결되어 있는지 함께 설명할 수 있습니다.


지금 다시 보면

지금 다시 만든다면

입력과 Gameplay 결과의 책임을 더 명확하게 나누고 싶습니다.

당시에는 이동, 발사, Item 사용 같은 입력 처리가 Player 쪽에 많이 모여 있었고,

Triple Tear처럼 Player 상태가 달라지면 입력 함수 자체를 분기하는 부분도 있었습니다.

Stage는 필요한 Collision 관계를 직접 호출했고,

Door Transition도 특정 Door Bucket과 Scene 전환 함수에 의존했습니다.

처음 구현하기에는 이해하기 쉬웠지만

Stage와 기능이 늘어날수록 분기와 중복, 누락 가능성이 커지는 구조였습니다.

지금 다시 만든다면:

Text
Input
Gameplay State
Projectile Spawn Rule
Collision Relation
Item Effect
Room Transition Data

를 조금 더 나눠서 관리하는 방향을 고민할 것 같습니다.

하지만 이 생각도 처음부터 가지고 있었던 것은 아닙니다.

직접 기능을 붙여보고

Player, Bullet, Monster, Item, Door가 어떻게 연결되는지 경험했기 때문에

나중에 책임 분리나 Data화의 필요성을 이해하게 됐습니다.


이후 프로젝트에서 이어진 부분

WinAPI-Isaac에서 가장 먼저 경험한 Gameplay 연결은 단순했습니다.

Text
Input
→ Object 생성 / 상태 변화
→ Collision
→ Gameplay Result

이후 DX9-Cat-Quest에서는

캐릭터와 Monster, Skill, 상호작용 구현으로 범위가 넓어졌습니다.

DX11-SpongeBob-BFBB에서는

3D 이동과 Collision, Navigation, Missile, Boss, UI 같은 Runtime 흐름으로 확장됐습니다.

Unreal 프로젝트에서는 구현 방식이 완전히 달라졌지만

입력이 단순한 Key 처리에서 끝나지 않는다는 관점은 그대로 이어졌습니다.

예를 들어 더 큰 Gameplay 구조에서는:

Text
Input
↓
Gameplay State / Action
↓
Attack 또는 Ability 실행
↓
Collision / Trace
↓
Target 상태 변화
↓
UI / 화면 결과

처럼 더 많은 단계가 연결됩니다.

WinAPI-Isaac은 그보다 훨씬 단순한 구조였지만,

제가 입력을 Runtime Gameplay 결과까지 끝까지 따라가 보는 습관을 처음 익힌 프로젝트였습니다.


정리

WinAPI-Isaac에서 Player 입력을 구현하며 배운 것은

입력이 단순히 캐릭터 위치를 바꾸는 코드가 아니라는 점이었습니다.

이동 입력은 위치와 Rect 갱신으로 이어졌고,

발사 입력은 새로운 Bullet Object를 만들어 Manager에 등록했습니다.

그 Bullet은 Stage의 Collision 흐름에서 Monster와 만나 상태를 바꿨고,

Item은 Player 상태나 이후 행동에 영향을 줬으며,

Door Trigger는 Collision을 통해 다음 Scene으로 이어졌습니다.

Text
Input
↓
Player State
↓
Object Spawn
↓
Manager
↓
Collision
↓
Monster / Item / Door State
↓
Scene 또는 Render Result

처음 만들 때는 이 기능들을 각각 따로 봤습니다.

하지만 직접 연결하고 다시 코드를 따라가면서

Input → Runtime Object → Collision → Gameplay Result가 하나의 흐름이라는 것을 이해하게 됐습니다.

WinAPI-Isaac은 제가 처음으로

키 입력이 화면 이동을 넘어 Object 생성과 상태 변화, Room Transition까지 이어지는 과정을 직접 구현해본 프로젝트였습니다.

빠른 검색

프로젝트, Evidence, 디버깅 노트, 학습 기록을 검색합니다.

목차