Skip to content

markdavidjamesbonner/flight-sim

Repository files navigation

Sky Dodger

A browser-based 3D flight simulator built with Three.js and TypeScript. Fly through procedurally generated terrain, dodge obstacles, and shoot down enemies for as long as you can survive.

Tech Stack

Layer Technology Role
Language TypeScript 5.9 Type-safe source
3D Engine Three.js 0.183 Rendering, geometry, materials, lighting
Build Vite 6.4 Dev server, HMR, production bundler
Audio Web Audio API Procedural sound effects (no asset files)
Storage localStorage High score persistence
Physics Custom sphere collision Lightweight hit detection without a physics engine

No frameworks. No external assets. Everything — terrain, models, sounds — is generated procedurally at runtime.

Architecture

┌─────────────────────────────────────────────────────────┐
│                     main.ts (Game)                      │
│          State machine: menu → playing → gameover        │
│          requestAnimationFrame loop, dt-capped           │
├─────────────┬───────────────┬───────────────────────────┤
│  entities/  │    scene/     │         systems/           │
│             │               │                            │
│  plane.ts   │  setup.ts     │  input.ts      audio.ts   │
│  enemy.ts   │  terrain.ts   │  collision.ts  scoring.ts │
│  projectile │  obstacles.ts │  difficulty.ts             │
│  camera.ts  │  clouds.ts    │  enemyManager.ts           │
│             │               │  projectileManager.ts      │
├─────────────┴───────────────┴───────────────────────────┤
│          ui/                    utils/                    │
│          hud.ts                 math.ts                   │
│          menu.ts                (lerp, clamp, fbm, noise)│
└─────────────────────────────────────────────────────────┘

How the pieces connect

Game Loop (main.ts)
  │
  ├─ InputManager ─────────── reads mouse (pointer lock) + keyboard
  │
  ├─ PlaneState ───────────── applies input → speed, pitch, roll, position
  │     └─ plane model ─────── Three.js group synced to PlaneState each frame
  │
  ├─ TerrainManager ────────── FBM noise → chunked ground mesh, recycled behind player
  ├─ ObstacleManager ──────── rocks, canyon walls, birds spawned per chunk
  ├─ CloudManager ─────────── decorative pink clouds
  │
  ├─ EnemyManager ─────────── spawns planes + blimps on timers, AI steering
  ├─ ProjectileManager ────── player & enemy bullets, lifetime + collision
  │
  ├─ checkCollisions ──────── sphere-sphere: obstacles vs player
  ├─ checkCombatCollisions ── projectiles vs enemies, projectiles vs player
  │
  ├─ ScoringSystem ────────── distance × speed + near-miss + kill bonuses
  ├─ DifficultySystem ─────── level = √(distance / 150), capped at 8
  │
  ├─ ChaseCamera ──────────── follows plane, screen shake on damage
  ├─ HUD ──────────────────── DOM overlay: speed, health, score, boost
  └─ renderer.render() ────── Three.js draws the frame

Project Structure

flight-sim/
├── index.html                 Entry point — inline styles, UI skeleton
├── package.json
├── tsconfig.json
├── vite.config.ts
└── src/
    ├── main.ts                Game class, loop, state transitions
    ├── entities/
    │   ├── plane.ts           Player model, flight physics, boost, damage
    │   ├── enemy.ts           Enemy planes (2 HP) and blimps (5 HP)
    │   ├── projectile.ts      Bullet geometry and lifetime
    │   └── camera.ts          Chase camera with shake
    ├── scene/
    │   ├── setup.ts           Scene, renderer, lights, fog
    │   ├── terrain.ts         FBM-noise chunked terrain, recycled behind player
    │   ├── obstacles.ts       Rock pillars, floating rocks, canyon walls, birds
    │   └── clouds.ts          Procedural pink clouds
    ├── systems/
    │   ├── input.ts           Mouse (pointer lock) + keyboard, smooth lerp
    │   ├── collision.ts       Obstacle and combat hit detection
    │   ├── scoring.ts         Points, multipliers, localStorage high scores
    │   ├── difficulty.ts      Level scaling by distance traveled
    │   ├── enemyManager.ts    Spawn timers, AI update, removal
    │   ├── projectileManager.ts  Bullet updates and cleanup
    │   └── audio.ts           Web Audio oscillators and noise generators
    ├── ui/
    │   ├── hud.ts             In-game speed, health, score, boost meter
    │   └── menu.ts            Title screen, game over, high score table
    └── utils/
        └── math.ts            lerp, clamp, randRange, 2D noise, FBM

Controls

Input Action
Mouse Steer (pointer lock)
Arrow keys Steer (smoothly interpolated alternative)
W / S Throttle up / down
A / D Roll left / right
Space Boost (2s duration, 5s cooldown)
F / Enter / Left click Fire

Game Mechanics

Flight — Speed ranges from 30 to 120 km/h (180 during boost). The plane is bounded laterally (±32 units) and vertically (2–35 units). Scraping the ground drains 50 HP/s.

Obstacles — Rock pillars (35 dmg), floating rocks (25 dmg), canyon walls (40 dmg), and birds (15 dmg) populate the terrain. A near miss within ~5 units awards +50 points; actual collision radius is ~2 units.

Enemies — Fighter planes (2 HP, shoot at you) spawn every 4–18 seconds. Blimps (5 HP, passive) spawn every 25–45 seconds. Kill rewards scale with difficulty level.

Scoring — Base score accrues from distance × speed multiplier. Near misses and kills add bonuses. Top 10 high scores persist in localStorage under key sky-dodger-highscores.

Difficulty — Level = floor(sqrt(distance / 150)), capped at 8. Higher levels increase obstacle density, enemy spawn rates, and kill bonuses.

Getting Started

npm install
npm run dev        # Vite dev server, auto-opens browser

Production build

npm run build      # TypeScript check + Vite production build → dist/
npm run preview    # Serve the production build locally

Design Decisions

  • No physics engine — Collision uses sphere-distance checks. The game doesn't need rigid bodies or contact resolution, so a full physics library would be dead weight.
  • No asset files — All geometry is built from Three.js primitives, terrain from FBM noise, audio from Web Audio oscillators. Zero network requests for assets means instant load.
  • Chunked terrain recycling — Ground meshes are generated in 100-unit chunks and repositioned behind the player once passed, keeping memory constant.
  • Input smoothing — Keyboard steering lerps toward targets rather than snapping, preventing the jerky feel of binary digital input.

About

Browser-based 3D endless flight game built with Three.js and TypeScript. Procedurally generated terrain, obstacles, and enemies — no static assets. Dodge, shoot, and chase high scores entirely in the browser.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors