# Getting started: новая игра с нуля

Минимальный набор шагов, чтобы получить работающее приложение на движке.

## 1. Структура

```
apps/my-game/
  index.html
  vite.config.ts        # alias @rpg/engine -> ../../packages/engine/src/index.ts
  src/
    main.ts             # bootstrap
    Game.ts             # общий контекст игры (опционально, но удобно)
    scenes/             # сцены игры
```

`vite.config.ts`:

```ts
import { defineConfig } from 'vite';
export default defineConfig({
    resolve: {
        alias: { '@rpg/engine': '../../packages/engine/src/index.ts' }
    }
});
```

## 2. Bootstrap (main.ts)

```ts
import { Engine, computeScale, type EngineOptions } from '@rpg/engine';
import { BootScene } from './scenes/BootScene';

const VW = 480, VH = 270; // виртуальное разрешение

async function main(): Promise<void> {
    const container = document.getElementById('game')!;

    const options: EngineOptions = {
        virtualWidth: VW,
        virtualHeight: VH,
        scale: computeScale(VW, VH, window.innerWidth, window.innerHeight),
        background: 0x0b0b12,
        fixedFps: 60,
        maxFps: 30, // лок отрисовки (0 — без лимита); FPS-счётчик — F3
        parent: container
    };

    const engine = new Engine(options);
    await engine.init();          // WebGL + ввод + оверлей переходов
    engine.input.bindActions({    // «действия» вместо кодов клавиш
        advance: ['Space', 'Enter'],
        menu: ['Escape'],
        up: ['KeyW', 'ArrowUp'],
        down: ['KeyS', 'ArrowDown']
    });
    engine.input.bindGamepad({    // те же действия на геймпаде (кнопки стандартной карты)
        advance: [0],             // A
        menu: [9],                // Start
        up: [12],
        down: [13]
    });

    await engine.scenes.push(new BootScene(engine));
    await engine.start();
}

void main();
```

Ресайз окна обрабатывается автоматически (`autoResize: true` по умолчанию):
масштаб пересчитывается, виртуальное разрешение не меняется.

## 3. Сцены

Сцена — экран игры. Минимальная:

```ts
import { Container, Text, type Scene } from '@rpg/engine';

export class TitleScene implements Scene {
    private view = new Container();

    constructor(engine: Engine) {
        const t = new Text({ text: 'Моя игра', style: { fill: 0xffffff, fontSize: 16, fontFamily: 'monospace' } });
        this.view.addChild(t);
        engine.renderer.uiRoot.addChild(this.view);
    }

    enter(): void {}
    exit(): void { this.view.destroy({ children: true }); }
    update(dt: number): void {
        if (engine.input.isActionJustPressed('advance')) {
            void engine.scenes.replace(new GameScene(engine), { duration: 0.4 });
        }
    }
    render(): void {}
}
```

Стек сцен: `push` (открыть поверх), `pop` (закрыть), `replace` (заменить верхнюю).
Любой метод принимает `{ duration, color }` — fade-затемнение между сценами.

## 4. Ассеты

```ts
import { AssetLoader } from '@rpg/engine';

const assets = new AssetLoader((key) => `assets/${key}.png`);
await assets.load(['tiles/grass', 'chars/hero'], (p) => console.log(`${Math.round(p * 100)}%`));
const tex = assets.texture('tiles/grass');

// Атлас (Spritesheet JSON + PNG):
const sheet = await assets.loadAtlas('chars/hero_sheet');
const walkFrames = assets.frames('chars/hero_sheet', 'hero_walk');
```

Смотрите [assets-audio-save.md](assets-audio-save.md).

## 5. Шрифт

```ts
import { ensurePixelFont, PixelText } from '@rpg/engine';

await ensurePixelFont('fonts/VT323-Regular.ttf'); // до создания текста
const label = new PixelText({ text: 'Привет', size: 10, color: 0xf0d878 });
```

## 6. Сборка

```bash
npm run dev        # dev-сервер
npm run build      # прод-сборка
npm run typecheck  # движок + игра
npm test           # Vitest
```

## Что дальше

- [core.md](core.md) — игровой цикл, твины, состояние, настройки
- [maps.md](maps.md) — если игра с картами/движением по тайлам
- [recipes.md](recipes.md) — готовые решения типовых задач