# Туториал: меню, пауза и смена сцен

Как устроены экраны в Russiano2D: менеджер сцен в `$`, отложенные переходы,
интерфейс на RmlUi и на узлах `<ui.*>`, пауза и сохранения.

Справочник по вызовам — [HIGH_LEVEL_API.md](HIGH_LEVEL_API), разделы
[`$.scene`](HIGH_LEVEL_API) и [`$.ui`](HIGH_LEVEL_API).

- [Зачем сцены](#зачем-сцены)
- [Как устроена сцена](#как-устроена-сцена)
- [Переключение и переходы](#переключение-и-переходы)
- [Стек сцен: пауза и оверлеи](#стек-сцен-пауза-и-оверлеи)
- [Два пути интерфейса](#два-пути-интерфейса)
- [Меню на RmlUi](#меню-на-rmlui)
- [HUD на узлах](#hud-на-узлах)
- [Пауза](#пауза)
- [Что живёт дольше сцены](#что-живёт-дольше-сцены)
- [Сохранения между запусками](#сохранения-между-запусками)

---

## Зачем сцены

Сцена — это экран игры: меню, уровень, пауза, экран проигрыша. При переходе
между сценами нужно не забыть убрать за предыдущей: узлы, тела, твины, таймеры,
документы интерфейса, музыку. `$` делает это сам — при смене сцены мир
очищается целиком, а сцена получает вызов `exit()`, где можно доделать своё.

## Как устроена сцена

```js
$.scene.add('level', {
    enter($)  { /* построить мир */ },
    exit()    { /* убрать за собой */ },
    update(dt, $) { /* логика кадра */ },
    render($) { /* необязательно: своя отрисовка */ },
});
```

Вместо объекта можно дать функцию — тогда она выполняется как `enter`:

```js
$.scene.add('level', ($) => { $('<player>').at(100, 200).appendTo($.world); });
```

Внутри методов `this` — сама сцена, поэтому состояние удобно хранить полями
(`this.score`, `this.doc`).

## Переключение и переходы

```js
$.scene.load('level');                              // с затемнением по умолчанию
$.scene.load('level', { transition: 'none' });      // мгновенно
$.scene.load('level', { ms: 500 });                 // длительность перехода
$.scene.restart();                                  // перезапустить текущую
```

Переход **отложен**: `load()` только ставит запрос, а замена происходит в
начале следующего кадра. Поэтому менять сцену можно прямо из обработчика клика
или из `update`, не разрушая объект посреди его работы.

```js
$.scene.current()      // 'level' или null
$.scene.names()        // список зарегистрированных сцен
$.scene.busy()         // идёт ли переход прямо сейчас
```

## Стек сцен: пауза и оверлеи

```js
$.scene.push('pause');   // запомнить текущую и открыть паузу
$.scene.pop();           // вернуться к запомненной
$.scene.stack();         // список отложенных сцен
```

`push`/`pop` — это обычные `load`, поэтому мир всё равно очищается. Если нужно
сохранить мир уровня «под» паузой, делайте паузу не сценой, а оверлеем
(см. ниже).

## Два пути интерфейса

**Закон интерфейса — RmlUi** ([UI_RMLUI_LAW.md](UI_RMLUI_LAW)): меню,
экраны и диалоги делаются документами `.rml` + `.rcss`. Таблица ниже описывает,
что осталось рабочим в существующих играх, а не то, что стоит выбирать для
нового интерфейса:

| Путь | Когда выбирать |
|---|---|
| Документы RmlUi (`$.ui.doc('ui/menu.rml')`) | **Основной путь**: меню, настройки, инвентарь, диалоги, экраны |
| Узлы `<ui.panel>`, `<ui.label>`, `<ui.button>`, `<ui.bar>`, `<ui.image>` | Быстрый HUD поверх сцены в существующих играх; новые меню на них не строятся |

Смешивать можно так: HUD на узлах, **весь интерфейс** — на RmlUi.

## Меню на RmlUi

Документ лежит в `game/ui/menu.rml` со стилями `menu.rcss`:

```xml
<rml>
<head><link type="text/rcss" href="menu.rcss"/></head>
<body>
    <div id="menu">
        <h1>RUSSIANO2D</h1>
        <button id="btn-play">Играть</button>
        <button id="btn-quit">Выход</button>
    </div>
</body>
</rml>
```

```js
export default function installMenu($) {
    $.scene.add('menu', {
        enter($) {
            // $.ui.doc() кэширует обёртку по пути, а .on() вешает слушатель
            // один раз — иначе после возврата в меню клик сработал бы дважды.
            this.doc = $.ui.doc('ui/menu.rml')
                .on('btn-play', 'click', () => $.scene.load('level'))
                .on('btn-quit', 'click', () => $.quit())
                .show();
        },
        exit() { if (this.doc) this.doc.hide(); },
        update(dt, $) {
            // Меню обязано работать и с клавиатуры: мышью пользуются не все.
            if ($.input.pressed('enter')) $.scene.load('level');
            if ($.input.pressed('escape')) $.quit();
        },
    });
}
```

Полезные методы документа:

```js
doc.text('score', '120');                 // заменить содержимое элемента
doc.cls('panel', 'hidden', true);         // добавить/снять CSS-класс
doc.style('bar', 'width', '50%');         // инлайновое свойство
doc.visible() / .hide() / .unload();
$.ui.icon('directions_run')               // 2235 иконок Material Design встроены
```

> Слушатели живут внутри RmlUi и не снимаются вместе со сценой, поэтому
> вешать их повторно при каждом входе нельзя — для этого `$.ui.doc()` отдаёт
> один и тот же объект, а `.on()` срабатывает только в первый раз.

## HUD на узлах

```js
$('<ui.bar>',  { id: 'hp',  value: 100, max: 100 }).at(120, 30).appendTo($.ui);
$('<ui.label>', { id: 'score', text: 'Очки: 0' }).at(30, 60).appendTo($.ui);

$.update(() => {
    $.ui.bar('#hp', $('#hero').hp(), 100);
    $.ui.label('#score', 'Очки: ' + this.score);
});
```

Узлы интерфейса — обычные узлы: у них есть селекторы, события и стили
(`.color`, `.alpha`, `.size`). Для чтения старого кода: legacy-события работают без разметки. Новая кнопка
меню создаётся в RmlUi и подписывается через `doc.on`, как выше:

```js
$('<ui.button>', { id: 'retry', text: 'Ещё раз' }).at(640, 400).appendTo($.ui)
    .on('click', () => $.scene.restart());
```

## Пауза

Пауза — это остановка игрового времени, а не смена сцены: мир остаётся на
экране, твины и таймеры замирают.

```js
if ($.input.pressed('escape')) {
    if ($.time.isPaused()) {
        $.time.resume();
        $('#overlay').hide(); $('#overlay-text').hide();
    } else {
        $.time.pause();
        $('#overlay').show();
        $('#overlay-text').show().text('Пауза');
    }
}
```

`$.time.pause()` влияет на `$.time.delta()`, твины, `$.time.wait/every` и
обновление камеры. Вспышки и тряска идут по реальному времени — они должны
догореть даже на паузе.

## Что живёт дольше сцены

При смене сцены `$` уничтожает все узлы, кроме:

* узлов интерфейса, если сцена загружена с `{ keepUI: true }`;
* узлов с классом `scene-persistent`.

```js
$('<ui.panel>', { id: 'fps' }).appendTo($.ui).addClass('scene-persistent');
```

Текстуры, спрайты, звуки и документы RmlUi живут в движке и переживают смену
сцены — загружать их повторно не нужно (повторный `loadTexture` вернёт тот же id).

## Сохранения между запусками

```js
$.store.file('save.json').load();      // при старте
$.store.set('best', 1200);
$.store.save();                        // когда удобно — например, на выходе
$.store.autoSave(30000);               // или пусть сохраняет сам

$.exit(() => $.store.save());          // последний шанс записать прогресс
```

`$.exit(fn)` вызывается движком при завершении — в том числе когда игру
останавливает агент.

---

Дальше: [HIGH_LEVEL_API.md](HIGH_LEVEL_API) — полный справочник,
[AGENT_API.md](AGENT_API) — как проверить меню и переходы без рук.
