О чем этот пример

Разработка игры с десятками сцен превращает один файл в нечитаемую простыню кода. Phaser позволяет выносить логику сцен в отдельные файлы и загружать их по мере необходимости. Это основа модульной архитектуры, которая упрощает поддержку, командную работу и динамическую подгрузку контента. В этой статье разберем, как правильно загружать внешние сцены и избежать типичных ошибок.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene {

    constructor ()
    {
        super();
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        //  It's essential that the key given here is the exact class name used in the JS file. It's case-sensitive.
        //  See the SceneB.js file and documentation for details.
        this.load.sceneFile('ExternalScene', 'assets/loader-tests/ExternalScene.js');
    }

    create ()
    {
        this.scene.start('myScene');
    }

}

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    backgroundColor: '#000',
    scene: Example
};

const game = new Phaser.Game(config);

Зачем нужна загрузка сцен из файлов

Когда игра растет, хранение всех сцен в одном основном файле становится проблемой. Это замедляет первоначальную загрузку и усложняет навигацию по коду. Метод this.load.sceneFile() решает эту проблему, позволяя разбить игру на логические модули.

Вы можете разрабатывать меню, уровни и экраны результатов в отдельных файлах. Такой подход особенно полезен для: * Больших проектов с командой разработчиков. * Игр с динамически загружаемыми уровнями (DLC, пользовательский контент). * Структурирования кода по принципу одной ответственности.

Ключевой API: `load.sceneFile()`

Метод this.load.sceneFile() загружает JavaScript-файл, содержащий класс сцены, и регистрирует его в менеджере сцен Phaser. Его сигнатура проста, но есть важные нюансы.

this.load.sceneFile(key, url);

* key (строка): Уникальный ключ для идентификации сцены. **Это должен быть точный, чувствительный к регистру идентификатор сцены**, который используется в this.scene.start(), this.scene.launch() или в конфигурации игры. * url (строка): Путь к JS-файлу с классом сцены.

Важно: этот метод только загружает и регистрирует класс. Самостоятельный запуск сцены не происходит.

Разбор примера кода: шаг за шагом

Рассмотрим пример, где основная сцена служит загрузчиком для внешней сцены.

**1. Настройка базового URL:** Перед загрузкой файла полезно установить базовый путь. Это сокращает дублирование в URL.

this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');

**2. Загрузка файла сцены:** Здесь происходит магия. Мы говорим загрузчику: "Возьми файл по этому адресу и зарегистрируй его содержимое как сцену с ключом 'ExternalScene'."

this.load.sceneFile('ExternalScene', 'assets/loader-tests/ExternalScene.js');

Ключ 'ExternalScene' должен совпадать с тем, который используется в загружаемом файле ExternalScene.js (например, в его super('ExternalScene') или в системном ключе).

**3. Запуск загруженной сцены:** После завершения загрузки в методе create() мы запускаем уже зарегистрированную сцену по её ключу.

this.scene.start('myScene');

**Внимание:** В данном примере есть несоответствие. Код загружает сцену с ключом 'ExternalScene', а запускает 'myScene'. Для работоспособности ключи должны совпадать, либо в ExternalScene.js должна быть сцена с ключом 'myScene'. Это частая ошибка новичков.

Структура внешнего файла сцены

Чтобы загрузка сработала, загружаемый файл должен корректно экспортировать класс, унаследованный от Phaser.Scene. Phaser автоматически выполнит этот файл и зарегистрирует класс.

Пример содержимого ExternalScene.js:

class ExternalScene extends Phaser.Scene {
    constructor() {
        // Ключ, под которым сцена будет доступна в системе, передается в super()
        super({ key: 'ExternalScene' });
    }
    create() {
        this.add.text(100, 100, 'Это внешняя сцена!', { fill: '#0f0' });
    }
}

Критически важно, чтобы ключ, переданный в конструктор сцены ('ExternalScene'), совпадал с ключом, использованным в load.sceneFile(). Именно по этому ключу вы будете обращаться к сцене из других частей игры.

Лучшие практики и частые ошибки

**1. Регистр символов имеет значение:** 'externalscene', 'ExternalScene' и 'EXTERNALSCENE' — это три разных ключа для Phaser. Будьте последовательны.

**2. Загрузка в preload:** Метод load.sceneFile() работает через систему загрузки Phaser, поэтому его вызов возможен только в preload() или до начала загрузки.

**3. Не смешивайте с конфигурацией:** Внешние сцены, загруженные через sceneFile, **не нужно** добавлять в массив scene в конфиге игры. Они регистрируются динамически.

**4. Проверка пути (URL):** Самая частая проблема — 404 ошибка. Убедитесь, что путь к файлу корректен. Использование setBaseURL() помогает избежать ошибок в длинных путях.

// Плохо: легко ошибиться в длинном пути
this.load.sceneFile('Level1', 'https://mycdn.com/game/assets/scenes/world2/level1.js');
// Лучше
this.load.setBaseURL('https://mycdn.com/game/');
this.load.sceneFile('Level1', 'assets/scenes/world2/level1.js');

Что попробовать дальше

Использование load.sceneFile() — это мощный шаг к созданию масштабируемой и поддерживаемой игры на Phaser. Он отделяет логику сцен от их загрузки, открывая путь к сложным архитектурным решениям. **Идеи для экспериментов:** 1. Реализуйте систему загрузки уровней "на лету": когда игрок выбирает уровень в меню, загрузите файл с его сценой и сразу запустите. 2. Создайте плагин или систему, которая по конфигурационному JSON-файлу загружает список необходимых сцен для текущей игровой сессии. 3. Поэкспериментируйте с отложенной загрузкой: загружайте сцены для следующего уровня в фоновом режиме, пока игрок смотрит кат-сцену.