에피소드 09 (MAKE) 착륙 지점 만들기
에피소드 09 (MAKE) — 착륙 지점 만들기
“얘들아… 이제 진짜 내려가야 하지 않을까?”
리온이 모니터를 보며 말했다. 신대륙 A-01의 지도는 이미 타일로 채워져 있었다. 바다, 땅, 숲, 그리고 살짝 높은 언덕까지. 폭풍을 뚫고 들어온 코러호는 아직도 하늘 위를 빙글빙글 돌고 있었다.
딕이 입을 삐죽 내밀었다. “맞아. 이제 좀 걷고 싶다. 계속 날기만 하니까 다리 저린 느낌이야.” 세이가 웃었다. “네가 걷는 것도 아닌데 왜 다리가 저려?” “기분이 그렇다고!”
리온이 진지한 표정으로 말했다. “그래서 오늘 미션은 한 줄이야. ‘코러호를 신대륙에 안전하게 내려앉게 하라.’”
딕의 눈이 반짝였다. “착륙!! 와, 그거 완전 멋있다. 우주선 착륙 같은 느낌!” 세이는 이미 태블릿을 켜면서 말했다. “좋아. 그럼 일단 질문부터 하자. ‘어디에, 어떻게, 내려앉을 건가?’”
1. MAKE — “여기가 우리 집이 될 자리야”
세이는 신대륙 A-01 지도를 불러와 특정 영역을 확대했다. 바람등대 언덕 근처의 평평한 땅. 바다와 숲이 모두 보이지만, 너무 험하지 않은 위치.
“일단 착륙 지점은 너무 극단적이지 않은 곳이 좋아.” 세이가 말했다. “바다 한가운데도 아니고, 숲 깊은 곳도 아니고… 바람을 보면서도, 숲도 갈 수 있는 중간 지점.”
딕이 고개를 끄덕였다. “맞아. 스타팅 마을 같은 느낌이네.” 리온이 웃으며 말했다. “그래, 오늘 우리는 ‘스타팅 착륙장’을 만드는 거야.”
세이는 지도 위에 네모를 그렸다.
// 착륙 지점 데이터
const landingZone = {
xTile: 7,
yTile: 5,
wTile: 3,
hTile: 2
};
“여기가 우리 첫 착륙 지점.” 세이가 말했다. “타일 기준으로 (7,5)부터 가로 3, 세로 2칸.”
딕이 물었다. “근데, 타일로만 보면 감이 안 오는데? 비행기 입장에서는 ‘픽셀’이잖아?”
리온이 설명했다. “그래서 이걸 캔버스 좌표로 바꿔 줄 거야.”
function tileToPixel(tileX, tileY) {
return {
x: tileX * TILE_SIZE,
y: tileY * TILE_SIZE
};
}
const landingZonePx = {
x: landingZone.xTile * TILE_SIZE,
y: landingZone.yTile * TILE_SIZE,
w: landingZone.wTile * TILE_SIZE,
h: landingZone.hTile * TILE_SIZE
};
세이는 화면에 빈 네모 상자를 그려보았다. 신대륙 지도 위에 얇은 라인으로 표시된 직사각형. “여기가 우리가 내려앉을 자리야.”
2. MAKE — ‘상태’를 나누지 않으면 꼬인다
딕이 손을 들었다. “근데 말이야. 지금 코러호는 그냥 ‘날고 있는 상태’잖아? 착륙하려면… 뭘 바꿔야 해?”
세이가 즉시 대답했다. “상태를 나눠야 해. FLYING, LANDING, LANDED… 최소 세 가지는 있어야, 나중에 머리가 안 터져.”
리온이 코드를 적기 시작했다.
const PLANE_STATE = {
FLYING: "flying",
LANDING: "landing",
LANDED: "landed"
};
let planeState = PLANE_STATE.FLYING;
딕이 물었다. “그럼 LANDING은 뭐가 다르게 돌아가는 거야?” 세이가 설명했다. “FLYING은 키보드로 완전 자유롭게 움직이지만, LANDING은 ‘목표 지점’을 향해서 자동으로 조용히 내려가는 상태야.”
리온이 설명을 이어갔다. “LANDED는 말 그대로 착륙 완료. 이때는 하늘이 아니라 지상 모드로 전환할 수 있고.”
딕이 이해했다는 듯 손뼉을 쳤다. “아~ 그러니까 게임에서 ‘컷신 느낌 착륙’ 넣을 수 있겠네. 자동으로 서서히 착륙하는 그 멋진 장면!”
3. MAKE — 착륙 조건 정하기
세이는 중요한 질문을 던졌다. “그럼, 언제 LANDING 상태로 들어갈까?” 리온이 대답했다. “빛의 틈을 지나고, 신대륙이 보이고, 플레이어가 ‘착륙’ 키를 누르면?”
딕은 바로 손을 들었다. “착륙 키는… 스페이스바!” 세이가 웃었다. “좋아. 그럼 이렇게 하자.”
// 스페이스바를 누르면 착륙 시도
document.addEventListener("keydown", (e) => {
if (e.code === "Space" && planeState === PLANE_STATE.FLYING) {
startLanding();
}
});
function startLanding() {
planeState = PLANE_STATE.LANDING;
// 목표 착륙 지점의 중앙 좌표 계산
const centerX = landingZonePx.x + landingZonePx.w / 2;
const centerY = landingZonePx.y + landingZonePx.h / 2;
landingTarget.x = centerX;
landingTarget.y = centerY;
}
딕이 물었다. “landingTarget는 뭐야?” 세이가 말했다. “비행기가 착륙할 목표 지점이야. LANDING 상태에서는 키가 아니라, 이 목표 지점을 향해서 자동으로 조금씩 내려가는 거지.”
리온이 업데이트 함수를 적었다.
const landingTarget = { x: 0, y: 0 };
function updateLanding() {
const dx = landingTarget.x - plane.x;
const dy = landingTarget.y - plane.y;
plane.x += dx * 0.05;
plane.y += dy * 0.05;
const distance = Math.sqrt(dx*dx + dy*dy);
if (distance < 2) {
planeState = PLANE_STATE.LANDED;
}
}
“이렇게 하면,” 세이가 정리했다. “스페이스를 누르면 비행기가 슬슬 멈추듯이 내려가서 착륙 지점 중앙에 서서히 안착할 거야.”
4. MAKE — 안전 반경과 실패 조건
딕이 갑자기 심각한 표정으로 말했다. “근데… 만약에 너무 이상한 위치에서 스페이스바를 눌렀다면?” “예를 들어?” 세이가 물었다. “신대륙이 훨씬 밑에 있는데, 하늘 꼭대기에서 미리 눌러버리는 거지.”
리온이 생각에 잠겼다. “좋은 지적이야. 그렇다면 착륙 가능 거리를 만들자.”
const MAX_LANDING_DISTANCE = 400; // 픽셀 기준
function canStartLanding() {
const centerX = landingZonePx.x + landingZonePx.w / 2;
const centerY = landingZonePx.y + landingZonePx.h / 2;
const dx = centerX - plane.x;
const dy = centerY - plane.y;
const distance = Math.sqrt(dx*dx + dy*dy);
return distance <= MAX_LANDING_DISTANCE;
}
세이는 startLanding 함수를 살짝 고쳤다.
function startLanding() {
if (!canStartLanding()) {
// 너무 멀면 안내 메시지
showMessage("착륙하기엔 너무 멀어요!");
return;
}
planeState = PLANE_STATE.LANDING;
const centerX = landingZonePx.x + landingZonePx.w / 2;
const centerY = landingZonePx.y + landingZonePx.h / 2;
landingTarget.x = centerX;
landingTarget.y = centerY;
}
딕이 만족스럽게 말했다. “좋아. 이제 억울한 착륙은 없겠다.” 세이는 고개를 끄덕였다. “맞아. 게임에서 가장 기분 나쁜 건, ‘그래도 해볼 만 했는데’가 아니라 ‘아예 기회가 없었다’라고 느끼는 순간이거든.”
5. PLAY → DREAM — 첫 착륙의 느낌
모든 코드가 준비되자, 세 아이는 한숨을 크게 쉬고 동시에 말했다.
“테스트!”
코러호는 신대륙 상공을 천천히 돌고 있었다. 바람등대 언덕 위, 평평한 땅 근처. 딕이 키보드를 잡고 조심스럽게 이동했다.
“좋아… 이제 스페이스!” 딕이 키를 눌렀다.
코러호는 갑자기 거친 움직임을 멈추고, 목표 지점 쪽으로 천천히, 아주 부드럽게 이동하기 시작했다. 상공에서 보이던 섬의 타일들이 점점 가까워졌다.
리온이 숨을 죽였다. 세이도 말없이 화면만 쳐다봤다. 코러호는 마치 누가 손으로 살며시 내려놓는 것처럼 착륙 지점 중앙에 안착했다.
“착륙 완료 — 신대륙 A-01 바람등대 언덕.” 작은 텍스트가 화면 한쪽에 나타났다.
딕이 자리에서 벌떡 일어나 외쳤다. “우와아아아!! 진짜 착륙했다!!!” 세이는 살짝 웃으며 눈을 깜박였다. “이제… 진짜 내려왔다.”
리온은 화면 속 코러호를 바라보며 조용히 말했다. “처음에는 그냥 캔버스 위 네모였는데… 이제는 진짜 우리 비행기 같아.”
6. Q&A / F&A
| Q | A |
|---|---|
| Q. 왜 상태(FLYING/LANDING/LANDED)를 나눠야 하나요? | A. 상태를 나누지 않으면, 키 입력, 자동 이동, 지상 모드가 서로 꼬여서 버그가 많이 생겨요. 상태를 분리하면 코드가 훨씬 이해하기 쉬워져요. |
| Q. 착륙 지점은 꼭 타일 기준으로 만들어야 하나요? | A. 아니요. 픽셀 기준으로 만들어도 되지만, 타일맵과 같이 쓸 때는 타일 단위로 관리하는 것이 훨씬 편리합니다. |
| Q. MAX_LANDING_DISTANCE는 어떻게 정하나요? | A. 플레이어가 “이 정도면 도전할 만하다”라고 느끼는 거리로 직접 플레이해 보며 수치를 조정하는 것이 가장 좋아요. |
| Q. 자동 착륙 대신 직접 조종 착륙도 가능할까요? | A. 물론이에요! 다만 난이도가 매우 올라가기 때문에, 먼저 자동 착륙을 구현해 둔 다음 옵션으로 제공하는 방식이 좋은 설계입니다. |
| F&A. 착륙 연출을 더 멋지게 만들려면? | A. 속도를 점점 줄이거나, 바람 소리·먼지 효과·카메라 줌 연출 등을 얹으면 훨씬 영화 같은 장면을 만들 수 있어요. |
7. 오늘의 정리
오늘 에피소드 09 (MAKE)에서는 신대륙 A-01 위에 첫 착륙 지점을 설계했다.
- 타일 기반으로 착륙 구역을 정하고
- 비행기의 상태를 FLYING / LANDING / LANDED로 나누고
- 스페이스바로 착륙을 시도하고
- 너무 멀면 거절하고, 가까우면 자동으로 내려가게 만들고
- 안전하게 중앙에 안착하면, 지상 모드로 바뀌도록 만들었다.
코러호는 이제 더 이상 “영원히 하늘만 나는 비행기”가 아니다. 세계 위로 내려와, 땅을 밟고, 앞으로 만들어질 마을과 사람들, 사건을 만날 준비를 끝냈다.
세 아이는 서로를 바라보며 웃었다. “우리가 만든 이 착륙장이, 언젠가 누군가의 첫 기억이 되겠지?” 그 말에 아무도 대답하지 않았지만, 모두 같은 생각을 하고 있었다.
다음 편은 에피소드 10 (PLAY) — 첫 발을 내딛는 날 지상에서의 첫 걸음이 시작된다.
8. English Summary (about 500 chars)
In Episode 09 (MAKE), Leon, Sey, and Dic design the first landing zone on the new continent A-01. They define a tile-based landing area, split the plane state into FLYING, LANDING, and LANDED, and add a safe landing distance so the player can’t trigger landing from too far away. With a soft auto-landing motion, Corer One finally touches the ground. The sky-only game becomes a world where real exploration on land can begin.
9. TAG
#에피소드09 #MAKE단계 #착륙지점 #신대륙A01 #DIY게임만들기 #코러호 #리온세이딕 #Canvas게임 #자바스크립트게임 #엔딕프로젝트

Leave a Comment