HTML 파일 하나로 3D 항공기 게임 만들기, fable5 세 번의 릴레이
Three.js를 실은 HTML 한 장에 절차적 지형과 뱅크 선회 물리, 실속 경고, 링 통과 미션까지 담았다. 세 AI가 앞사람의 코드를 이어받아 층층이 쌓아 올린 브라우저 비행 시뮬레이터의 완성 과정.
AI와 사회 · 2026-07-30
브라우저에서 그대로 열리는 HTML 파일 하나에 3D 항공기 조종 게임을 담는 작업이다. 별도의 빌드 도구나 설치 없이 Three.js를 CDN으로 불러오고, 저장한 .html을 더블클릭하면 곧바로 이륙한다.
세 번의 발화가 층을 이룬다. 첫 발화가 비행과 환경의 뼈대를 세우고, 둘째가 물리와 그래픽 디테일을 얹고, 셋째가 게임성과 소리를 붙여 마무리한다. 앞사람의 코드를 버리지 않고 그대로 이어받아 쌓는 방식이라, 완성본은 세 층이 포개진 결과물이다.
비행이 실기처럼 움직이는 이유
조종의 바탕은 쿼터니언 기반 6자유도다. 피치와 롤과 요를 각각 로컬축으로 돌리고, 스로틀과 기수 각도에 따라 속도가 오르내린다.
실기 같은 손맛은 여기에 몇 겹을 더해 나온다. 기체를 롤로 기울이면 양력의 수평 성분이 생겨 저절로 선회가 걸리고, 조종면의 효과는 속도에 비례해 저속에서 둔해진다. 속도가 65노트 아래로 떨어지면 실속에 들어가 기수가 처지고 고도를 잃으며 경고등이 깜빡인다. 급격한 입력을 관성으로 눅여 키를 눌렀을 때의 반응을 부드럽게 만든 것도 이 대목이다.
실사 지향의 화면을 만드는 방법
환경은 절차적으로 짓는다. 사인과 코사인을 겹친 노이즈로 지형의 높낮이를 만들고, 고도에 따라 해변의 모래에서 초원, 바위, 설산으로 색을 나눈다. 하늘은 그라데이션 돔에 태양 방향으로 번지는 광채를 셰이더로 넣고, 바다는 버텍스 셰이더로 물결을 흔든다.
그 위에 빌보드 스프라이트로 뭉게구름 예순 덩이를 띄우고, 프로펠러 회전과 블러 디스크, 날개 끝에서 흘러나오는 항적운 파티클을 더한다. ACES 필름 톤매핑과 부드러운 그림자가 이 요소들을 한 화면에 눌러 담아 실사에 가까운 인상을 만든다.
영상이 아니라 게임이 되는 지점
마지막 층은 이것을 놀 수 있는 것으로 바꾼다. 공중에 링 열 개가 이어지고, 다음 링까지의 거리가 화면에 뜨며 통과할 때마다 효과음이 울린다. 지상에는 주변 지형을 평탄하게 고른 활주로가 놓여, 자세가 완만하면 착지가 허용되고 급한 각도나 산악에 부딪히면 파티클이 터지며 폭발음과 함께 3초 뒤 다시 시작된다.
소리는 전부 WebAudio로 합성한다. 톱니파와 구형파를 로우패스로 걸러 만든 엔진음이 스로틀과 속도에 맞춰 음정을 바꾸고, 링 통과음과 노이즈 폭발음이 따로 붙는다. 시작 화면에서 이륙을 누르면 5초간 기체 주위를 도는 궤도 카메라가 한 바퀴 돌고 나서 조종권이 넘어온다.
fable5로 코딩을 해보자. 작성 기회는 3번임. 이전 ai가 작성한 것을 토대로 더 개선하고 쌓아나가면 됨. 첫번째 발화에서는 초안을 쓰고, 두번째 발화에서 한번 더 보태고 마지막 발화에서 최종코드를 제출해. 코드 작성 주제는, HTML로 실행가능한 3d 실사 스타일 항공기 조종 게임 영상 만들기임.
Frontend Graphics Architect · claude-fable-5
좋아, 1차 초안 갑니다. Three.js CDN을 사용해 브라우저에서 바로 실행되는 3D 항공기 조종 게임의 뼈대를 잡을게요. 이번 초안의 목표는 핵심 비행 물리 + 실사 지향 환경(하늘, 태양, 안개, 지형, 바다)이고, 다음 턴에서 그래픽 디테일(구름, 기체 모델링 강화, HUD)을 쌓아올리면 됩니다.
html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>Flight Sim - Draft 1</title>
<style>
body { margin:0; overflow:hidden; background:#000; font-family:sans-serif; }
#hud { position:fixed; top:12px; left:12px; color:#cfe8ff; font-size:14px;
text-shadow:0 0 4px #000; user-select:none; }
#help { position:fixed; bottom:12px; left:12px; color:#9fb8cc; font-size:12px; }
</style>
</head>
<body>
<div id="hud"></div>
<div id="help">W/S: 피치 | A/D: 롤 | Q/E: 요 | Shift/Ctrl: 스로틀 | C: 카메라</div>
<script type="importmap">
{ "imports": { "three": "https://unpkg.com/[email protected]/build/three.module.js" } }
</script>
<script type="module">
import * as THREE from 'three';
// ---------- 기본 셋업 ----------
const renderer = new THREE.WebGLRenderer({ antialias:true });
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 0.9;
docu…
Game Logic Engineer · claude-fable-5
좋습니다, 2차로 쌓아올릴게요. 1차 뼈대를 유지하면서 비행 물리 강화(뱅크 선회, 실속, 조타 관성) + 그래픽 디테일(구름, 프로펠러, 항적운, 물결, 인공수평의 HUD)를 추가했습니다.
html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>Flight Sim - Draft 2</title>
<style>
body { margin:0; overflow:hidden; background:#000; font-family:sans-serif; }
#hud { position:fixed; top:12px; left:12px; color:#cfe8ff; font-size:14px;
text-shadow:0 0 4px #000; user-select:none; line-height:1.6; }
#help { position:fixed; bottom:12px; left:12px; color:#9fb8cc; font-size:12px; }
#horizon { position:fixed; top:16px; right:16px; width:120px; height:120px;
border-radius:50%; border:2px solid rgba(200,225,255,.5); overflow:hidden; }
#horizonInner { position:absolute; left:-40px; top:-100px; width:200px; height:320px; }
#horizonMark { position:absolute; left:30px; top:58px; width:60px; height:4px; }
#stall { position:fixed; top:150px; right:16px; width:124px; text-align:center;
color:#ff5544; font-weight:bold; font-size:13px; text-shadow:0 0 6px #400;
display:none; animation:blink .5s infinite; }
@keyframes blink { 50% { opacity:.2; } }
</style>
</head>
<body>
<div id="hud"></div>
<div id=…
Gamma · claude-fable-5
좋습니다, 마지막 3차 턴입니다. 2차 코드를 기반으로 게임성(링 통과 미션, 활주로, 충돌/폭발), WebAudio 엔진음, 시네마틱 오프닝을 얹어 완성본을 제출합니다.
html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>Sky Ace - Final</title>
<style>
body { margin:0; overflow:hidden; background:#000; font-family:sans-serif; }
#hud { position:fixed; top:12px; left:12px; color:#cfe8ff; font-size:14px;
text-shadow:0 0 4px #000; user-select:none; line-height:1.6; }
#help { position:fixed; bottom:12px; left:12px; color:#9fb8cc; font-size:12px; }
#horizon { position:fixed; top:16px; right:16px; width:120px; height:120px;
border-radius:50%; border:2px solid rgba(200,225,255,.5); overflow:hidden; }
#horizonInner { position:absolute; left:-40px; top:-100px; }
#horizonMark { position:absolute; left:30px; top:58px; width:60px; height:4px; }
#stall { position:fixed; top:150px; right:16px; width:124px; text-align:center;
color:#ff5544; font-weight:bold; font-size:13px; text-shadow:0 0 6px #400;
display:none; animation:blink .5s infinite; }
@keyframes blink { 50% { opacity:.2; } }
#mission { position:fixed; top:12px; left:50%; transform:translateX(-50%);
color:…