Игры на Paper.js

Paper.js предоставляет векторную сцену и удобную модель работы с объектами, что делает его подходящим инструментом для создания 2D-игр с упором на геометрию, анимацию и интерактивность. Основой является объект Project, содержащий Layer и графические элементы (Path, Shape, Group), а также система обработки событий и таймлайна.

Игровое приложение на Paper.js обычно строится вокруг:

  • Сцены (Scene) — текущее состояние игрового мира
  • Игрового цикла (Game Loop) — обновление и отрисовка
  • Обработки ввода — мышь, клавиатура
  • Системы объектов — игрок, враги, снаряды, эффекты

Игровой цикл и обновление состояния

Paper.js предоставляет встроенный обработчик onFrame, который вызывается каждый кадр. Это основной механизм обновления логики игры.

function onFrame(event) {
    // event.delta — время между кадрами
}

Ключевые задачи в onFrame:

  • Перемещение объектов
  • Проверка столкновений
  • Удаление/создание объектов
  • Анимации

Пример простого движения:

let player = new Path.Circle({
    center: view.center,
    radius: 20,
    fillColor: 'blue'
});

function onFrame(event) {
    player.position.x += 100 * event.delta;
}

Работа с координатами и векторами

Paper.js использует объект Point для представления координат и векторов. Это существенно упрощает физику и движение.

let velocity = new Point(2, 3);
player.position += velocity;

Полезные операции:

  • add, subtract
  • normalize
  • length
  • getDistance

Пример движения к цели:

let target = new Point(300, 200);

function onFrame() {
    let direction = target.subtract(player.position);
    direction.length = 2;
    player.position += direction;
}

Управление вводом

Paper.js поддерживает события мыши и клавиатуры.

Мышь

function onMouseMove(event) {
    player.position = event.point;
}

Клавиатура

let keys = {};

function onKeyDown(event) {
    keys[event.key] = true;
}

function onKeyUp(event) {
    keys[event.key] = false;
}

function onFrame() {
    if (keys['left']) player.position.x -= 3;
    if (keys['right']) player.position.x += 3;
}

Организация игровых объектов

Для удобства используется объектно-ориентированный подход.

Базовый класс объекта

class GameObject {
    constructor() {
        this.shape = null;
    }

    update(delta) {}

    remove() {
        this.shape.remove();
    }
}

Игрок

class Player extends GameObject {
    constructor(position) {
        super();
        this.shape = new Path.Circle({
            center: position,
            radius: 20,
            fillColor: 'blue'
        });
        this.speed = 200;
    }

    update(delta) {
        let move = new Point(0, 0);

        if (keys['w']) move.y -= 1;
        if (keys['s']) move.y += 1;
        if (keys['a']) move.x -= 1;
        if (keys['d']) move.x += 1;

        move.length = this.speed * delta;
        this.shape.position += move;
    }
}

Система столкновений

Paper.js предоставляет метод intersects, который упрощает проверку пересечений.

if (player.shape.intersects(enemy.shape)) {
    console.log("Collision!");
}

Более точная проверка

let distance = player.shape.position.getDistance(enemy.shape.position);

if (distance < playerRadius + enemyRadius) {
    // столкновение
}

Генерация врагов

Пример создания простых врагов:

class Enemy extends GameObject {
    constructor(position) {
        super();
        this.shape = new Path.Circle({
            center: position,
            radius: 15,
            fillColor: 'red'
        });
    }

    update(delta) {
        this.shape.position.y += 50 * delta;
    }
}

Создание с интервалом:

let enemies = [];

setInterval(() => {
    let x = Math.random() * view.size.width;
    let enemy = new Enemy(new Point(x, 0));
    enemies.push(enemy);
}, 1000);

Снаряды и стрельба

class Bullet extends GameObject {
    constructor(position) {
        super();
        this.shape = new Path.Circle({
            center: position,
            radius: 5,
            fillColor: 'yellow'
        });
        this.velocity = new Point(0, -300);
    }

    update(delta) {
        this.shape.position += this.velocity * delta;
    }
}

Создание при клике:

let bullets = [];

function onMouseDown(event) {
    let bullet = new Bullet(player.shape.position.clone());
    bullets.push(bullet);
}

Удаление объектов

Важно удалять объекты, чтобы избежать утечек памяти.

bullets = bullets.filter(bullet => {
    if (bullet.shape.position.y < 0) {
        bullet.remove();
        return false;
    }
    return true;
});

Анимации и эффекты

Paper.js позволяет создавать плавные визуальные эффекты.

Пульсация

function onFrame(event) {
    player.scale(1 + Math.sin(event.time * 5) * 0.01);
}

Взрыв

function createExplosion(position) {
    let circle = new Path.Circle({
        center: position,
        radius: 10,
        fillColor: 'orange'
    });

    let life = 0;

    function animate(event) {
        life += event.delta;
        circle.scale(1.1);
        circle.opacity -= 0.02;

        if (circle.opacity <= 0) {
            circle.remove();
            view.off('frame', animate);
        }
    }

    view.on('frame', animate);
}

Слои и группировка

Для сложных сцен используется Layer и Group.

let playerLayer = new Layer();
let enemyLayer = new Layer();

let group = new Group([player.shape]);

Преимущества:

  • логическое разделение
  • управление порядком отрисовки
  • массовые операции

Камера и смещение сцены

Paper.js не имеет встроенной камеры, но можно имитировать её через смещение view.center.

function onFrame() {
    view.center = player.shape.position;
}

Оптимизация

Минимизация объектов

  • избегать частого создания Path
  • переиспользовать элементы

Ограничение проверок

  • проверять столкновения только рядом расположенных объектов
  • использовать простые формы (окружности вместо сложных путей)

Частота обновлений

  • уменьшать нагрузку в onFrame
  • использовать event.delta

Пример простой игры: уклонение

let player = new Player(view.center);
let enemies = [];

function onFrame(event) {
    player.update(event.delta);

    enemies.forEach(enemy => enemy.update(event.delta));

    enemies.forEach(enemy => {
        if (player.shape.intersects(enemy.shape)) {
            console.log("Game Over");
        }
    });
}

Расширение функциональности

Счёт

let score = 0;

function increaseScore() {
    score += 10;
}

Интерфейс

let text = new PointText({
    point: [10, 20],
    content: 'Score: 0',
    fillColor: 'black'
});

Обновление:

text.content = 'Score: ' + score;

Звуки и взаимодействие с браузером

Paper.js не работает со звуком напрямую, используется стандартный API:

let sound = new Audio('shoot.mp3');
sound.play();

Архитектурные подходы

ECS (Entity Component System)

  • Entity — объект
  • Component — данные (позиция, скорость)
  • System — логика

MVC-подобная структура

  • Model — состояние игры
  • View — Paper.js объекты
  • Controller — обработка ввода

Ограничения Paper.js для игр

  • Нет физического движка
  • Ограниченная производительность при большом количестве объектов
  • Отсутствие спрайтов (в классическом понимании)

Однако для:

  • геометрических игр
  • обучающих проектов
  • визуальных симуляций

библиотека остаётся мощным и удобным инструментом.