Я использую фреймворк entity-component-system (ECS) для создания 2D-космического шутера. Мои компоненты только для данных, мои системы обрабатывают всю логику, а сущности - это в основном идентификаторы.Component-Entity-System - Обработка ввода
Вот некоторые из моих компонентов:
struct LaserCannon {
bool shooting;
};
struct Thrusters {
bool up, left, right, down;
float acceleration;
};
struct Movement {
sf::Vector2f velocity, acceleration;
float maxSpeed;
};
Я использую довольно прохладно системы ввода, построенный одним из разработчиков SFML, называется thor. Он сопоставляет входы, такие как sf::Keyboard::W
, моим собственным событиям более высокого уровня, таким как Control::UP
. Я могу проверить, было ли событие возбуждено через inputMap->isActive(Control::UP)
, а затем ответить соответствующим образом.
Это приносит исходный вход 'вверх' один уровень абстракции - но мне нужно идти немного дальше. Мне нужны сущности, чтобы иметь возможность реагировать на такие вещи, как Control::UP
, и активировать результирующие объекты уровня сущности, такие как активация движителей корабля игрока. Я еще этого не делал, потому что я не знаю, как подойти к нему с помощью компонентов и систем!
Я пришел с одним из возможных решений: я мог бы создать новый компонент под названием PlayerShipController, который имеет булевы, представляющие все вещи, которые я хочу контролировать. Тогда я мог бы иметь систему, которая реагирует на ввод и устанавливает эти булевы соответственно так:
struct PlayerShipController {
bool up, left, right, down, shoot;
};
// Input handling system
struct InputSystem : public entityx::System<InputSystem> {
InputSystem(std::shared_ptr<thor::ActionMap<Event>> inputMap) : inputMap(inputMap) {}
void update(entityx::EntityManager &es, entityx::EventManager &events, entityx::TimeDelta dt) override {
// Iterate across all entities with a PlayerShipController
es.each<cm::PlayerShipController>([dt, this](entityx::Entity entity, cm::PlayerShipController &controller) {
if (inputMap->isActive(Event::LEFT_BUTTON)) {
controller.left = true;
}
if (inputMap->isActive(Event::UP_BUTTON)) {
controller.up = true;
}
if (inputMap->isActive(Event::RIGHT_BUTTON)) {
controller.right = true;
}
if (inputMap->isActive(Event::DOWN_BUTTON)) {
controller.down = true;
}
});
};
std::shared_ptr<thor::ActionMap<Event>> inputMap;
};
Но мне интересно, если есть другие, возможно, более эффективные способы для достижения этой цели. То, что мне действительно не нравится в моем решении, заключается в том, что он требует, чтобы я написал систему, которая очень специфична для игры (ей нужен PlayerShipController - waaay слишком конкретный, я думаю!)! Я думаю, что было бы намного опрятно иметь какой-то средний слой. Для меня решение ниже намного круче!
- прессы пользователя «ш»
- «ш» переводится в
Control::UP
по Thor InputMap Control::UP
каким-то образом отображается на события на уровне образований, какShip::THRUST_UP
, ибо только корабли управляются пользователем- события Entity уровня, такие как
Ship::THRUST_UP
переводятся в действия как активацияThrusters::up
булево
Что такое especi что я в идеале мог бы использовать одни и те же события на уровне сущности для управления многими другими вещами, такими как взрыва астероидов, или вражеское судно с использованием подруливающих устройств. Я просто не понимаю, как его реализовать, или интегрировать в ECS. Как я могу это сделать?