Создание игры с помощью ECS — часть 1

В этой серии уроков я постараюсь познакомить вас с основными концепциями ECS, создав игру с помощью ECS Game Framework.

Что такое ECS?

ECS или Entity-Component-System — это архитектура разработки игр, которая используется для разработки высокоэффективных игр. Она состоит из 3 элементов —

1. Сущность

относится к игровому объекту/сущности, состоящей из различных компонентов. Системы «запрашивают» эти сущности для выполнения различных функций. Они хранят «данные», которые используются системами для определения текущего состояния сущности и выполнения соответствующих операций.

2. Компонент

Компонент — это не что иное, как данные или свойство. Сущность может иметь несколько компонентов, таких как здоровье, положение, форма и т.д.

3. Система

Система — это место, где все начинает двигаться. Вы можете думать о них как о функциях, которые вызываются при каждом тике/обновлении. Они выполняют специализированные функции, такие как рендеринг сущностей, физика, столкновения и т.д. Они «запрашивают» интересующие их компоненты (те, которые имеют определенный набор компонентов, необходимых системе) и обновляют игру.

Запрос

Другим важным компонентом ECS является запрос. Его можно представить как набор/массив имен компонентов. Системы используют эти запросы, чтобы получить список сущностей, которые имеют эти компоненты. Это похоже на то, как вы запрашиваете элементы DOM с помощью CSS-запросов.

Начало

Мы будем использовать Square — ECS Framework, написанный на Javascript.

Структура проекта

/game
 - queries.js
 - components.js
 - systems.js
 - main.js
Вход в полноэкранный режим Выход из полноэкранного режима

queries.js
Он будет содержать запросы, которые будут использовать наши системы.

export const Renderable = ['@position', '@size', '@shape'];
Войти в полноэкранный режим Выйти из полноэкранного режима

components.js

export class PositionComponent {
    constructor(x = 0, y = 0) {
        this.x = x;
        this.y = y;
    }
}

export class SizeComponent {
    constructor(width = 0, height = 0) {
        this.width = width;
        this.height = height;
    }
}

export class ShapeComponent {
    static RECTANGLE = 'rectangle';
}
Вход в полноэкранный режим Выход из полноэкранного режима

systems.js

import { ShapeComponent } from './components.js';

export function RenderingSystem(app) {
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');

    app.on('init', () => document.body.appendChild(canvas));

    app.on('update', () => {
        context.clearRect(0, 0, canvas.width, canvas.height);

        app.emit('render', canvas, context);
    });
}

export function ShapeRenderer(app) {
    app.on('render', (_canvas, ctx) => {
        const entities = app.query('@shape');

        entities.forEach(entity => {
            if(entity.shape === ShapeComponent.RECTANGLE) {
                ctx.fillRect(
                    entity.position.x,
                    entity.position.y,
                    entity.size.width,
                    entity.size.height
                );
            }
        });
    });
}
Войти в полноэкранный режим Выход из полноэкранного режима

main.js

// importing the library
import { Application } from 'https://unpkg.com/square-ecs@latest';

// importing various parts of our game
import { RenderingSystem, ShapeRenderer } from './systems.js';
import { PositionComponent, SizeComponent, ShapeComponent } from './components.js';

// creating an instance of the game
const app = new Application({
    // shared data that our game will use, 
    // it can contain any kind of data
    data: {}, 
    // systems of our game
    systems: [
        RenderingSystem,
        ShapeRenderer
    ]
});

app.on('init', () => {
    const entity = app.entityPool.getEntity(); // borrow an entity from the entity pool

    // attach various components
    entity
        .attach('shape', ShapeComponent.RECTANGLE)
        .attach('position', new PositionComponent(100, 100))
        .attach('size', new SizeComponent(50, 50));

    // add it to the world
    app.add(entity);
});

// start the game
app.start();
Войти в полноэкранный режим Выход из полноэкранного режима

Вывод

Да! Мы успешно отрисовали квадрат.

В следующей статье все начнет двигаться.

Джай Шри Рам!

Оцените статью
devanswers.ru
Добавить комментарий