게임 개발 최소 필수 지식 — Steam 출시(macOS + Windows)까지
프로그래밍은 하지만 게임 개발은 처음인 개발자 / AI(Claude Code) 보조 개발 / 1인 / 목표는 Steam 출시 — 백과사전이 아니라 "이것만 알면 첫 게임을 내고 살아남는다"는 최소 집합
0. 30초 결론
- 엔진은 Godot 4.7.x를 써라. MIT 라이선스(로열티·구독 0원), 씬/리소스가 텍스트 포맷(.tscn/.tres)이라 Git·AI 친화적, 에디터 200MB대로 가볍고, Mac에서 Mac/Windows/Linux 빌드를 다 뽑을 수 있으며, GDScript는 AI가 잘 다룬다. 대가는 3D 성능·에셋스토어 규모·구인 시장이 Unity/Unreal보다 작다는 것 — 2D 인디 첫 게임에는 무관한 단점이다.
- Steam 출시 최소 비용은 $100(Steam Direct, 앱당) + $99/년(Apple Developer Program) = 약 $199. 여기에 Windows 코드서명 인증서($200~500/년)는 Steam 배포만 한다면 선택이다.
- 초보가 반드시 걸려 넘어지는 지점은 macOS 공증(notarization)이다. Steam은 2019-10-14부터 신규 macOS 앱에 64비트 + Apple 공증을 요구한다. 즉 Mac 빌드를 내려면 Apple Developer Program $99/년이 사실상 필수다.
- 첫 게임의 현실적 규모: 플레이타임 30분~2시간, 개발 3~6개월, 시스템 3개 이하. 오픈월드·멀티플레이·대작 로그라이크·RPG는 첫 게임으로 고르면 100% 미완성으로 끝난다.
- 셰이더 작성, ECS, 네트워킹, 커스텀 엔진, 절차적 생성, 최적화는 지금 배우지 마라. (1장 스킵 목록 참조)
- AI는 "코드"에 강하고 "재미"에 약하다. 시스템 코드·세이브·툴·리팩터는 맡기고, 수치 튜닝(점프 높이, 피격 경직, 밸런스)은 직접 플레이하며 손으로 잡아라.
1. 지금 배우지 않아도 되는 것 (스킵 목록) — 이 문서에서 가장 중요한 장
표 1 — 스킵 목록
| 주제 | 왜 지금 스킵하나 | 언제 배우나 | 대신 지금 쓸 것 |
|---|---|---|---|
| 셰이더 작성(GLSL/GDShader) | 그래픽스 수학 + GPU 파이프라인 이해가 선행. 게임 재미와 무관 | 첫 게임 출시 후, 특정 비주얼이 필요할 때 | 엔진 기본 머티리얼, modulate(색조), 내장 CanvasItem 셰이더 |
| ECS(Entity Component System) | 엔티티 수천 개일 때의 성능/구조 해법. 수십 개짜리 게임엔 순수 오버헤드 | 대규모 시뮬레이션·탄막·RTS를 만들 때 | 엔진의 씬 트리 + 노드 상속 |
| 멀티플레이 네트워킹 | 난이도가 싱글의 3~10배. 지연 보상·롤백·권한 모델은 별도 커리큘럼 | 싱글 게임 1개 출시 후 | 싱글플레이. 필요하면 로컬 협동(같은 화면 2인) |
| 커스텀 엔진 제작 | "엔진 만들기"와 "게임 만들기"는 다른 프로젝트. 게임이 안 나온다 | 취미로, 게임 출시와 별개로 | 기성 엔진 |
| 절차적 생성(procgen) | 무한 콘텐츠는 무한 QA. 손으로 만든 20분이 랜덤 2시간보다 재밌다 | 코어 루프가 검증된 뒤 | 손으로 배치한 레벨 |
| 성능 최적화 / 오브젝트 풀링 | 최적화는 프로파일링 후에 하는 것. 조기 최적화는 시간만 태운다 | 프레임 드랍이 실제로 보일 때 | 그냥 만들고, 느려지면 그때 프로파일러 |
| 3D(모델링·리깅·애니메이션·라이팅) | 에셋 제작 비용이 2D의 5~10배. 1인 개발 최대 지뢰 | 2D 게임 1개 출시 후 | 2D. 또는 3D면 에셋 100% 구매 |
| 물리 엔진 심화(관절, 래그돌, 소프트바디) | 튜닝 난이도 높고 버그가 물리적으로 재현 안 됨 | 물리가 코어 메카닉일 때만 | AABB/원 충돌, 커스텀 이동(Kinematic) |
| AI 행동트리(Behavior Tree) | 적 3종에 BT는 과잉 설계 | 적 종류가 10개 넘어갈 때 | enum 상태머신 |
| 에디터 확장 / 커스텀 툴 | 툴 만들다 게임을 안 만드는 전형적 함정 | 반복 작업이 하루 30분 넘을 때 | CSV/JSON 데이터 파일 + 스프레드시트 |
| 로컬라이제이션(다국어) | 텍스트가 확정되기 전에 하면 두 번 일함 | 출시 직전 또는 출시 후 | 영어 1개(+한국어). 텍스트는 처음부터 코드에 하드코딩하지 말고 파일로 분리만 해둘 것 |
| 업적/클라우드 세이브/도전과제 | Steamworks SDK 연동은 게임 완성 후 1~2일 작업 | 빌드가 돌아간 뒤 | 없어도 Steam 출시 가능 |
| DLC·인앱결제·시즌패스 | 본편도 안 팔린 상태에서 무의미 | 본편이 팔린 뒤 | — |
| 커스텀 물리 통합(deterministic lockstep 등) | 멀티/리플레이 전용 | 필요할 때 | — |
2. 웹/백엔드 개발과 무엇이 근본적으로 다른가
당신은 이미 프로그래밍을 안다. 그래서 필요한 건 문법이 아니라 패러다임 차이다.
표 2 — 웹/백엔드 vs 게임
| 축 | 웹/백엔드 | 게임 |
|---|---|---|
| 실행 모델 | 요청-응답. 이벤트가 올 때만 깨어남 | 초당 60번 도는 무한 루프가 프로그램의 심장. 아무 일이 없어도 계속 돈다 |
| 시간 | 대체로 무시. now() 정도 |
모든 것이 시간의 함수. delta를 빼먹으면 PC 성능마다 게임 속도가 달라진다 |
| 상태 | 요청 간 stateless를 지향 | 전부 stateful. 메모리에 살아있는 월드가 곧 게임 |
| 지연 예산 | 200ms 응답이면 훌륭 | 16.6ms 안에 모든 걸 끝내야 60FPS. 1프레임 넘기면 사용자가 즉시 느낌 |
| 정확성 기준 | 명세 충족 = 성공 | "재미있는가"가 유일한 기준. 명세대로 만들어도 재미없으면 실패 |
| 테스트 | 단위/통합 테스트 자동화 | 자동화가 매우 어려움. 직접 플레이해서 손끝 감각으로 판정 |
| GC / 메모리 | 가끔 튀어도 됨 | GC 스파이크 = 프레임 드랍 = 즉시 체감. 매 프레임 할당을 피함 |
| 배포 | 서버에 푸시, 즉시 롤백 가능 | 바이너리를 사용자 PC에 배포. 롤백 어렵고 플랫폼별 서명·공증 필요 |
| 버그의 성격 | 500 에러, 데이터 불일치 | "점프가 뻑뻑하다", "적이 벽에 낀다" — 재현 조건이 물리적 |
for루프 안에서 시간을 기다릴 수 없다.sleep은 금지, 모든 대기는 "N프레임 동안 상태 유지"로 표현한다.- 코드가 맞아도 화면을 보기 전엔 맞는지 모른다. 이게 AI 협업에서 가장 큰 제약이다(7장).
- "완성"의 정의가 없다. 정하지 않으면 영원히 안 끝난다(6장).
3. 개념 최소 집합 9가지
각 항목의 정의 → 왜 최소 집합인가 → 최소 코드 → 초보가 막히는 지점 순서. 코드는 개념 설명은 JavaScript, 실전은 GDScript(Godot 4.x) 로 제시한다.
3.1 게임 루프 (Game Loop)
입력 수집 → 상태 갱신 → 화면 그리기를 수행한다.
왜 최소 집합인가: 게임의 모든 코드는 "이 루프의 어느 지점에서 실행되는가"로 위치가 정해진다. 이걸 모르면 코드를 어디 둬야 할지 자체를 모른다.
// 개념용 — 브라우저의 가장 기본적인 게임 루프
let last = performance.now();
function frame(now) {
const dt = Math.min((now - last) / 1000, 0.1); // 초 단위. 스파이크는 0.1s로 클램프
last = now;
processInput(); // 1) 입력: 이번 프레임의 키/마우스 상태를 읽는다
update(dt); // 2) 갱신: 위치·체력·타이머 등 월드 상태를 dt만큼 진행
render(); // 3) 렌더: 갱신된 상태를 화면에 그린다
requestAnimationFrame(frame); // 4) 다음 프레임 예약(모니터 주사율에 동기)
}
requestAnimationFrame(frame);
Godot에서는 이 루프를 엔진이 돌린다. 당신은 콜백만 채운다.
extends Node2D
func _process(delta: float) -> void:
# 매 렌더 프레임 호출 (가변 timestep). 연출·UI·카메라·타이머용
pass
func _physics_process(delta: float) -> void:
# 고정 주기 호출 (기본 60Hz). 이동·충돌·물리용
pass
_process와_physics_process를 구분 못 해 캐릭터 이동을_process에 넣는다 → 프레임 레이트에 따라 충돌 판정이 흔들린다. 움직이고 부딪히는 것은 전부_physics_process.while루프로 무언가를 기다리려 한다 → 게임 전체가 멈춘다. 대기는 항상 "상태 + 타이머"로 표현한다.
3.2 델타타임과 고정 timestep
- 델타타임(delta time, dt): 직전 프레임부터 지금까지 흐른 실제 시간(초).
- 가변 timestep(variable timestep): 매 프레임 dt가 다름. 렌더링/연출에 적합.
- 고정 timestep(fixed timestep): 시뮬레이션을 항상 동일한 Δt(예: 1/60초)로 진행. 물리/충돌에 적합.
왜 최소 집합인가: 델타타임을 안 곱하면 144Hz 모니터에서 캐릭터가 2.4배 빨라진다. 이건 버그가 아니라 재앙이고, 출시 후 리뷰에서 바로 지적된다. 그리고 고정 timestep을 모르면 물리가 PC마다 다르게 동작한다.
# 나쁨: 프레임마다 5픽셀 → 60FPS면 초당 300px, 144FPS면 초당 720px
position.x += 5
# 좋음: 초당 300픽셀 → 어떤 프레임레이트에서도 동일한 속도
position.x += 300.0 * delta
왜 곱하는가: 이동량 = 속도 × 시간. delta가 그 "시간"이다. 코드에 등장하는 모든 상수는 "프레임당"이 아니라 "초당"으로 정의하는 습관을 들여라(SPEED = 300.0 # px/s, GRAVITY = 980.0 # px/s²).
고정 timestep이 왜 필요한가: dt가 들쭉날쭉하면 물리 적분 오차가 달라져, 같은 입력인데도 점프 높이가 미묘하게 달라지고, dt가 크게 튀면 터널링(tunneling — 빠른 물체가 얇은 벽을 한 프레임에 통과해버림) 이 발생한다. 그래서 물리는 항상 같은 Δt로 여러 번 나눠 돌린다.
// 개념용 — accumulator 패턴 (Godot/Unity는 엔진이 내부적으로 이걸 한다)
const STEP = 1 / 60;
let acc = 0;
function frame(now) {
const dt = Math.min((now - last) / 1000, 0.25);
last = now;
acc += dt;
while (acc >= STEP) { // 밀린 만큼 고정 간격으로 따라잡는다
fixedUpdate(STEP); // 물리·충돌
acc -= STEP;
}
render(acc / STEP); // 남은 잔여분으로 보간(interpolation)하면 부드러워짐
requestAnimationFrame(frame);
}
Godot 대응
_physics_process(delta)= 고정 timestep.delta는 항상1/Engine.physics_ticks_per_second(기본 1/60).- 프로젝트 설정 → Physics → Common → Physics Ticks per Second로 조절.
- Godot 4에는 Physics Interpolation 옵션이 있어, 물리 60Hz + 렌더 144Hz에서도 부드럽게 보이게 해준다.
_physics_process에서도delta를 곱해야 하나? → 곱해라. 값이 상수라도, 나중에 틱레이트를 바꾸면 전부 깨진다. 단, Godot의move_and_slide()는velocity(초당 단위)를 받아 내부적으로 delta를 곱하므로 velocity에는 delta를 곱하지 않는다.- 창을 드래그하거나 브레이크포인트를 걸면 dt가 몇 초로 튄다 → 반드시 클램프(
min(dt, 0.1)) 하라. 안 그러면 캐릭터가 맵 밖으로 순간이동한다.
3.3 좌표계 (Coordinate System)
왜 최소 집합인가: 엔진마다 Y축 방향이 다르다. 이걸 모르면 "위로 점프시켰는데 아래로 떨어진다"는 첫날 좌절을 겪는다. 또 스크린 좌표 ↔ 월드 좌표 변환을 모르면 마우스 클릭 처리를 못 한다.
표 3 — 엔진별 좌표계
| 엔진/환경 | 2D 원점 | 2D Y축 | 3D 규약 |
|---|---|---|---|
| Godot 4 (2D) | 좌상단 (0,0) | 아래로 + | — |
| Godot 4 (3D) | — | — | Y-up, 앞 방향 −Z, 오른손 좌표계 |
| Unity (2D) | 중앙(월드) | 위로 + | Y-up, 앞 방향 +Z, 왼손 좌표계 |
| HTML Canvas / DOM | 좌상단 | 아래로 + | — |
| Unreal | — | — | Z-up, 앞 방향 +X, 왼손 |
Godot 2D에서 점프는
velocity.y = -400(음수가 위쪽)이다. Unity 습관으로 양수를 넣으면 땅으로 파고든다.
알아둘 좌표 3종
- 로컬 좌표(local): 부모 노드 기준 상대 위치 →
position - 글로벌/월드 좌표(global): 월드 원점 기준 절대 위치 →
global_position - 스크린 좌표(screen/viewport): 화면 픽셀 기준. 마우스 입력이 여기로 들어온다
# 마우스가 가리키는 월드 지점을 향해 회전 (2D)
func _process(_delta: float) -> void:
var mouse_world: Vector2 = get_global_mouse_position() # 스크린→월드 변환 완료된 값
look_at(mouse_world)
# 카메라가 있을 때 수동 변환이 필요하면
# var world = get_viewport().get_canvas_transform().affine_inverse() * screen_pos
- 부모 노드가 회전/스케일되어 있으면
position과global_position이 전혀 다른 값이 된다. 충돌·타겟팅 계산은 거의 항상 global로 해야 한다. - 해상도 대응: 1920×1080 기준으로 만들고, 프로젝트 설정 → Display → Window → Stretch Mode =
canvas_items, Aspect =expand로 두면 대부분의 모니터에서 무난하다. UI는 앵커(anchor — UI 요소를 부모의 어느 모서리에 고정할지 정하는 기준점) 를 반드시 설정하라.
3.4 스프라이트와 에셋 파이프라인
- 스프라이트(sprite): 게임 화면에 그려지는 2D 이미지 한 장. 캐릭터, 총알, 배경 조각 등.
- 텍스처 아틀라스(texture atlas) / 스프라이트 시트(sprite sheet): 여러 스프라이트를 한 장의 큰 이미지에 모아둔 것.
- 드로우콜(draw call): CPU가 GPU에게 "이거 그려라"라고 보내는 명령 1건. 개수가 성능을 좌우한다.
- 배칭(batching): 같은 텍스처를 쓰는 여러 스프라이트를 드로우콜 1건으로 묶어 보내는 최적화.
- 타일맵(tilemap): 격자 칸에 작은 타일 이미지를 배치해 레벨을 구성하는 방식.
왜 최소 집합인가: 게임의 눈에 보이는 것 전부가 스프라이트다. 그리고 에셋 제작이 1인 개발의 최대 병목이라, 어디서 구하고 어떤 라이선스인지 아는 것이 코드보다 중요할 때가 많다.
아틀라스를 왜 쓰는가: 텍스처를 바꿀 때마다 드로우콜이 끊긴다. 캐릭터·적·이펙트가 한 장에 있으면 수백 개 스프라이트를 한 번에 그린다. → 동일 아틀라스 사용 = 자동 배칭. (다만 초반엔 신경 쓰지 마라. 1장 스킵 목록의 "최적화" 항목 참조.)
# 스프라이트 시트로 애니메이션: AnimatedSprite2D 사용이 가장 쉽다
# 에디터에서 SpriteFrames 리소스에 프레임 등록 후:
func _ready() -> void:
$AnimatedSprite2D.play("idle")
func on_move_start() -> void:
$AnimatedSprite2D.play("run")
$AnimatedSprite2D.flip_h = velocity.x < 0.0 # 좌우 반전
픽셀아트를 쓴다면 반드시 할 3가지
- 텍스처 임포트 설정 → Filter 끄기(Nearest). 안 그러면 픽셀이 뭉개진다.
- 프로젝트 설정 → Rendering → Textures → Canvas Textures → Default Texture Filter = Nearest.
- 카메라/위치를 정수 픽셀에 스냅(선택). 안 하면 픽셀이 떨린다.
에셋 조달 전략 (1인 개발 현실론)
| 방법 | 장점 | 함정 |
|---|---|---|
| CC0 무료 팩 (Kenney.nl 등) | 비용 0, 상업 이용 자유 | 다른 게임과 똑같아 보임 → 색조·이펙트로 차별화 |
| 에셋 구매 (itch.io, Humble, Godot Asset Store) | 품질·일관성 | 라이선스 확인 필수: 재배포 금지/크레딧 필수/AI 학습 금지 조항 |
| 직접 제작 (Aseprite 등) | 완전한 일관성·차별화 | 시간. 1인 개발 최대 리스크 |
| AI 생성 | 빠름 | 저작권·Steam 공시(5장·7장) + 스타일 일관성 확보가 매우 어려움 |
| 외주 | 품질 | 비용, 커뮤니케이션 |
- 이미지 파일을 프로젝트 밖 경로에서 참조 → 빌드에 포함 안 됨. 에셋은 반드시
res://아래에 둘 것. - 원본 PSD/Aseprite 파일과 내보낸 PNG를 같은 폴더에 두면 임포트가 꼬인다.
assets_src/(원본)와res://assets/(게임용) 분리.
3.5 충돌 판정 — AABB까지만
- AABB(Axis-Aligned Bounding Box): 회전하지 않은, 축에 정렬된 사각형. 두 사각형의 겹침 판정이 비교 4번으로 끝나 가장 빠르고 단순하다.
- 브로드페이즈(broad phase): "충돌 가능성 있는 쌍"만 대충 추려내는 1차 필터.
- 내로우페이즈(narrow phase): 추려진 쌍에 대해 정밀 판정.
- 터널링(tunneling): 빠른 물체가 한 프레임 사이에 얇은 물체를 뛰어넘어 통과해버리는 현상.
왜 최소 집합인가: 게임의 상호작용 대부분이 "닿았는가"로 표현된다. 총알-적, 플레이어-바닥, 아이템-줍기. AABB만 알아도 2D 게임 90%가 커버된다. SAT(분리축 정리), 회전 충돌, 볼록다각형은 지금 필요 없다.
| 방식 | 특징 |
|---|---|
| AABB | 빠름 · 회전 불가 |
| 원(circle) | 빠름 · 회전 무관 |
| SAT(분리축 정리) | 느림 · 회전 가능 (나중에) |
// AABB 겹침 판정 — 이게 전부다
function aabbOverlap(a, b) {
return a.x < b.x + b.w &&
a.x + a.w > b.x &&
a.y < b.y + b.h &&
a.y + a.h > b.y;
}
// 왜 이게 맞나: "겹치지 않으려면 4개 축 방향 중 하나라도 완전히 떨어져야 한다"의 부정
# Godot에서는 직접 짤 필요가 거의 없다. 하지만 원리를 알면 디버깅이 된다.
func aabb_overlap(a: Rect2, b: Rect2) -> bool:
return a.intersects(b)
# 실전: 물리 레이어/마스크를 쓴 신호 기반 처리
extends Area2D
func _ready() -> void:
body_entered.connect(_on_body_entered)
func _on_body_entered(body: Node2D) -> void:
if body.is_in_group("player"):
body.take_damage(1)
queue_free() # 총알 제거
Godot의 충돌 바디 3종 — 이것만 구분하면 된다
| 노드 | 용도 | 특징 |
|---|---|---|
Area2D | 감지 전용 (아이템 줍기, 피격 판정, 트리거존) | 밀어내지 않음. 신호(body_entered)로 알림 |
CharacterBody2D | 플레이어·적 | 직접 velocity 제어 + move_and_slide(). 게임필 튜닝에 최적 |
StaticBody2D | 벽·바닥 | 안 움직이는 충돌체 |
(RigidBody2D) | 물리 시뮬레이션이 코어일 때만 | 엔진이 힘으로 굴림. 플랫포머 주인공에 쓰면 조작감이 미끄덩해진다 |
물리 레이어(layer)와 마스크(mask): Godot에서 collision_layer는 "나는 어떤 층에 있는가", collision_mask는 "나는 어떤 층을 감지하는가". 초보가 가장 많이 헤매는 설정이다. 프로젝트 설정 → Layer Names에서 레이어에 이름을 붙여두면(1=player, 2=enemy, 3=world, 4=bullet) 실수가 급감한다.
터널링 대책: CharacterBody2D의 move_and_slide()는 내부적으로 슬라이드 판정을 하므로 대개 안전하다. 총알처럼 매우 빠른 것은 레이캐스트(RayCast — 이전 위치에서 현재 위치까지 선을 쏴서 사이에 뭐가 있었는지 검사) 로 처리하거나, RigidBody2D라면 continuous_cd를 켠다. 세 가지 해결책: 레이캐스트로 선분 검사 / 고정 timestep 유지 / Continuous CD 켜기.
3.6 입력 처리 (+ 인풋 버퍼·코요테 타임)
- 폴링(polling): 매 프레임 "지금 이 키가 눌려있나?"를 물어보는 방식.
- 이벤트(event): 키가 눌린 순간에 콜백이 오는 방식.
- 액션 매핑(action map): 물리 키(Space)가 아니라 논리 행동("jump")에 코드를 붙이는 것.
- 인풋 버퍼(input buffer): 조건이 안 맞을 때 들어온 입력을 잠깐 기억했다가, 곧 조건이 맞으면 실행해주는 장치.
- 코요테 타임(coyote time): 발판에서 떨어진 직후 아주 짧은 시간 동안 점프를 허용해주는 장치.
왜 최소 집합인가: 키보드+게임패드 동시 지원, 키 리매핑은 Steam 출시에 사실상 필수인데, 액션 매핑을 안 쓰면 나중에 전부 다시 짜야 한다. 그리고 인풋 버퍼/코요테 타임은 "조작감이 좋다"는 평의 8할을 만드는 저비용 고효율 장치다.
# 프로젝트 설정 > Input Map 에서 "jump", "move_left", "move_right" 액션을 만들고
# 각 액션에 키보드 키와 게임패드 버튼을 둘 다 등록해두면 코드는 하나로 끝난다.
extends CharacterBody2D
const SPEED := 300.0 # px/s
const JUMP_VELOCITY := -420.0 # px/s (Godot 2D는 위쪽이 음수)
const GRAVITY := 980.0 # px/s^2
const BUFFER_TIME := 0.12 # 초
const COYOTE_TIME := 0.10 # 초
var _jump_buffer := 0.0
var _coyote := 0.0
func _physics_process(delta: float) -> void:
# 1) 입력 (폴링)
var dir := Input.get_axis("move_left", "move_right") # -1.0 ~ 1.0, 게임패드 아날로그도 자동 처리
# 2) 인풋 버퍼: 눌린 순간 타이머를 채우고, 이후 매 프레임 감소
if Input.is_action_just_pressed("jump"):
_jump_buffer = BUFFER_TIME
else:
_jump_buffer = maxf(0.0, _jump_buffer - delta)
# 3) 코요테 타임: 땅에 있으면 채우고, 공중이면 감소
_coyote = COYOTE_TIME if is_on_floor() else maxf(0.0, _coyote - delta)
# 4) 이동
velocity.x = dir * SPEED
velocity.y += GRAVITY * delta
# 5) 점프: "버퍼가 남아있고 && 코요테가 남아있으면"
if _jump_buffer > 0.0 and _coyote > 0.0:
velocity.y = JUMP_VELOCITY
_jump_buffer = 0.0
_coyote = 0.0
# 6) 가변 점프 높이: 버튼을 일찍 떼면 짧게 뛴다
if Input.is_action_just_released("jump") and velocity.y < 0.0:
velocity.y *= 0.45
move_and_slide() # velocity(초당 단위)를 받아 내부에서 delta 적용 + 충돌 슬라이드
위 40줄이 "조작감 좋은 플랫포머"의 90% 다. 버퍼·코요테·가변 점프 이 3개를 빼면 같은 코드가 "뻑뻑하다"는 평을 받는다.
폴링 vs 이벤트, 언제 뭘 쓰나
- 폴링(
Input.is_action_pressed): 이동, 조준, 지속 사격 —_physics_process안에서. - 이벤트(
_input(event)/_unhandled_input(event)): 메뉴 조작, 텍스트 입력, UI — 프레임 손실 없이 한 번만 처리해야 하는 것.
Input.is_key_pressed(KEY_SPACE)처럼 물리 키를 직접 검사 → 게임패드 지원·키 리매핑·다른 키보드 배열에서 전부 깨진다. 처음부터 액션 매핑을 써라._process에서is_action_just_pressed를 읽으면 물리 틱과 어긋나 입력이 씹힐 수 있다 → 위처럼 버퍼로 흡수.
3.7 상태머신 (Finite State Machine, FSM)
왜 최소 집합인가: 상태머신 없이 if is_jumping and not is_attacking and is_grounded ... 를 쌓기 시작하면 적 3마리째에 코드가 붕괴한다. 게임 로직은 본질적으로 상태 기계다. 그리고 AI에게 코드를 맡길 때 가장 잘 통하는 구조이기도 하다(구조가 명확해 지시가 정확해짐).
extends CharacterBody2D
enum State { IDLE, RUN, JUMP, FALL, HURT, DEAD }
var state: State = State.IDLE
var _state_time := 0.0 # 현재 상태에 머문 시간 — 연출/무적시간에 유용
func change_state(next: State) -> void:
if next == state:
return
_exit_state(state)
state = next
_state_time = 0.0
_enter_state(state)
func _enter_state(s: State) -> void:
match s:
State.RUN: $AnimatedSprite2D.play("run")
State.JUMP: $AnimatedSprite2D.play("jump"); velocity.y = JUMP_VELOCITY
State.HURT: $AnimatedSprite2D.play("hurt"); modulate = Color.RED
State.DEAD: $AnimatedSprite2D.play("die"); set_physics_process(false)
_: $AnimatedSprite2D.play("idle")
func _exit_state(s: State) -> void:
if s == State.HURT:
modulate = Color.WHITE
func _physics_process(delta: float) -> void:
_state_time += delta
match state:
State.IDLE:
if absf(velocity.x) > 1.0: change_state(State.RUN)
elif not is_on_floor(): change_state(State.FALL)
State.RUN:
if is_zero_approx(velocity.x): change_state(State.IDLE)
State.HURT:
if _state_time > 0.4: change_state(State.IDLE) # 경직 해제
State.DEAD:
pass
move_and_slide()
규칙 3가지
- 상태는 한 번에 하나. 두 개가 동시에 필요하면 상태를 잘못 나눈 것이거나, 별도 축(예: 무적 타이머)으로 빼야 한다.
- 전이 조건은 한 곳에만 쓴다. 여기저기서
state = X를 직접 대입하면 상태머신의 이점이 사라진다. 반드시change_state()경유. - enter/exit에서 정리하라. 이펙트·타이머·색 변경은 exit에서 되돌린다. 안 그러면 "가끔 캐릭터가 빨간 채로 남는" 버그가 난다.
언제 비헤이비어 트리로 넘어가나: 적 종류가 10개 넘고 행동이 조합적으로 재사용될 때. 첫 게임에선 오지 않는다.
3.8 씬 관리 (Scene Management)
- 씬(scene): 노드들의 트리를 묶어 하나의 단위로 저장한 것. Godot에선 레벨도 씬, 적 한 마리도 씬, 버튼 하나도 씬이다(= 재사용 가능한 프리팹 개념).
- 씬 전환: 현재 씬 트리를 버리고 다른 씬을 로드하는 것.
- 오토로드/싱글턴(autoload): 씬이 바뀌어도 살아남는 전역 노드.
왜 최소 집합인가: "타이틀 → 게임 → 결과 → 타이틀" 흐름과 "씬이 바뀌어도 유지되어야 하는 데이터(점수, 설정, 진행도)"를 다루지 못하면 게임 구조 자체가 안 선다.
# --- Game.gd : Project Settings > Autoload 에 "Game" 이름으로 등록 ---
extends Node
var score := 0
var current_level := 1
var settings := {"master_volume": 0.8, "fullscreen": false}
func goto_level(n: int) -> void:
current_level = n
# deferred: 현재 프레임의 노드 처리 중 트리를 갈아엎지 않도록 안전하게 지연 실행
get_tree().change_scene_to_file("res://scenes/levels/level_%02d.tscn" % n)
func game_over() -> void:
get_tree().change_scene_to_file("res://scenes/ui/game_over.tscn")
# --- 어느 씬에서든 접근 ---
func _on_coin_collected() -> void:
Game.score += 10
씬 구성 관례 (첫 게임 기준)
res://
scenes/
main_menu.tscn
levels/level_01.tscn ...
ui/hud.tscn, pause_menu.tscn, game_over.tscn
actors/player.tscn, enemy_slime.tscn
fx/explosion.tscn
scripts/
assets/ (sprites/, sfx/, music/, fonts/)
동적 인스턴스화 (총알·적 생성)
const BULLET := preload("res://scenes/actors/bullet.tscn") # preload = 컴파일 타임 로드
func shoot() -> void:
var b := BULLET.instantiate()
b.global_position = $Muzzle.global_position
b.direction = (get_global_mouse_position() - global_position).normalized()
get_tree().current_scene.add_child(b) # 플레이어의 자식으로 붙이면 같이 움직여버린다
- 총알을 발사자의 자식으로 붙여서, 캐릭터가 움직이면 총알도 따라 움직인다 → 월드(현재 씬)에 붙여라.
- 씬 전환 중
change_scene_to_file을 노드의_process안에서 즉시 호출 → 크래시/경고.call_deferred또는 신호 처리 후 호출. - 로딩이 길어지는 큰 씬 →
ResourceLoader.load_threaded_request()로 비동기 로딩. 첫 게임에선 대개 불필요.
3.9 세이브와 직렬화
왜 최소 집합인가: 세이브 없는 게임은 Steam에서 사실상 상품이 안 된다(짧은 아케이드라도 최고점수·설정은 저장해야 한다). 그리고 세이브 포맷을 나중에 바꾸면 기존 플레이어 데이터가 날아간다 → 처음부터 버전 필드를 넣는 게 핵심.
# --- SaveSystem.gd (Autoload) ---
extends Node
const SAVE_PATH := "user://save.json"
const SAVE_VERSION := 1
func save_game() -> void:
var data := {
"version": SAVE_VERSION,
"score": Game.score,
"level": Game.current_level,
"unlocked": Game.unlocked_items, # Array
"settings": Game.settings,
"saved_at": Time.get_unix_time_from_system(),
}
var f := FileAccess.open(SAVE_PATH, FileAccess.WRITE)
if f == null:
push_error("save failed: %s" % FileAccess.get_open_error())
return
f.store_string(JSON.stringify(data, "\t")) # "\t" = 사람이 읽을 수 있게 들여쓰기
f.close()
func load_game() -> bool:
if not FileAccess.file_exists(SAVE_PATH):
return false
var f := FileAccess.open(SAVE_PATH, FileAccess.READ)
var text := f.get_as_text()
f.close()
var parsed: Variant = JSON.parse_string(text)
if not (parsed is Dictionary):
push_warning("save file corrupted — starting fresh")
return false
var data: Dictionary = parsed
var v: int = int(data.get("version", 0))
if v < SAVE_VERSION:
data = _migrate(data, v) # 구버전 세이브 마이그레이션
Game.score = int(data.get("score", 0))
Game.current_level = int(data.get("level", 1))
Game.unlocked_items = data.get("unlocked", [])
Game.settings = data.get("settings", Game.settings)
return true
func _migrate(data: Dictionary, from_version: int) -> Dictionary:
if from_version < 1:
data["unlocked"] = [] # v0에는 없던 필드 기본값 채우기
data["version"] = SAVE_VERSION
return data
경로 규칙 (매우 중요)
| 경로 | 의미 | 쓰기 가능? |
|---|---|---|
res:// | 프로젝트/게임 내부 리소스 | 불가(내보낸 빌드에선 읽기 전용 pck 안에 있음) |
user:// | OS별 사용자 데이터 폴더 | 가능 — 세이브는 반드시 여기 |
user://의 실제 위치:
- macOS:
~/Library/Application Support/Godot/app_userdata/<프로젝트명>/ - Windows:
%APPDATA%\Godot\app_userdata\<프로젝트명>\ - (프로젝트 설정에서
application/config/use_custom_user_dir로 커스터마이즈 가능)
- 에디터에선
res://에 파일이 써져서 잘 되다가, 내보낸 빌드에서만 세이브가 실패한다. → 원인은 100%res://쓰기. - 노드 객체를 통째로 저장하려 함 → 저장할 데이터는 순수 값(Dictionary/Array/숫자/문자열)으로 분리하라. 이걸 지키면 나중에 AI에게 세이브 코드를 맡기기도 쉽다.
- JSON은 사용자가 열어서 고칠 수 있다(치트). 신경 쓰이면
FileAccess.open_encrypted_with_pass()또는 바이너리(var_to_bytes)를 쓰되, 싱글플레이 인디에선 대개 무시해도 된다. - Steam 클라우드 세이브는
user://경로를 Steamworks에 등록하면 코드 변경 없이 동작한다(Steam Auto-Cloud).
3.10 보너스: 게임필(주스) — 최소한의 재미 장치
- 게임필(game feel): 조작에 대한 반응이 손끝에서 느껴지는 감각.
- 주스(juice): 게임 규칙을 바꾸지 않으면서 피드백을 과장해 만족감을 올리는 연출 기법.
왜 최소 집합인가: 같은 코드가 주스 유무에 따라 "밋밋한 습작"과 "손맛 좋은 게임"으로 갈린다. 투자 대비 효과가 게임 개발 전체에서 가장 크다. 그리고 이건 AI가 대신 해주지 못하는 영역이라(7장) 당신이 알아야 한다.
비용 대비 효과 순으로 8가지
| 기법 | 설명 | 구현 난이도 |
|---|---|---|
| 효과음 | 점프·피격·획득·클릭에 소리 | ★ (가장 효과 큼) |
| 히트스톱(hit stop) | 타격 순간 0.05~0.1초 프레임 정지 | ★ |
| 화면 흔들림(screen shake) | 폭발·피격 시 카메라 랜덤 오프셋 | ★ |
| 스쿼시&스트레치 | 점프 시 세로로 늘리고 착지 시 납작하게 | ★★ |
| 피격 플래시 | modulate를 흰색으로 2프레임 | ★ |
| 파티클 | 착지 먼지, 피격 파편 | ★★ |
| 트윈/이징(tween/easing) | UI가 튕기며 등장 | ★★ |
| 화면 표시 데미지 숫자 | 수치를 띄우고 위로 사라짐 | ★★ |
# 히트스톱 + 화면 흔들림 (Godot 4)
func hit_stop(duration := 0.06) -> void:
Engine.time_scale = 0.0
await get_tree().create_timer(duration, true, false, true).timeout # ignore_time_scale=true
Engine.time_scale = 1.0
func shake(camera: Camera2D, amount := 6.0, duration := 0.2) -> void:
var t := 0.0
var base := camera.offset
while t < duration:
camera.offset = base + Vector2(randf_range(-amount, amount), randf_range(-amount, amount))
t += get_process_delta_time()
await get_tree().process_frame
camera.offset = base
4. 엔진 선택 — 결론: Godot 4.7.x
4.1 판단 조건 (당신의 상황)
- Steam 출시 / macOS + Windows 동시
- 1인 개발
- AI(Claude Code) 보조 개발 — 이게 판단을 크게 바꾼다
- 게임 개발 경험 0
표 4 — 엔진 비교표
| 항목 | Godot 4.7 | Unity 6 | Unreal 5 | 웹 스택(Phaser/PixiJS + Electron·Tauri) |
|---|---|---|---|---|
| 라이선스/비용 | MIT. 완전 무료, 로열티 0 | Personal 무료(회사 연매출·펀딩 $200k 이하). 초과 시 Pro 유료. Pro/Enterprise 가격 2026-01-12부터 5% 인상 | 무료. 평생 총매출 $1M 초과분에 5% 로열티(Epic Games Store 판매는 로열티 면제) | 무료(오픈소스) |
| 설치 크기 | 약 100~200MB(단일 실행파일) | 수 GB(Hub + 에디터 + 모듈) | 수십 GB(+ 소스 빌드 시 100GB↑) | 수백 MB(node_modules) |
| 씬/에셋 포맷 | .tscn/.tres = 텍스트(INI 유사). Git diff·머지 가능. AI가 직접 읽고 수정 가능 | .unity/.prefab = YAML(텍스트지만 GUID 참조로 사람이 읽기 어려움), .meta 파일 필수 | .uasset = 바이너리. diff 불가 | 전부 코드(가장 텍스트 친화적) |
| Git 친화성 | ★★★★★ | ★★★ (LFS·머지툴 필요) | ★★ (바이너리, LFS 필수) | ★★★★★ |
| AI 코딩 도구 적합성 | ★★★★★ — GDScript는 Python 유사 문법에 파일 단위가 작고, 씬이 텍스트라 AI가 전체 맥락을 볼 수 있음 | ★★★★ — C#은 AI가 아주 잘 씀. 다만 작업의 절반이 에디터 GUI 조작이라 AI가 못 함 | ★★ — C++은 빌드가 무겁고, 블루프린트(비주얼 스크립팅)는 바이너리라 AI가 손댈 수 없음 | ★★★★★ — 순수 코드 |
| 2D 역량 | ★★★★★ (전용 2D 렌더러) | ★★★★ | ★★ (3D 엔진에 2D 얹은 구조) | ★★★★ |
| 3D 역량 | ★★★ (인디 규모는 충분, AAA는 아님) | ★★★★ | ★★★★★ | ★ |
| Mac에서 Win/Mac 빌드 | ★★★★★ 익스포트 템플릿 하나로 전 플랫폼 | ★★★★ (모듈 설치 필요) | ★★★ (무겁고 크로스 컴파일 제약) | ★★★★ |
| Steam 연동 | GodotSteam(GDExtension) — 커뮤니티 제작, 성숙 | 공식 Steamworks.NET / Facepunch.Steamworks | 공식 Online Subsystem Steam | greenworks 등, 손이 많이 감 |
| Steam 실적 | Brotato, Cassette Beasts, Dome Keeper, Halls of Torment, Buckshot Roulette 등 | 압도적 다수 | 대형·고사양 위주 | 소수 |
| 학습 자료량 | 중간(4.x 기준 자료가 계속 늘어나는 중) | 최다(단, 구버전 자료 혼재가 오히려 함정) | 중간 | 많음 |
| 에셋스토어 | 작음 | 최대 | 큼(Fab) | 없음 |
| 취업 시장 | 작음 | 큼 | 큼 | — |
| 컴파일 대기 | 없음(GDScript는 즉시 실행) | C# 컴파일 수 초~수십 초 | C++ 빌드 수 분~수십 분 | 없음(HMR) |
4.3 추천: Godot 4.7.x + GDScript
이유 4가지
- AI 협업 효율이 압도적이다. 이게 결정적이다. Godot 프로젝트는 씬(
.tscn), 리소스(.tres), 스크립트(.gd)가 전부 텍스트 파일이다. Claude Code가grep으로 프로젝트 전체를 읽고, 씬 구조를 파악하고, 노드를 추가하는 diff를 만들 수 있다. Unity는 작업의 상당 부분이 인스펙터 GUI에서 값을 드래그하는 것이고, Unreal의 블루프린트는 아예 바이너리라 AI가 볼 수 없다. - 반복 주기(iteration loop)가 가장 짧다. F5 누르면 즉시 실행. 컴파일 대기가 없다는 것은 "AI가 코드 수정 → 실행 → 피드백"을 하루 100번 돌릴 수 있다는 뜻이다. Unreal에서 이걸 하면 하루 10번이다.
- 라이선스 리스크가 0이다. Unity는 2023년 9월 Runtime Fee(설치 횟수당 과금) 발표로 업계 신뢰를 크게 잃었고 2024년 9월 철회했지만, 정책이 바뀔 수 있다는 사실 자체가 1인 개발자에겐 리스크다. Godot은 MIT다. 바뀔 게 없다.
- 2D 인디 게임에 필요한 모든 것이 이미 있다. TileMapLayer, AnimatedSprite2D, 물리, 오디오, UI, 파티클, 그리고 macOS 공증(notarization)이 익스포트 프리셋에 내장되어 있다(5.3절). 이건 초보에게 생각보다 큰 가치다.
이 선택의 대가 (trade-off) — 반드시 알고 가라
| 대가 | 실제 영향 | 완화책 |
|---|---|---|
| 3D 고사양은 약하다 | 2D 첫 게임엔 영향 없음 | 3D를 하고 싶다면 스타일라이즈드 저폴리로 |
| 에셋스토어가 작다 | 즉석 해결책이 적음 | itch.io / Kenney / 직접 구현(AI 보조로 커버 가능) |
| 튜토리얼이 Unity보다 적다 | 검색으로 답이 안 나올 때가 있음 | 공식 문서 품질이 매우 좋다. + AI에게 질문 |
| 커뮤니티 플러그인 의존(GodotSteam 등) | Godot 메이저 업데이트 시 지연 가능 | 버전을 고정하고 출시 전엔 엔진 업그레이드 금지 |
| C# 지원은 있지만 GDScript보다 배포 제약 | 웹 익스포트 등 일부 제약 | GDScript를 써라. C# 이점은 첫 게임엔 없다 |
언제 이 추천을 뒤집나
- 3D 고사양 비주얼이 게임의 핵심 판매 포인트다 → Unreal
- 이미 C#/Unity 경험이 있고 그 자산을 쓰고 싶다 → Unity
- 모바일 광고 SDK·라이브서비스가 목표다 → Unity
- 게임이 아니라 "웹 기술로 만든 인터랙티브 작품"에 가깝다 → 웹 스택 + Tauri
4.4 Godot 설치 후 첫날 할 일 (체크리스트)
- ☐ godotengine.org에서 Standard 버전(C# 아님) 다운로드 — Mac은 앱 하나로 끝
- ☐ Godot 에디터에서 Export Templates 설치 (Editor → Manage Export Templates)
- ☐ 새 프로젝트 → 렌더러 Forward+(데스크톱 전용이면 OK) 또는 2D 전용이면 Compatibility도 무난
- ☐ Git 저장소 생성 +
.gitignore에.godot/,export_presets.cfg(민감정보 포함 시),*.tmp추가 - ☐ 프로젝트 설정 → Display → Window → Stretch Mode
canvas_items, Aspectexpand - ☐ Input Map에
move_left/move_right/jump/pause액션 등록(키보드+게임패드 둘 다) - ☐ 공식 튜토리얼 "Your first 2D game"(Dodge the Creeps) 를 손으로 1회 완주 — 2~3시간. 이것만은 AI 없이 직접 하라. 에디터 조작 감각이 없으면 이후 AI에게 지시조차 못 한다.
5. Steam 출시에 실제로 필요한 것
5.1 표 5 — 비용 총정리
| 항목 | 금액 | 주기 | 필수? | 비고 |
|---|---|---|---|---|
| Steam Direct 등록비 | $100 USD | 앱(게임) 1개당 1회 | 필수 | 환불 불가. 단, 해당 상품이 조정 총매출 $1,000 달성 후 지급분에 회수(recoupable) 됨 |
| Apple Developer Program | $99 USD | 연 1회 | Mac 빌드를 낸다면 필수 | 공증(notarization)에 필요. 5.3절 |
| Windows 코드서명 인증서(OV) | 약 $200~300 | 연 | Steam 배포만이면 선택 | 자체 사이트/itch.io 직접 배포 시 권장. 5.4절 |
| Windows 코드서명(EV) | 약 $300~500 | 연 | 선택 | 2024년 3월 이후 EV의 SmartScreen 즉시 통과 특권은 사라짐 — OV로 충분 |
| 미국 세금 서류(W-8BEN) | 0 | — | 필수 | Steamworks 온보딩에서 작성. 한미 조세조약으로 원천징수 감면 가능 |
| 은행/환전 수수료 | 변동 | — | — | Steam은 USD로 송금 |
| 스토어 아트 외주(선택) | $50~500 | 1회 | 선택 | 캡슐 이미지 품질이 클릭률을 크게 좌우 |
5.2 출시 타임라인과 심사 흐름
핵심 제약 3개 (일정 계산에 반드시 반영)
- Steam Direct 수수료 결제 후 30일 대기. 이 기간은 단축·생략 불가.
- 스토어 페이지 심사 3~5 영업일. 수정 여지를 고려해 원하는 공개일 7일 전에 제출 권장.
- "Coming Soon" 스토어 페이지가 최소 2주간 라이브여야 출시 가능. (위시리스트 축적 목적)
- 빌드 심사(게임 실행 확인)는 1~5 영업일.
즉 "오늘 게임이 완성돼도 최소 30일 + 알파 후에야 출시할 수 있다." 이걸 모르고 출시일을 잡으면 반드시 어긋난다.
단계별 흐름
1. Steamworks 파트너 등록 (회사/개인 정보, 은행, 세금 서류 W-8BEN)
2. Steam Direct 수수료 $100 결제 ──► [30일 대기 시작]
3. AppID 발급
4. 스토어 페이지 작성 (설명, 태그, 시스템 요구사항, 에셋 업로드, 트레일러)
5. 스토어 페이지 심사 제출 → 3~5 영업일 → "Coming Soon" 공개
──► [여기서부터 최소 2주 이상 위시리스트 수집]
6. SteamPipe로 빌드 업로드 (steamcmd + VDF 스크립트)
7. 빌드를 default 브랜치에 올리고 "출시 준비 완료" 체크리스트 제출
8. 빌드 심사 1~5 영업일 (Valve가 실제로 실행해봄)
9. 승인 후 → 당신이 직접 "출시" 버튼을 누름 (자동 출시 아님)
SteamPipe 빌드 업로드 (실무 요약)
- 도구:
steamcmd(커맨드라인 Steam 클라이언트) - 설정: VDF(Valve Data Format) 텍스트 스크립트 2종 —
app_build_<AppID>.vdf+depot_build_<DepotID>.vdf - 뎁팟(depot): 플랫폼/언어별 콘텐츠 묶음. Windows 뎁팟 / macOS 뎁팟을 따로 만든다.
- 업로드 시 파일을 약 1MB 청크로 쪼개 변경분만 전송(델타 업데이트) → 패치가 가볍다
- 브랜치(branch):
default가 공개용,beta브랜치에 먼저 올려 비밀번호를 걸고 테스트하는 것이 표준 관행
# 개략적인 업로드 명령 (실제 경로/ID는 자신의 것으로)
steamcmd +login <빌드계정> +run_app_build /path/to/app_build_1234560.vdf +quit
.exe + .pck(또는 embed), macOS는 .app 번들 또는 .zip.
.app 번들을 그대로 업로드하면 실행 권한 비트와 심볼릭 링크가 깨질 수 있다. Steamworks 문서의 macOS 관련 가이드를 따르고, 업로드 후 반드시 실제 Mac에서 Steam으로 설치해 실행 확인하라. (확인 필요: 최신 SteamPipe의 심볼릭 링크 처리 정책)
5.3 macOS 코드 서명·공증 — 초보 최대 함정
- 코드 서명(code signing): 개발자 인증서로 바이너리에 서명해 "누가 만들었고 변조되지 않았음"을 증명하는 것.
- 공증(notarization): 서명된 앱을 Apple 서버에 업로드해 악성코드 검사를 받고, 통과 증명(ticket)을 받는 절차.
- 스테이플링(stapling): 받은 공증 티켓을 앱 번들에 붙여, 오프라인에서도 검증되게 하는 것.
- Gatekeeper: macOS가 서명·공증 여부를 확인해 실행을 막는 보안 장치.
- Steam이 요구한다. Steamworks 문서: "Starting October 14th, 2019 Steam will require all new macOS Applications to be 64-bit and notarized by Apple." 그리고 Steamworks 앱 설정의 "App Bundles Are Notarized" 체크박스를 켜야 한다.
- Apple Silicon(arm64) 바이너리는 서명이 없으면 아예 실행되지 않는다. 인텔 시절처럼 "서명 없이도 돌아가는" 여지가 없다.
- 따라서 Apple Developer Program $99/년이 사실상 강제된다. Mac 빌드를 포기하지 않는 한 우회 불가.
- 게다가 공증은 Mac에서만 가능하다(
xcrun notarytool). 당신은 Mac을 쓰니 다행이지만, Windows 개발자에겐 큰 장벽이다.
필요한 것 (준비 순서)
- Apple Developer Program 가입 ($99/년). 개인(Individual)이면 여권/신분 확인. 가입 승인에 며칠 걸릴 수 있다 → 출시 2개월 전에는 가입해둘 것.
- Developer ID Application 인증서 발급 (App Store 외부 배포용). App Store용 인증서가 아니다 — 여기서 초보가 자주 틀린다.
- App Store Connect API Key(.p8 파일 + Key ID + Issuer ID) 발급. Apple ID + 앱 암호 방식보다 안정적이고 자동화에 적합.
- Xcode 또는 Command Line Tools 설치 (
xcode-select --install) —notarytool이 여기 들어있다.
Godot에서의 절차 (4.x 익스포트 프리셋 내장)
- Export Preset → macOS →
- Codesign:
Xcode codesign또는built-in, Identity에 Developer ID Application 인증서 지정 - Notarization:
Xcode notarytool선택 (구altool은 Apple이 2023년 말 지원 종료) - API UUID(Issuer ID) / API Key(.p8 파일 경로) / API Key ID 입력
- Entitlements → Debugging 비활성화 (디버깅 entitlement가 켜져 있으면 공증에 실패한다)
- Hardened Runtime 활성화
- Codesign:
- 내보내기 실행 → Godot이 서명 → Apple에 업로드 → 결과 대기(수 분~수십 분)
# 상태 확인 (Godot이 내부적으로 하는 일을 직접 볼 때)
xcrun notarytool history --key AuthKey_XXXX.p8 --key-id <KeyID> --issuer <IssuerID>
xcrun notarytool log <submission-id> --key ... --key-id ... --issuer ... # 실패 원인 확인
# 티켓 스테이플 + 최종 검증
xcrun stapler staple "MyGame.app"
spctl -a -vvv -t install "MyGame.app" # "accepted / source=Notarized Developer ID" 나오면 성공
공증 실패 단골 원인
| 원인 | 해결 |
|---|---|
| Hardened Runtime 미적용 | 익스포트 설정에서 활성화 |
| Debugging entitlement 켜짐 | 끄기 (가장 흔함) |
번들 내부의 서명 안 된 동적 라이브러리(GDExtension .dylib, GodotSteam, libsteam_api.dylib) | 내부 바이너리를 각각 서명해야 함. Godot이 자동 처리 못 하는 경우가 있다 |
| Bundle Identifier 미설정/중복 | com.yourname.yourgame 형식으로 고유하게 |
| 타임스탬프 서버 미사용 | --timestamp 필요(Godot 기본 처리) |
5.4 Windows SmartScreen·백신 오탐
결론부터: Steam으로만 배포한다면 코드 서명 인증서는 필수가 아니다. Steam 클라이언트가 다운로드한 파일에는 웹브라우저가 붙이는 Mark-of-the-Web(인터넷 출처 표식)이 붙지 않아 SmartScreen 경고가 뜨지 않는 것이 일반적이다.
그럼 언제 필요한가
- 자체 웹사이트·itch.io에서 데모/빌드를 직접 배포할 때 → 경고 없이 받게 하려면 서명 필요
- 퍼블리셔/플랫폼이 서명을 요구할 때
- 백신 오탐을 줄이고 싶을 때 (완전 해결은 아님)
최신 상황(2026-08 기준)
- OV 인증서 약 $200~300/년, EV 약 $300~500/년
- 2024년 3월 Microsoft Trusted Root Program 변경으로 EV의 "SmartScreen 즉시 통과" 특권이 사라졌다. 이제 OV·EV 모두 다운로드 수 기반으로 평판을 쌓는다 → 비싼 EV를 살 이유가 거의 없다.
- 2023년 6월부터 코드서명 키는 하드웨어 토큰 또는 클라우드 HSM에 보관해야 한다(물리 USB 배송을 받거나 클라우드 서명 서비스 이용).
- 클라우드 기반 대안(Azure Trusted Signing 등)이 더 저렴하고 CI 연동이 쉬운 경우가 있다. (확인 필요: 개인 개발자 자격 요건과 지역 제한)
백신 오탐 대응 (더 현실적인 문제)
- Godot로 만든 게임은 자체 압축·자체 추출 구조 때문에 백신이 오탐하는 사례가 종종 보고된다.
- 대응: ①서명하기 ②VirusTotal로 확인 ③오탐한 벤더에 false positive 신고 ④빌드에 UPX 등 패커를 쓰지 않기
5.5 스토어 페이지 필수 에셋
표 6 — Steam 스토어 그래픽 에셋 규격
| 에셋 | 크기(px) | 용도 | 필수 |
|---|---|---|---|
| Header Capsule | 920 × 430 | 스토어 페이지 상단, 추천 목록, 라이브러리 | 필수 |
| Small Capsule | 462 × 174 | 검색 결과, 인기 목록 (120×45 등으로 자동 축소) | 필수 |
| Main Capsule | 1232 × 706 | 스토어 홈 캐러셀 | 필수 |
| Vertical Capsule | 748 × 896 | 세일·특집 페이지 | 필수 |
| Page Background | 1438 × 810 | 페이지 배경 (미제공 시 마지막 스크린샷에서 자동 생성) | 선택 |
| 스크린샷 | 최소 1920 × 1080 (16:9) | 최소 5장 | 필수 |
| 트레일러 | 권장 1920×1080, H.264 | 게임플레이 중심 | 사실상 필수 |
| Library 자료 (Capsule 600×900, Header 920×430, Hero 3840×1240, Logo 1280×720) | — | 사용자 라이브러리 표시 | 출시 시 필요 |
규격 외에 중요한 것
- Small Capsule에서는 로고가 캡슐을 거의 꽉 채워야 한다. 검색 결과에서 120×45로 줄어들기 때문. 여기서 초보 게임은 대부분 "글씨가 안 보이는" 실수를 한다.
- 스크린샷은 컨셉아트나 마케팅 문구가 아니라 실제 게임플레이여야 한다.
- 태그(tag)와 짧은 설명(Short Description)이 노출 알고리즘에 직결된다. 유사 게임의 태그를 조사해 넣어라.
- 시스템 요구사항, 연령 등급 정보, 지원 언어, 컨트롤러 지원 여부를 정확히 기입.
5.6 Steam Deck
당신이 해야 할 일 (거의 없음)
- Valve가 자동으로 호환성 테스트를 한다. Linux 빌드가 있으면 그것을, 없거나 실패하면 Windows 빌드를 Proton으로 테스트해 더 유리한 쪽 결과를 채택한다.
- 즉 Windows 빌드만 있어도 Deck에서 돌아갈 가능성이 높다.
- Godot은 Linux 빌드도 클릭 한 번이므로 Linux 뎁팟도 같이 올려두면 이득이다(추가 비용 0).
Deck Verified를 노린다면 최소 체크리스트
- ☐ 기본 컨트롤러(게임패드) 완전 지원 — 키보드 없이 전 메뉴 조작 가능
- ☐ 1280×800 해상도에서 텍스트가 읽히는가 (Deck 화면은 작다. UI 폰트 크기가 최대 탈락 사유)
- ☐ 텍스트 입력이 필요하면 Steam 가상 키보드 호출
- ☐ 기본 그래픽 설정에서 안정적 프레임
- ☐ 실행 즉시 컨트롤러 아이콘 표시(키보드 아이콘 아님)
2D 인디 게임은 Deck Verified를 받기가 상대적으로 쉽고, Verified 뱃지는 판매에 실제로 도움이 된다. 첫 게임에서 노려볼 만한 저비용 목표다.
5.7 Steamworks SDK는 필수인가
아니다. Steamworks SDK 연동 없이도 게임을 Steam에 올리고 팔 수 있다. 다음이 필요할 때만 붙인다.
| 기능 | SDK 필요? | 비고 |
|---|---|---|
| 그냥 게임 판매/실행 | 불필요 | 빌드만 올리면 됨 |
| Steam 클라우드 세이브 | 불필요(Auto-Cloud) | Steamworks 웹 설정에서 user:// 경로만 지정 |
| 업적(Achievements) | 필요 | GodotSteam 등 |
| 통계·순위표(Leaderboard) | 필요 | |
| Steam 오버레이(Shift+Tab) | 대체로 자동 | Godot 게임에서 문제 사례 있음 — 확인 필요 |
| 리치 프레젠스, 친구 초대, 워크샵 | 필요 |
Godot에서의 방법: GodotSteam(GDExtension). 2026년 8월 기준 GodotSteam 4.7 계열이 Steamworks SDK 1.65 기반으로 제공된다. 주의점 2가지:
run_callbacks()를 매 프레임 호출해야 Steam 콜백이 처리된다.- GDExtension
.dylib가 들어가면 macOS 공증 시 내부 서명 문제가 생길 수 있다(5.3절 참조). 업적을 붙일 계획이면 공증 테스트를 SDK 붙인 상태로 다시 한 번 해봐야 한다.
6. 스코프 관리 — 첫 게임을 완성시키는 규칙
6.1 첫 게임의 현실적 규모
| 지표 | 권장 값 |
|---|---|
| 총 개발 기간 | 3~6개월 (주말 개발이면 6~9개월) |
| 플레이 타임 | 30분 ~ 2시간 |
| 핵심 시스템 개수 | 3개 이하 (예: 이동/사격, 적 스폰, 업그레이드) |
| 적 종류 | 3~5종 |
| 레벨/스테이지 | 5~15개 또는 절차 배치 1맵 |
| 캐릭터 | 1명 (선택 캐릭터 여러 명 = 함정) |
| 가격대 | $4.99 ~ $9.99 또는 무료 |
| 목표 | "수익"이 아니라 "출시 경험 1회 완주" |
6.2 표 7 — 첫 게임 장르 적합도
| 장르 | 적합? | 이유 |
|---|---|---|
| 아케이드 하이스코어 (벽돌깨기·슈팅·리듬) | ★★★★★ | 코어 루프가 30초. 콘텐츠 양이 적어도 성립 |
| 웨이브 서바이버(뱀서라이크) | ★★★★★ | 적 1종 + 무기 5종으로 시작 가능. 확장이 선형적 |
| 퍼즐(원화면 완결형) | ★★★★★ | 에셋 최소, 레벨 = 데이터. AI 보조에 최적 |
| 짧은 타워디펜스 / 오토배틀러 | ★★★★ | 시스템이 명확, 재사용 높음 |
| 짧은 플랫포머(15~20 스테이지) | ★★★★ | 게임필 학습에 최고. 단 레벨 디자인이 노동 |
| 비주얼 노벨 / 내러티브 | ★★★★ | 코드 부담 최소. 단 글 쓰는 능력이 전부 |
| 클리커/인크리멘탈 | ★★★★ | 수식과 UI가 전부. 밸런싱 학습에 좋음 |
| 로그라이크 대작(Hades급) | ★☆ | 콘텐츠 물량이 본질. 1인 3년 코스 |
| RPG (스토리·퀘스트·인벤·전투) | ★ | 시스템 6개 이상 동시 필요 |
| 오픈월드 | ☆ | 월드 제작 = 팀 단위 노동 |
| 멀티플레이 / MMO | ☆ | 난이도 3~10배 + 서버 운영비 + 동접 확보 실패 시 게임 자체가 성립 안 함 |
| 소울라이크 / 액션 대작 | ☆ | 애니메이션·모션 품질이 진입장벽 |
| 생존/제작(크래프팅) 샌드박스 | ☆ | 시스템 상호작용 폭발 |
6.3 스코프 폭발을 막는 7가지 규칙
- "완성"의 정의를 문서 첫 줄에 써라. "적 3종, 스테이지 10개, 보스 1마리, 엔딩 화면이 나오면 완성." 이게 없으면 영원히 안 끝난다.
- 기능 추가는 "교체"로만 한다. 새 아이디어가 떠오르면 MVP 목록에서 하나를 빼고 넣는다. 순증가 금지.
- 아이디어는 죽이지 말고
LATER.md에 적어라. 지우면 아까워서 자꾸 넣는다. 적어두면 마음이 놓인다. - 첫 2주 안에 "플레이 가능한 30초"를 만든다. 사각형이 사각형을 쏘는 수준이면 된다. 재미없으면 그 아이디어를 버려라. 에셋·메뉴·세이브는 그 다음.
- 에셋은 마지막에. 프로토타입은 전부 흰 사각형과 원으로. 그림이 붙는 순간 애착이 생겨 게임을 못 버린다.
- 세로 슬라이스(vertical slice)를 먼저 만든다. 레벨 20개의 뼈대보다 완성도 100%인 레벨 1개가 낫다. 그 1개를 복제해 나머지를 만든다.
- 마감일을 먼저 정한다. "6개월 후 출시"를 못 박고 거기에 콘텐츠를 맞춘다. 콘텐츠에 일정을 맞추면 무한히 늘어난다.
6.4 개발 단계 로드맵
[1주차] 엔진 학습 + 공식 튜토리얼 1개 완주 (직접, AI 없이)
[2~3주차] 프로토타입: 코어 루프 30초. 흰 사각형. "재미있나?" 판정
└─ 재미없으면 여기서 버리고 다른 아이디어로. 이게 정상이다.
[4주차] macOS 공증 테스트 1회 (빈 프로젝트로) ← 반드시 여기서!
[2~3개월] 세로 슬라이스: 레벨 1개를 출시 품질로 (에셋·사운드·UI 포함)
[3~4개월] 콘텐츠 확장: 레벨/적/업그레이드 복제·변주
[4개월차] Steam 파트너 등록 + $100 결제 (30일 시계 시작)
스토어 페이지 작성 → Coming Soon 공개 (위시리스트 수집 시작)
[5개월차] 플레이테스트(모르는 사람 5명 이상. 옆에서 말없이 관찰)
밸런싱 + 버그 수정. 데모 공개 고려(Next Fest)
[6개월차] 빌드 심사 제출 → 승인 → 출시
[출시 후] 1~2주간 버그 핫픽스가 가장 중요. 리뷰 응대.
플레이테스트 규칙: 테스터가 막히는 걸 봐도 절대 설명하지 마라. 당신이 설명해야 하는 부분은 전부 게임의 결함이다. 이게 가장 값싸고 강력한 개선 도구다.
7. AI로 게임 개발할 때의 실전 지침
표 8 — AI가 잘하는 일 vs 못하는 일
| AI에게 맡기기 좋은 일 | 왜 잘하나 |
|---|---|
| 상태머신·인벤토리·세이브/로드 등 정형화된 시스템 코드 | 패턴이 명확하고 텍스트로 완결 |
| 보일러플레이트(신호 연결, UI 바인딩, 설정 메뉴) | 반복적이고 지루한 코드 |
| 리팩터링·이름 정리·중복 제거 | 전체 파일을 한 번에 볼 수 있음 |
| 데이터 테이블 생성(적 스탯 JSON/CSV 20종) | 구조화된 대량 생성 |
| 빌드/배포 스크립트(steamcmd VDF, 공증 셸 스크립트, CI) | 문서화된 CLI 작업 |
| 순수 로직의 단위 테스트 (데미지 계산, 인벤 규칙) | 결정론적 검증 가능 |
| 에러 메시지 해석·디버깅 가설 제시 | 방대한 사례 지식 |
| 문서화, 스토어 설명문 초안, 패치노트 | 텍스트 생성 |
| 알고리즘 구현(A* 길찾기, 스폰 가중치, 이징 함수) | 잘 알려진 문제 |
| AI가 아직 못하는 일 | 왜 못하나 | 당신이 해야 할 방식 |
|---|---|---|
| 게임필 튜닝 (점프 높이, 가속도, 경직 시간) | 손끝 감각의 문제. 화면을 못 봄 | 직접 플레이하며 숫자를 0.05씩 바꿔라. 인스펙터에 @export로 노출시켜 실시간 조절 |
| 밸런싱 | "적정 난이도"는 플레이어 심리 | 자신 + 테스터 플레이 데이터 기반 |
| "재미있는가" 판정 | 근본적으로 불가 | 오직 플레이테스트 |
| 레벨 디자인의 리듬 | 공간 감각·긴장 곡선 | 손으로 배치. AI는 도구 스크립트 작성에만 |
| 에셋 제작(일관된 스타일) | 스타일 일관성 유지가 매우 어려움 | 구매 또는 직접 제작. AI는 컨셉·목업 단계까지 |
| 사운드 믹싱/타이밍 | 청각 판단 | 직접 |
| 에디터 GUI 조작 | 파일만 볼 수 있음 | 당신이 클릭. 단, Godot은 .tscn이 텍스트라 AI가 씬 파일 자체를 편집할 수는 있음(리스크 있음) |
| 실행 결과 확인 | 화면을 못 봄 | 스크린샷을 찍어 붙여주는 루프를 만들어라 |
7.2 AI 협업 5원칙 (게임 개발 특화)
- 구조는 내가 정하고, 구현은 AI에게. "적 AI 만들어줘"(❌) → "
enum State{PATROL, CHASE, ATTACK, HURT}인 FSM으로,change_state()경유 전이만 허용,_physics_process에서 처리. PATROL은 좌우 왕복, 플레이어가 200px 내면 CHASE"(⭕). 게임 코드는 구조가 90%다. - 한 번에 한 기능. 게임 코드는 서로 얽혀 있어서, 큰 변경을 맡기면 다른 기능이 조용히 망가진다(자동 테스트가 없어서 발견도 늦다). 커밋 단위를 작게.
- "보는 눈"을 AI에게 만들어줘라. AI는 게임 화면을 못 본다.
- 게임 실행 후 스크린샷을 찍어 대화에 붙여라. 이게 가장 효과적인 피드백 루프다.
- 디버그 오버레이(속도, 상태, 충돌 박스, FPS)를 초기에 만들어두면 스크린샷 한 장의 정보량이 급증한다.
print()로그를 파일로 남기고 AI에게 읽히는 것도 유효.
- 로직을 엔진에서 떼어내라(가능한 만큼). 데미지 계산, 인벤토리 규칙, 업그레이드 조합 같은 순수 함수를 별도 스크립트로 분리하면 AI가 테스트를 짤 수 있다. 엔진 노드에 얽힌 코드는 AI가 검증할 방법이 없다.
- Git을 방어선으로 써라. 게임은 "돌아가긴 하는데 느낌이 이상해진" 회귀가 잦다. 재미있게 튜닝된 수치는 커밋 메시지에 남겨라(
feat: jump feels good — JUMP_VEL -420, coyote 0.10, buffer 0.12). 나중에 이 커밋으로 돌아올 일이 반드시 생긴다.
7.3 프로토타입 우선 전략 (AI 시대의 이점)
AI의 진짜 가치는 "완성품을 대신 만들어주는 것"이 아니라 "버리는 프로토타입의 비용을 1/5로 낮추는 것" 이다.
아이디어 3개 → 각각 2~3일 프로토타입(AI 보조) → 직접 플레이 →
가장 재밌는 1개만 남기고 2개는 버린다 → 그 1개를 6개월 개발
예전엔 프로토타입 1개가 2주였다. 지금은 2일이다. 그러니 아깝지 않게 버려라. 첫 아이디어가 재미없다는 걸 3주차에 아는 것과 4개월차에 아는 것의 차이가 프로젝트의 생사를 가른다.
7.4 AI 생성 에셋 — Steam 정책과 저작권
Steam(Valve) 정책 — 2026-01-16 개정
- Valve는 AI 생성 콘텐츠를 금지하지 않는다. 다만 공시(disclosure) 를 요구한다.
- 2026년 1월 개정으로 범위가 좁아졌다: 공시 대상은 "게임에 함께 배포되어 플레이어가 소비하는(player-facing) AI 생성 콘텐츠" — 인게임 아트/사운드/내러티브, 마케팅 자료.
- 개발 도구로서의 AI(코드 어시스턴트, 컨셉아트 보조 등 "효율 향상")는 공시 초점이 아니라고 명시되었다. → Claude Code로 코드를 짜는 것 자체는 공시 대상이 아니다. (확인 필요: 최종 판단은 Steamworks 제출 폼의 최신 문구를 따를 것)
- 분류: 사전 생성(pre-generated) = 출시 전에 AI로 만든 콘텐츠(일반 콘텐츠와 동일 규칙 적용) / 실시간 생성(live-generated) = 게임 실행 중 AI가 생성(추가로 불법 콘텐츠 방지 가드레일 설명 필요).
- 공시가 정확하기만 하면 AI 사용을 이유로 게임을 거부하지는 않는다. 스토어 페이지에 AI 사용 라벨이 표시된다(2026년 기준 신작의 약 1/5).
저작권 리스크 (법률 자문 아님, 반드시 별도 확인)
| 이슈 | 요지 |
|---|---|
| 순수 AI 생성물의 저작권 | 미국 저작권청은 인간의 창작적 기여가 없는 순수 AI 산출물에 저작권을 인정하지 않는다는 입장. → 당신 게임의 AI 아트를 남이 베껴도 막을 근거가 약할 수 있다 |
| 학습 데이터 출처 | 생성 모델의 학습 데이터 관련 소송이 다수 진행 중. 결과에 따라 리스크 변동 |
| 상표·캐릭터 유사성 | AI가 기존 IP와 유사한 이미지를 뱉을 수 있음. 상업 배포 전 육안 검수 필수 |
| 서비스 약관 | 각 AI 서비스마다 상업적 이용 조건이 다름. 유료 플랜에서만 상업 이용 허용인 경우 존재 |
| 목소리·음악 | 실존 인물 목소리 모사는 퍼블리시티권 문제. 음악은 특히 민감 |
- 코드: AI 적극 활용. 공시 대상 아님.
- 최종 게임 에셋: CC0/구매 에셋 또는 직접 제작을 우선. AI는 컨셉·목업·플레이스홀더에 쓰고 최종본은 대체하는 전략이 안전하다.
- AI 에셋을 최종 사용한다면 Steamworks AI 공시 폼을 정확히 작성하고, 어떤 툴로 무엇을 만들었는지 자체 기록을 남겨라.
8. 다음 단계: 최소 기획서 템플릿
# [게임 제목] — 최소 기획서 v1
## 1. 한 줄 컨셉 (One-line pitch)
"[플레이어]는 [무엇]을 하여 [목표]를 달성한다."
예) "플레이어는 밀려오는 적을 자동 공격으로 정리하며 20분간 생존한다."
- [ ] 한 문장으로 안 써지면 아이디어가 아직 흐린 것이다.
## 2. 레퍼런스 3개
- 유사 게임 A: ____ (여기서 가져올 것: ____)
- 유사 게임 B: ____ (여기서 가져올 것: ____)
- 차별점 한 가지: ____ ← "딱 하나"만 다르게 한다
## 3. 코어 루프 (30초~3분 단위로 반복되는 것)
1. 플레이어가 ____ 한다
2. 그 결과 ____ 가 발생한다
3. 그래서 ____ 를 얻는다
4. 그것으로 ____ 가 강해진다 → 1번으로 복귀
- [ ] 이 루프가 재미없으면 나머지를 다 만들어도 재미없다.
## 4. 승/패 조건
- 승리: ____
- 패배: ____
- 세션 길이: ____ 분
- [ ] 명확한 끝이 없는 게임(샌드박스)은 첫 게임으로 부적합.
## 5. 조작
| 입력 | 행동 |
|---|---|
| 이동 | WASD / 왼쪽 스틱 |
| ____ | ____ |
- [ ] 버튼이 4개를 넘으면 첫 게임으로 복잡하다.
## 6. MVP 기능 목록 (이것만 되면 "게임"이다)
- [ ] 플레이어 이동/조작
- [ ] 적 1종 + 스폰
- [ ] 승리/패배 판정
- [ ] 타이틀 → 게임 → 결과 → 타이틀 순환
- [ ] 효과음 3종(행동/피격/획득)
- [ ] 세이브(최고 기록 + 설정)
- [ ] 일시정지 + 볼륨 설정 + 종료
- [ ] (추가 ____)
※ 전부 합쳐 10개를 넘기지 말 것
## 7. 제외 목록 (v1에서 절대 안 만드는 것) — 가장 중요한 항목
- [ ] 멀티플레이
- [ ] 스토리/컷신
- [ ] 캐릭터 선택
- [ ] 절차적 생성
- [ ] 업적/순위표 (출시 후 패치로)
- [ ] 다국어
- [ ] (추가 ____)
※ 만들다 떠오른 아이디어는 여기 또는 LATER.md 로 보낸다
## 8. 에셋 목록 (수량까지 적을 것)
| 종류 | 수량 | 조달 방법 | 상태 |
|---|---|---|---|
| 플레이어 스프라이트 | 1종 × 3애니(idle/run/hit) | 구매/제작 | ☐ |
| 적 스프라이트 | 3종 | | ☐ |
| 타일셋 | 1세트 | | ☐ |
| UI(버튼/폰트/아이콘) | 1세트 | | ☐ |
| 효과음 | 8종 | freesound(CC0) | ☐ |
| BGM | 2곡 | 구매 | ☐ |
| Steam 캡슐 4종 + 스크린샷 5장 + 트레일러 | | | ☐ |
※ 이 표의 항목 수가 곧 남은 노동량이다. 30줄을 넘으면 스코프를 줄여라.
## 9. 일정
- 프로토타입 완성일: ____ (착수 +2~3주)
- macOS 공증 테스트 통과일: ____ (착수 +1개월)
- 세로 슬라이스 완성일: ____
- Steam Direct 결제일: ____ (출시 -2개월 이상)
- 출시 목표일: ____
## 10. 성공 기준
- [ ] Steam에 출시했다 (← 첫 게임의 진짜 목표는 이것)
- [ ] 판매 ____ 장 / 리뷰 ____ 개
10. 출처 (전부 2026-08-05 조회)
본 문서의 수치·정책은 모두 2026-08-05 기준으로 조회되었으며, Godot 최신 stable 버전 4.7.1(2026-07-14)을 기준으로 한다. 실제 적용 전에는 반드시 공식 문서로 재확인하라.
Steam / Valve (공식)
- Steam Direct Fee — partner.steamgames.com/doc/gettingstarted/appfee (수수료 $100, 환불 불가·$1,000 매출 후 회수)
- Onboarding — partner.steamgames.com/doc/gettingstarted/onboarding (30일 대기)
- Releasing Your Product — partner.steamgames.com/doc/store/releasing (페이지 심사 3~5영업일, Coming Soon 최소 2주, 빌드 심사)
- Uploading to Steam (SteamPipe) — partner.steamgames.com/doc/sdk/uploading (steamcmd, VDF, 뎁팟, 브랜치, 1MB 청크)
- Store Graphical Assets — partner.steamgames.com/doc/store/assets/standard (캡슐 규격, 스크린샷 최소 5장)
- Platforms(macOS 요구사항) — partner.steamgames.com/doc/store/application/platforms ("Starting October 14th, 2019 Steam will require all new macOS Applications to be 64-bit and notarized by Apple", "App Bundles Are Notarized" 체크박스)
- Steam Deck Compatibility Review — partner.steamgames.com/doc/steamdeck/compat (Linux 빌드 우선 테스트, 없으면 Windows+Proton)
- Steam Direct 도입 공지 — steamcommunity.com/games/593110/announcements
- macOS 요구사항 공지 — steamcommunity.com/groups/steamworks/announcements
Steam AI 공시 정책 (2026-01-16 개정)
- generationamiga.com — Valve rewrites Steam's AI disclosure rules
- theoutpost.ai — Steam AI disclosure form clarified
- strayspark.studio — Steam AI disclosure rules 2026 indie developer guide
- ※ 2차 출처 기반. Steamworks 제출 폼의 실제 문구로 최종 확인 필요
Apple
- Apple Developer Program 가입 — developer.apple.com/help/account/membership/program-enrollment ($99/년)
- Notarizing macOS software before distribution — developer.apple.com/documentation/security/notarizing-macos-software-before-distribution
엔진
- Godot 릴리스 (최신 stable 4.7.1, 2026-07-14) — github.com/godotengine/godot-builds/releases
- Godot 4.5 릴리스 노트 — godotengine.org/releases/4.5
- Godot macOS 익스포트 문서 — godotengine/godot-docs — exporting_for_macos.rst (notarytool 사용, altool 폐기)
- Unity Runtime Fee 취소 발표 — unity.com/blog/unity-is-canceling-the-runtime-fee
- Unity 요금제 변경 안내 — unity.com/products/pricing-updates (Personal 무료 한도 $200k, Pro/Enterprise 2026-01-12부터 5% 인상)
- Unity Runtime Fee 취소 보도 — gamedeveloper.com — Unity is killing its controversial runtime fee
- Unreal Engine 라이선스 — unrealengine.com/license (평생 총매출 $1M 초과분 5% 로열티)
Steam 연동
- GodotSteam 문서 — godotsteam.com/tutorials/initializing (run_callbacks 필요)
- GodotSteam GDExtension (Godot Asset Store) — store.godotengine.org — godotsteam-gdextension (Steamworks SDK 1.65 기반)
Windows 코드 서명
- Microsoft: Code signing options for Windows app developers — learn.microsoft.com — code-signing-options
- OV vs EV 비교 — ssl.com/faqs — which-code-signing-certificate-do-i-need-ev-ov
- ※ "2024년 3월 이후 EV의 SmartScreen 즉시 통과 특권 소멸"은 2차 출처 기반 — 확인 필요
- Steamworks AI 공시 폼의 최신 정확한 문구 (파트너 로그인 필요)
- Azure Trusted Signing의 개인 개발자 자격 요건·지역 제한
- SteamPipe의 macOS
.app심볼릭 링크/실행 권한 처리 최신 정책 - Godot 게임에서의 Steam 오버레이 동작 안정성(렌더러별 차이)
- 미국 저작권청의 AI 생성물 관련 최신 가이던스 (법률 자문 아님)