Создание игры «Space Invaders» на JavaScript с использованием PixiJS.

В современном мире разработки игр создание простых, увлекательных проектов — отличный способ изучить основные концепции программирования и познакомиться с различными инструментами и библиотеками. Одной из таких классических игр, которая часто служит примером при обучении, является «Space Invaders». В этой статье мы подробно рассмотрим, как создать игру «Space Invaders» на языке JavaScript с использованием мощной графической библиотеки PixiJS. Это позволит не только самостоятельно реализовать игровую логику, но и понять, как работать с анимациями, обработкой событий и взаимодействием с пользователем.

PixiJS — это одна из самых популярных 2D GPU-ускоренных библиотек, которая позволяет создавать сложные и плавные визуальные эффекты в браузере. Благодаря простому API и высокой производительности, PixiJS отлично подходит для создания прототипов и полноценных игр. В ходе статьи мы поэтапно создадим игру, начиная с базовой сцены и заканчивая реализацией врагов, выстрелов и системы очков.

Подготовка окружения и знакомство с PixiJS

Перед тем как приступать непосредственно к разработке, необходимо подготовить среду и подключить необходимые библиотеки. PixiJS можно использовать как через CDN, так и устанавливая через npm для более сложных проектов. Для начального этапа подойдет вариант с прямым подключением через тег <script> в HTML.

Основные понятия PixiJS — это сцена (Stage, Container), спрайты (Sprite), текстуры и рендерер. Сцена — контейнер всех объектов, которые должны быть отображены. Спрайт — это объект с изображением, который можно перемещать и анимировать. Рендерер отвечает за вывод изображения на экран.

Рассмотрим простой пример создания приложения PixiJS, чтобы понять структуру проекта:

Пример базовой инициализации PixiJS

    
const app = new PIXI.Application({
    width: 800,
    height: 600,
    backgroundColor: 0x000000,
});
document.body.appendChild(app.view);
    
  

Данный код создаёт игровой холст размером 800 на 600 пикселей с чёрным фоном и добавляет его в тело страницы. После этого можно добавлять спрайты и другие объекты в сцену и запускать игровой цикл.

Создание игрового поля и игрока

Первоочередной задачей станет создание области, где будет разворачиваться геймплей, и визуальное представление игрока — космического корабля. Мы создадим класс Player, который будет отвечать за отображение, движение и свойства игрока.

Для изображения игрока можно использовать простую фигуру или загрузить внешний файл с изображением, например, в формате PNG. Для упрощения возьмём встроенную графику и создадим треугольник, символизирующий корабль, с помощью API PixiJS — графического объекта Graphics.

Код для создания и управления игроком

    
// Создание объекта игрока
class Player {
    constructor(app) {
        this.app = app;
        this.ship = new PIXI.Graphics();
        this.ship.beginFill(0xFFFFFF);
        this.ship.moveTo(0, 0);
        this.ship.lineTo(40, 20);
        this.ship.lineTo(0, 40);
        this.ship.endFill();

        this.ship.x = app.view.width / 2 - 20;
        this.ship.y = app.view.height - 50;
        app.stage.addChild(this.ship);
        this.speed = 5;

        // Инициализация флагов движения
        this.moveLeft = false;
        this.moveRight = false;

        this.bindEvents();
    }

    bindEvents() {
        window.addEventListener('keydown', e => {
            if (e.code === 'ArrowLeft') this.moveLeft = true;
            if (e.code === 'ArrowRight') this.moveRight = true;
        });

        window.addEventListener('keyup', e => {
            if (e.code === 'ArrowLeft') this.moveLeft = false;
            if (e.code === 'ArrowRight') this.moveRight = false;
        });
    }

    update() {
        if (this.moveLeft && this.ship.x > 0) {
            this.ship.x -= this.speed;
        }
        if (this.moveRight && this.ship.x < this.app.view.width - 40) {
            this.ship.x += this.speed;
        }
    }
}
    
  

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

Враги и их поведение

В игре "Space Invaders" противниками являются ряды вражеских инопланетян, которые движутся горизонтально с периодическим спуском вниз. Для создания врагов также можно использовать либо спрайты с текстурами, либо графику. Для демонстрации реализуем их с помощью простых квадратов, окрашенных в зелёный цвет.

Особенность врагов — групповой паттерн движения: одновременно все враги сдвигаются вправо до края, затем вниз и влево, и так далее. Также потребуется проверка столкновений с выстрелами игрока и удаление уничтоженных врагов.

Класс врага и группа врагов

    
class Enemy {
    constructor(x, y, app) {
        this.app = app;
        this.sprite = new PIXI.Graphics();
        this.sprite.beginFill(0x00FF00);
        this.sprite.drawRect(0, 0, 30, 30);
        this.sprite.endFill();
        this.sprite.x = x;
        this.sprite.y = y;
        app.stage.addChild(this.sprite);
        this.isAlive = true;
    }

    destroy() {
        this.app.stage.removeChild(this.sprite);
        this.isAlive = false;
    }
}

class EnemyGroup {
    constructor(app) {
        this.app = app;
        this.enemies = [];
        this.speedX = 1;
        this.speedY = 10;
        this.moveDirection = 1; // 1 - вправо, -1 - влево
        this.createEnemies();
    }

    createEnemies() {
        const rows = 4;
        const cols = 10;
        const startX = 50;
        const startY = 50;
        const spacing = 40;

        for (let row = 0; row < rows; row++) {
            for (let col = 0; col < cols; col++) {
                const enemy = new Enemy(startX + col * spacing, startY + row * spacing, this.app);
                this.enemies.push(enemy);
            }
        }
    }

    update() {
        // Проверяем необходимость изменить направление движения
        let shouldChangeDirection = false;
        for (let enemy of this.enemies) {
            if (!enemy.isAlive) continue;
            if (enemy.sprite.x + enemy.sprite.width >= this.app.view.width && this.moveDirection > 0) {
                shouldChangeDirection = true;
            }
            if (enemy.sprite.x <= 0 && this.moveDirection < 0) {
                shouldChangeDirection = true;
            }
        }

        if (shouldChangeDirection) {
            this.moveDirection *= -1;
            for (let enemy of this.enemies) {
                if (enemy.isAlive) enemy.sprite.y += this.speedY;
            }
        } else {
            for (let enemy of this.enemies) {
                if (enemy.isAlive) enemy.sprite.x += this.speedX * this.moveDirection;
            }
        }
    }
}
    
  

В данном коде представлен класс Enemy, создающий отдельного врага и метод destroy для удаления из сцены. Класс EnemyGroup управляет всей группой врагов и реализует логику движения и смены направления при достижении границ игрового поля.

Реализация выстрелов и столкновений

Для полноценной игры необходимо реализовать возможность стрелять по врагам и обрабатывать столкновения пуль с ними. Мы создадим класс Bullet, который будет представлять выстрел игрока. Пуля будет двигаться вверх с постоянной скоростью и уничтожал врага при попадании.

Обработка столкновений проще всего осуществляется с помощью проверки пересечения прямоугольных областей (bounding box collision). Если прямоугольники пули и врага пересекаются, можно считать, что произошло попадание.

Класс Bullet и проверка столкновений

    
class Bullet {
    constructor(x, y, app) {
        this.app = app;
        this.sprite = new PIXI.Graphics();
        this.sprite.beginFill(0xFFFF00);
        this.sprite.drawRect(0, 0, 5, 15);
        this.sprite.endFill();
        this.sprite.x = x;
        this.sprite.y = y;
        app.stage.addChild(this.sprite);
        this.speed = 7;
        this.isAlive = true;
    }

    update() {
        this.sprite.y -= this.speed;
        if (this.sprite.y < 0) {
            this.destroy();
        }
    }

    destroy() {
        this.app.stage.removeChild(this.sprite);
        this.isAlive = false;
    }
}

// Проверка столкновения 2 прямоугольников
function isCollision(rect1, rect2) {
    return !(
        rect1.x + rect1.width < rect2.x ||
        rect1.x > rect2.x + rect2.width ||
        rect1.y + rect1.height < rect2.y ||
        rect1.y > rect2.y + rect2.height
    );
}
    
  

Класс Bullet создаёт жёлтый прямоугольник, который движется вверх, и удаляется, когда покидает поле видимости. Функция isCollision проверяет, пересекаются ли два объекта, основываясь на их координатах и размерах.

Объединение всех компонентов и игровой цикл

После реализации отдельных компонентов приступим к объединению их в одну игру. Для этого добавим в главный скрипт создание экземпляров Player, EnemyGroup и массив пуль. Реализуем управление выстрелами игрока — по нажатию пробела будет создаваться новая пуля из центра корабля.

Основной игровой цикл — функция update, вызываемая при помощи requestAnimationFrame через PIXI.Application.ticker, — будет обновлять положение игрока, врагов и пуль, а также проверять столкновения. Если пуля попадает во врага, последний уничтожается, а пуля удаляется.

Общий код игрового цикла и обработчиков событий

    
const app = new PIXI.Application({
    width: 800,
    height: 600,
    backgroundColor: 0x000000,
});
document.body.appendChild(app.view);

const player = new Player(app);
const enemies = new EnemyGroup(app);
const bullets = [];

window.addEventListener('keydown', e => {
    if (e.code === 'Space') {
        // Создание новой пули из центра корабля
        const bulletX = player.ship.x + 20 - 2.5; // центр корабля минус половина ширины пули
        const bulletY = player.ship.y;
        const bullet = new Bullet(bulletX, bulletY, app);
        bullets.push(bullet);
    }
});

app.ticker.add(() => {
    player.update();
    enemies.update();

    // Обновление пуль
    for (let i = bullets.length - 1; i >= 0; i--) {
        const bullet = bullets[i];
        if (!bullet.isAlive) {
            bullets.splice(i, 1);
            continue;
        }
        bullet.update();

        // Проверка столкновений с врагами
        for (let enemy of enemies.enemies) {
            if (!enemy.isAlive) continue;

            const bulletRect = {
                x: bullet.sprite.x,
                y: bullet.sprite.y,
                width: bullet.sprite.width,
                height: bullet.sprite.height
            };

            const enemyRect = {
                x: enemy.sprite.x,
                y: enemy.sprite.y,
                width: enemy.sprite.width,
                height: enemy.sprite.height
            };

            if (isCollision(bulletRect, enemyRect)) {
                enemy.destroy();
                bullet.destroy();
                break;
            }
        }
    }
});
    
  

В этом участке реализуется обработка выстрелов по нажатию пробела, обновление состояний всех игровых объектов и взаимодействие между ними. По мере удаления пуль они удаляются из массива, что предотвращает утечки памяти.

Дополнительные возможности и улучшения

Созданная версия игры — базовая и может быть значительно расширена. Например, можно добавить счёт очков, уровни сложности, улучшенную графику, звуковое сопровождение и анимацию врагов. Также стоит реализовать контроллеры для мобильных устройств и меню старта.

Следует продумать логику окончания игры — при достижении врагами нижней границы или попадании в игрока будет полезно показывать сообщение о проигрыше и предлагать перезапустить игру. Помимо этого, можно использовать спрайт-листы и анимации для улучшения визуального восприятия.

Таблица потенциальных улучшений

Улучшение Описание Сложность реализации
Счёт и таблица рекордов Отслеживание набранных очков, сохранение и отображение лучших результатов Средняя
Анимации врагов Использование спрайт-листов для плавного движения врагов и их атак Высокая
Звуковые эффекты Добавление аудио при выстрелах и разрушениях Низкая — средняя
Уровни и увеличение сложности Увеличение скорости врагов и их количества с продвижением по уровням Средняя
Меню и интерфейс Создание экранов старта, паузы и конца игры с кнопками управления Средняя

Заключение

Создание игры "Space Invaders" с использованием JavaScript и PixiJS — увлекательный и полезный проект, который помогает разобраться с основами игровой логики, графики и взаимодействия с пользователем. В ходе статьи мы рассмотрели пошаговую реализацию ключевых компонентов: от инициализации сцены и создания игрока до программирования врагов, выстрелов и столкновений.

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

Такой проект отлично подходит как учебное задание для начинающих разработчиков игр и веб-программирования, а также может служить основой для более сложных и масштабных игровых решений.

Вот HTML-таблица с 10 LSI-запросами для статьи 'Создание игры "Space Invaders" на JavaScript с использованием PixiJS':

```html

Создание 2D игр на JavaScript PixiJS для разработчиков игр Примеры игр на JavaScript Коды для Space Invaders Графика в играх на PixiJS
Обучение JavaScript для начинающих История игры Space Invaders Анимация в PixiJS Разработка игр с использованием Canvas Советы по программированию игр

```

Эта таблица содержит пять строк и столбцов с релевантными LSI-запросами, которые могут помочь в оптимизации статьи.