Quick Start - Build Your First Game in 30 Minutes
Complete tutorial from installation to Multi-Scale game with Vue UI
📦 Step 1: Installation (2 minutes)
Prerequisites
- Node.js 18+ and pnpm 8+
- Code editor (VS Code recommended)
- Modern browser
Clone and Install
# Clone repository
git clone <your-repo-url> my-game
cd my-game
# Install dependencies
ppnpm install
# Verify installation
pnpm devOpen http://localhost:5173 - you should see LINX game demo.
Press TAB to cycle modes
Resize window to see auto-adaptation
✅ If game loads - installation successful!
🎮 Step 2: Create Your First Scene (5 minutes)
Create Scene File
Create src/game/scenes/MyFirstScene.ts:
import Phaser from "phaser";
import { UIManager } from "@core/ui/UIManager";
import { GameModeManager } from "@core/systems/GameModeManager";
import { ViewportManager } from "@core/utils/ViewportManager";
export class MyFirstScene extends Phaser.Scene {
private uiManager!: UIManager;
private gameModeManager!: GameModeManager;
private viewportManager!: ViewportManager;
constructor() {
super({ key: "MyFirstScene" });
}
create(): void {
// Initialize viewport
this.viewportManager = new ViewportManager(this);
this.viewportManager.init();
// Initialize game modes
this.gameModeManager = new GameModeManager(this, this.viewportManager);
this.gameModeManager.init();
// Initialize UI
this.uiManager = new UIManager(this, {
enableLogging: true,
});
// Add visual feedback
this.add
.text(400, 300, "My First CASCADA Game!", {
fontSize: "32px",
color: "#00ffff",
})
.setOrigin(0.5);
// Log current mode
this.gameModeManager.onModeChange((event) => {
console.log(`Mode: ${event.newMode}`);
});
}
}Register Scene
Edit src/game/config/game.config.ts:
import { MyFirstScene } from "@game/scenes/MyFirstScene";
// Find scene array and add:
scene: [
BootScene,
PreloaderScene,
MyFirstScene, // ← Add this
// ... other scenes
];Launch Scene
Edit src/game/scenes/PreloaderScene.ts:
create() {
// Change this line:
this.scene.start("MyFirstScene"); // Instead of "MainMenuScene"
}Test: pnpm dev → you should see "My First CASCADA Game!"
Press TAB - mode info appears in console!
🎨 Step 3: Add Vue UI (8 minutes)
Create Vue Component
Create src/game/ui/MyGameHUD.vue:
<script setup lang="ts">
import { defineProps, defineEmits, computed } from "vue";
const props = defineProps({
score: { type: Number, default: 0 },
mode: { type: String, default: "NANO" },
});
const emit = defineEmits(["reset-game"]);
const scoreFormatted = computed(() => {
return props.score.toString().padStart(6, "0");
});
</script>
<template>
<div class="game-hud">
<div class="hud-top">
<div class="mode-display">MODE: {{ mode }}</div>
<div class="score">SCORE: {{ scoreFormatted }}</div>
</div>
<div class="hud-bottom">
<button @click="emit('reset-game')" class="btn-reset">Reset</button>
</div>
</div>
</template>
<style scoped>
.game-hud {
position: fixed;
inset: 0;
pointer-events: none;
font-family: monospace;
color: #00ffff;
}
.hud-top {
display: flex;
justify-content: space-between;
padding: 20px;
pointer-events: none;
}
.mode-display,
.score {
background: rgba(0, 0, 0, 0.8);
padding: 10px 20px;
border: 1px solid #00ffff;
font-size: 16px;
}
.hud-bottom {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
}
.btn-reset {
padding: 10px 30px;
background: rgba(0, 255, 255, 0.1);
border: 1px solid #00ffff;
color: #00ffff;
cursor: pointer;
pointer-events: auto;
transition: all 0.3s;
}
.btn-reset:hover {
background: rgba(0, 255, 255, 0.3);
}
</style>Integrate in Scene
Update src/game/scenes/MyFirstScene.ts:
import { VueUIBridge } from "@core/ui/VueUIBridge";
import MyGameHUD from "@game/ui/MyGameHUD.vue";
export class MyFirstScene extends Phaser.Scene {
private vueBridge!: VueUIBridge;
private score: number = 0;
create(): void {
// ... existing code ...
// Create Vue bridge
this.vueBridge = new VueUIBridge(this.uiManager);
// Mount HUD
this.vueBridge.mount("hud", MyGameHUD, {
props: {
score: this.score,
mode: this.gameModeManager.getConfig().name,
},
events: {
"reset-game": () => this.resetGame(),
},
});
this.vueBridge.show("hud");
// Update on mode change
this.gameModeManager.onModeChange((event) => {
const config = this.gameModeManager.getConfig();
this.vueBridge.updateProps("hud", {
mode: config.name,
});
});
}
update(): void {
// Update score every frame for demo
this.score += 1;
this.vueBridge.updateProps("hud", { score: this.score });
}
resetGame(): void {
this.score = 0;
console.log("Game reset!");
}
shutdown(): void {
this.vueBridge.unmountAll();
this.uiManager.shutdown();
}
}Test: pnpm dev
- HUD with mode and score visible ✅
- Press TAB - mode changes in HUD ✅
- Click Reset - score resets ✅
You just created reactive Vue UI in Phaser!
🎯 Step 4: Add Multi-Scale Gameplay (10 minutes)
Add Player Object
Update MyFirstScene.ts:
export class MyFirstScene extends Phaser.Scene {
private player!: Phaser.GameObjects.Arc;
private moveSpeed: number = 5;
create(): void {
// ... existing code ...
// Create player
this.player = this.add.circle(400, 300, 20, 0x00ffff);
// Keyboard controls
this.cursors = this.input.keyboard.createCursorKeys();
// React to mode changes
this.gameModeManager.onModeChange(() => {
this.adaptToMode();
});
// Initial adaptation
this.adaptToMode();
}
update(): void {
// Update score
this.score += 1;
this.vueBridge.updateProps("hud", { score: this.score });
// Move player
if (this.cursors.left.isDown) {
this.player.x -= this.moveSpeed;
}
if (this.cursors.right.isDown) {
this.player.x += this.moveSpeed;
}
if (this.cursors.up.isDown) {
this.player.y -= this.moveSpeed;
}
if (this.cursors.down.isDown) {
this.player.y += this.moveSpeed;
}
}
adaptToMode(): void {
const mode = this.gameModeManager.getCurrentMode();
const config = this.gameModeManager.getConfig();
// Adapt player speed based on mode
if (this.gameModeManager.can("detailedInteraction")) {
// NANO/MICRO - detailed control, slow movement
this.moveSpeed = 5;
this.player.setScale(1);
} else {
// MESO/MACRO/MEGA - fast movement, smaller player
this.moveSpeed = 15;
this.player.setScale(0.5);
}
console.log(`Adapted to ${config.name}:`, {
speed: this.moveSpeed,
zoom: config.cameraZoom,
timeScale: config.timeScale,
});
}
}Test: pnpm dev
- Move player with arrow keys ✅
- Press TAB - player speed CHANGES based on mode ✅
- NANO mode (zoom 2.5x): slow, precise movement
- MEGA mode (zoom 0.3x): fast, strategic movement
You just added adaptive gameplay!
🌊 Step 5: Add Enemies (5 minutes)
Spawn Enemies
export class MyFirstScene extends Phaser.Scene {
private enemies: Phaser.GameObjects.Arc[] = [];
create(): void {
// ... existing code ...
// Spawn enemies
this.spawnEnemies();
// Respawn timer
this.time.addEvent({
delay: 3000,
callback: () => this.spawnEnemies(),
loop: true,
});
}
spawnEnemies(): void {
const maxEnemies = this.gameModeManager.can("detailedInteraction")
? 5 // NANO/MICRO - few enemies, detailed combat
: 20; // MESO/MACRO/MEGA - many enemies, strategic
// Spawn up to max
while (this.enemies.length < maxEnemies) {
const enemy = this.add.circle(
Phaser.Math.Between(100, 700),
Phaser.Math.Between(100, 500),
15,
0xff0000
);
this.enemies.push(enemy);
}
}
update(): void {
// ... existing movement code ...
// Check collisions
this.enemies.forEach((enemy, index) => {
const distance = Phaser.Math.Distance.Between(
this.player.x,
this.player.y,
enemy.x,
enemy.y
);
if (distance < 35) {
// Collision!
enemy.destroy();
this.enemies.splice(index, 1);
this.score += 100;
}
});
// Update UI
this.vueBridge.updateProps("hud", { score: this.score });
}
adaptToMode(): void {
// ... existing code ...
// Clear old enemies, spawn new count
this.enemies.forEach((e) => e.destroy());
this.enemies = [];
this.spawnEnemies();
}
}Test: pnpm dev
- Red enemies spawn ✅
- Touch enemy - destroyed, score +100 ✅
- Press TAB:
- NANO mode → 5 enemies (tactical combat)
- MEGA mode → 20 enemies (strategic overview)
Gameplay now adapts to scale!
🎨 Step 6: Add Theme Switching (5 minutes)
Update HUD Component
Edit src/game/ui/MyGameHUD.vue:
<script setup lang="ts">
const props = defineProps({
score: { type: Number, default: 0 },
mode: { type: String, default: "NANO" },
theme: { type: String, default: "minimal" }, // ← Add theme
});
const emit = defineEmits(["reset-game", "change-theme"]); // ← Add event
</script>
<template>
<div class="game-hud">
<div class="hud-top">
<div class="mode-display">MODE: {{ mode }}</div>
<div class="score">SCORE: {{ scoreFormatted }}</div>
</div>
<!-- Theme selector -->
<div class="hud-theme">
<select
@change="emit('change-theme', $event.target.value)"
:value="theme"
class="theme-select"
>
<option value="minimal">Minimal</option>
<option value="neon-grid">Neon Grid</option>
<option value="brutalist">Brutalist</option>
<option value="liquid-crystal">Liquid Crystal</option>
</select>
</div>
<div class="hud-bottom">
<button @click="emit('reset-game')" class="btn-reset">Reset</button>
</div>
</div>
</template>
<style scoped>
/* ... existing styles ... */
.hud-theme {
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
}
.theme-select {
background: rgba(0, 0, 0, 0.8);
border: 1px solid #00ffff;
color: #00ffff;
padding: 8px 16px;
font-family: monospace;
pointer-events: auto;
cursor: pointer;
}
</style>Handle Theme Changes
Update MyFirstScene.ts:
import { ThemeManager } from "@core/config/theme-manager";
export class MyFirstScene extends Phaser.Scene {
private themeManager!: ThemeManager;
private currentTheme: string = "minimal";
create(): void {
// ... existing code ...
// Initialize theme manager
this.themeManager = new ThemeManager();
this.themeManager.setTheme("minimal");
// Mount HUD with theme prop
this.vueBridge.mount("hud", MyGameHUD, {
props: {
score: this.score,
mode: this.gameModeManager.getConfig().name,
theme: this.currentTheme, // ← Add theme
},
events: {
"reset-game": () => this.resetGame(),
"change-theme": (theme: string) => this.changeTheme(theme), // ← Add handler
},
});
}
changeTheme(themeId: string): void {
this.themeManager.setTheme(themeId);
this.currentTheme = themeId;
console.log(`Theme changed to: ${themeId}`);
// Update player color based on theme
const theme = this.themeManager.getCurrentTheme();
this.player.setFillStyle(parseInt(theme.primary.replace("#", ""), 16));
}
}Test: pnpm dev
- Theme selector visible at top ✅
- Change theme - colors update instantly ✅
- Try "Neon Grid" - cyberpunk aesthetic!
- Try "Liquid Crystal" - rainbow shimmer!
🚀 Step 7: Add Scoring System (5 minutes)
Create Score Manager
Create src/game/core/ScoreManager.ts:
export class ScoreManager {
private score: number = 0;
private highScore: number = 0;
private multiplier: number = 1;
constructor() {
// Load high score from localStorage
const saved = localStorage.getItem("highScore");
this.highScore = saved ? parseInt(saved) : 0;
}
addScore(points: number): void {
this.score += points * this.multiplier;
if (this.score > this.highScore) {
this.highScore = this.score;
localStorage.setItem("highScore", this.highScore.toString());
}
}
setMultiplier(value: number): void {
this.multiplier = value;
}
getScore(): number {
return this.score;
}
getHighScore(): number {
return this.highScore;
}
reset(): void {
this.score = 0;
this.multiplier = 1;
}
}Use in Scene
import { ScoreManager } from "@game/core/ScoreManager";
export class MyFirstScene extends Phaser.Scene {
private scoreManager!: ScoreManager;
create(): void {
// ... existing code ...
this.scoreManager = new ScoreManager();
// Update HUD with high score
this.vueBridge.mount("hud", MyGameHUD, {
props: {
score: this.scoreManager.getScore(),
highScore: this.scoreManager.getHighScore(), // ← Add
mode: this.gameModeManager.getConfig().name,
theme: this.currentTheme,
},
// ... events ...
});
}
update(): void {
// ... existing code ...
// Enemy collision
this.enemies.forEach((enemy, index) => {
const distance = Phaser.Math.Distance.Between(
this.player.x,
this.player.y,
enemy.x,
enemy.y
);
if (distance < 35) {
enemy.destroy();
this.enemies.splice(index, 1);
// Use score manager
this.scoreManager.addScore(100);
// Update UI
this.vueBridge.updateProps("hud", {
score: this.scoreManager.getScore(),
});
}
});
}
resetGame(): void {
this.scoreManager.reset();
this.vueBridge.updateProps("hud", {
score: 0,
});
}
}Test: High score persists across reloads!
🎮 Step 8: Test Multi-Scale Adaptation (5 minutes)
Add Keyboard Controls
Update MyFirstScene.ts:
create(): void {
// ... existing code ...
// Keyboard shortcuts
this.input.keyboard?.on("keydown-TAB", () => {
this.gameModeManager.cycleMode();
});
this.input.keyboard?.on("keydown-ONE", () => {
this.gameModeManager.setMode(GameModeType.MODE_1, "manual");
});
this.input.keyboard?.on("keydown-TWO", () => {
this.gameModeManager.setMode(GameModeType.MODE_2, "manual");
});
this.input.keyboard?.on("keydown-THREE", () => {
this.gameModeManager.setMode(GameModeType.MODE_3, "manual");
});
this.input.keyboard?.on("keydown-FOUR", () => {
this.gameModeManager.setMode(GameModeType.MODE_4, "manual");
});
this.input.keyboard?.on("keydown-FIVE", () => {
this.gameModeManager.setMode(GameModeType.MODE_5, "manual");
});
this.input.keyboard?.on("keydown-A", () => {
const currentAuto = this.gameModeManager["autoModeEnabled"];
this.gameModeManager.setAutoMode(!currentAuto);
});
}Test All Modes
Run: pnpm dev
Try each mode:
Press 1 (NANO):
├─ Zoom: 2.5x (very close)
├─ Speed: Slow, precise
├─ Enemies: 5 (tactical combat)
└─ Time: 1.0x (normal)
Press 3 (MESO):
├─ Zoom: 0.8x (medium)
├─ Speed: Fast movement
├─ Enemies: 20 (strategic)
└─ Time: 2.0x (faster)
Press 5 (MEGA):
├─ Zoom: 0.3x (very far)
├─ Speed: Very fast
├─ Enemies: 20 (overview)
└─ Time: 10.0x (very fast)Resize browser window:
- Small (< 768px) → NANO mode
- Medium (1024px) → MESO mode
- Large (1920px) → MEGA mode
Your game now adapts to device!
✨ Final Result (30 minutes total)
What You Built
✅ Multi-Scale Game:
- 5 gameplay modes with different mechanics
- Automatic device detection
- Manual mode switching
✅ Vue UI Integration:
- Reactive HUD with score and mode
- Theme selector with hot-swapping
- Reset button with event handling
✅ Adaptive Mechanics:
- Player speed changes per mode
- Enemy count adapts to scale
- Time scale affects gameplay
✅ Production-Ready Systems:
- UIManager for UI layer
- VueUIBridge for Vue integration
- GameModeManager for scales
- ViewportManager for devices
- ThemeManager for visual styles
Your Game in Numbers
| Metric | Value |
|---|---|
| Lines of code | ~150 lines |
| Time taken | 30 minutes |
| Features | 5 game modes, Vue UI, themes |
| Platform support | Web (iOS/Android via Capacitor) |
| UI framework | Vue 3 with Composition API |
🚀 Next Steps
Enhance Your Game
Add more gameplay:
- EntityRegistry - Auto entity tracking
- SphereOfInfluence - Event cascading
- ActionValidator - Context-aware actions
Improve UI:
- Vue Integration Guide - Advanced patterns
- Framework Bridges - React/Svelte
- Add pause menu, settings, leaderboard
Study Example:
- LINX Game - Complete game breakdown
- Graph-based gameplay
- Energy economy
- Bot AI
Go Mobile:
- Mobile Export - iOS/Android with Capacitor
- Touch controls
- Performance optimization
📚 Full Documentation
- Multi-Scale Gameplay - Detailed mode examples
- Gameplay Accessibility - Why it matters
- Architecture - Design philosophy
- All APIs - Complete API reference
🎯 Tips for Success
1. Use Capability Flags
// ✅ GOOD - adapts to mode
if (gameModeManager.can("specialAbilities")) {
this.enableDivinePowers();
}
// ❌ BAD - hardcoded mode check
if (mode === GameModeType.MODE_5) {
this.enableDivinePowers();
}2. React to Mode Changes
// ✅ GOOD - event-driven
gameModeManager.onModeChange(() => {
this.adaptGameplay();
});
// ❌ BAD - polling in update
update() {
if (this.mode !== this.gameModeManager.getCurrentMode()) {
this.adaptGameplay();
}
}3. Use Reactive Props
// ✅ GOOD - Vue handles rendering
vueBridge.updateProps("hud", { score: newScore });
// ❌ BAD - manual DOM manipulation
document.getElementById("score").textContent = newScore;Congratulations! You built your first Multi-Scale game with CASCADA! 🎉