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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('piggy', 'assets/pics/pigchampagne.png');
        this.load.image('atari', 'assets/sprites/atari130xe.png');
    }

    create ()
    {
        const piggy = this.add.image(400, 300, 'piggy');

        const textureA = this.textures.get('piggy');
        const textureB = this.textures.get('atari');

        this.input.on('pointerdown', () => {

            textureA.setSource(textureB.source);

        });
    }
}

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

const game = new Phaser.Game(config);

Зачем это нужно? Проблема и решение

Представьте, что у вас есть объект (например, спрайт персонажа), и вам нужно мгновенно изменить его внешний вид на другой. Самый очевидный способ — создать новый спрайт с другой текстурой и удалить старый. Однако это не всегда эффективно, особенно если переключений много.

Метод texture.setSource() предлагает альтернативу. Вместо замены спрайта мы заменяем источник данных (пиксели) у самой текстуры. Все объекты, использующие эту текстуру, мгновенно обновятся. Это похоже на замену холста, на котором нарисовано изображение, при этом сам фрейм (текстура в памяти Phaser) остаётся прежним.

Разбираем пример: Загрузка и получение текстур

Вначале в методе preload() загружаются два изображения с присвоением им уникальных ключей.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('piggy', 'assets/pics/pigchampagne.png');
    this.load.image('atari', 'assets/sprites/atari130xe.png');
}

В create() создаётся изображение piggy. Затем с помощью менеджера текстур this.textures получаем ссылки на объекты текстур, а не на спрайты.

create ()
{
    const piggy = this.add.image(400, 300, 'piggy');

    const textureA = this.textures.get('piggy');
    const textureB = this.textures.get('atari');
}

textureA — это объект текстуры, которую сейчас использует изображение piggy. textureB — текстура, которую мы хотим использовать в качестве нового источника.

Волшебный щелчок: Использование setSource

Вся магия происходит по клику мыши. Мы назначаем текстуре textureA (с ключом 'piggy') источник данных от текстуры textureB (с ключом 'atari').

this.input.on('pointerdown', () => {
    textureA.setSource(textureB.source);
});

Важно понять, что меняется именно внутренний источник (source) — данные изображения. Ключ текстуры textureA остаётся 'piggy', но теперь она выглядит как 'atari'. Изображение piggy на сцене мгновенно меняет свою картинку. Если бы на сцене было несколько объектов с текстурой 'piggy', все они бы синхронно обновились.

Важные нюансы и ограничения API

1. **Размер и формат:** Phaser не масштабирует источник под размер целевой текстуры. Если новый источник имеет другие пропорции, изображение может отобразиться некорректно. Убедитесь, что текстуры имеют одинаковые или подходящие размеры. 2. **Кэширование:** Замена источника не затрагивает кэш загрузчика (this.load). Ключ 'piggy' в кэше всё ещё будет вести на исходное изображение поросёнка. 3. **Область видимости:** Метод setSource() работает с объектом типа Phaser.Textures.Texture. Источник (source) — это свойство такого объекта, обычно это HTMLImageElement или Canvas. 4. **Производительность:** Эта операция очень быстрая, так как не требует пересоздания GPU-текстуры, а лишь обновляет её данные.

Практическое применение в играх

Вот несколько идей, где это может быть полезно: * **Аватары и снаряжение:** У вас есть базовая текстура персонажа. При надевании шлема вы подменяете источник текстуры шлема на текстуру шлема другого вида. * **Эффекты состояния:** Текстура здорового дерева мгновенно меняется на текстуру срубленного пня. * **Оптимизация частиц:** Можно иметь одну текстуру частицы и менять её источник для создания разнообразных эффектов (огонь, дым, звёзды), не создавая сотни отдельных текстурных объектов. * **Динамические интерфейсы:** Создание кнопок, иконки которых меняются в зависимости от контекста.

Пример смены снаряжения:

// Предположим, textureBaseArmor — текстура для слота брони на персонаже.
// textureNewArmor — текстура новой брони из инвентаря.
equipButton.on('pointerdown', () => {
    textureBaseArmor.setSource(textureNewArmor.source);
});

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

Метод setSource() — это скрытый gem в API Phaser для работы с текстурами. Он позволяет динамически и эффективно менять визуальное представление объектов, что полезно для систем кастомизации, визуальных эффектов и оптимизации. Для экспериментов попробуйте создать анимацию, плавно переключая источники между несколькими текстурами по таймеру, или реализуйте простой редактор спрайтов, где игрок может комбинировать части изображений.