Systems
ΠΡΠ½ΠΎΠ²Ρ ΡΠΈΡΡΠ΅ΠΌ
Π‘ΠΈΡΡΠ΅ΠΌΡ Π² Wudgine β ΡΡΠΎ ΠΊΠ»Π°ΡΡΡ, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°ΡΡ Π΄Π°Π½Π½ΡΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠΎΠ² ΠΈ ΡΠ΅Π°Π»ΠΈΠ·ΡΡΡ ΠΈΠ³ΡΠΎΠ²ΡΡ Π»ΠΎΠ³ΠΈΠΊΡ. ΠΠ½ΠΈ ΡΠ°Π±ΠΎΡΠ°ΡΡ Ρ Π³ΡΡΠΏΠΏΠ°ΠΌΠΈ ΡΡΡΠ½ΠΎΡΡΠ΅ΠΉ, ΠΈΠΌΠ΅ΡΡΠΈΡ ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½Π½ΡΠ΅ Π½Π°Π±ΠΎΡΡ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠΎΠ².
Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΡΠΈΡΡΠ΅ΠΌΡ
// ΠΠ°Π·ΠΎΠ²ΡΠΉ ΠΊΠ»Π°ΡΡ ΡΠΈΡΡΠ΅ΠΌΡ
class ISystem {
public:
virtual ~ISystem() = default;
virtual void initialize() {}
virtual void update(float deltaTime) {}
virtual void lateUpdate(float deltaTime) {}
virtual void fixedUpdate(float fixedDeltaTime) {}
virtual void cleanup() {}
};
// ΠΡΠΈΠΌΠ΅Ρ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΎΠΉ ΡΠΈΡΡΠ΅ΠΌΡ
class MovementSystem : public ISystem {
private:
World* m_world;
EntityQuery m_query;
public:
MovementSystem(World* world) : m_world(world) {
// Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Π·Π°ΠΏΡΠΎΡΠ° Π΄Π»Ρ ΡΡΡΠ½ΠΎΡΡΠ΅ΠΉ Ρ Π½ΡΠΆΠ½ΡΠΌΠΈ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ°ΠΌΠΈ
m_query = m_world->createQuery()
.with<TransformComponent>()
.with<VelocityComponent>()
.build();
}
void update(float deltaTime) override {
// ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° Π²ΡΠ΅Ρ
ΡΡΡΠ½ΠΎΡΡΠ΅ΠΉ, ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΡΡΠΈΡ
Π·Π°ΠΏΡΠΎΡΡ
m_query.forEach([deltaTime](Entity entity,
TransformComponent& transform,
VelocityComponent& velocity) {
// ΠΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ ΠΏΠΎΠ·ΠΈΡΠΈΠΈ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ ΡΠΊΠΎΡΠΎΡΡΠΈ
transform.position += velocity.value * deltaTime;
});
}
};
// Π Π΅Π³ΠΈΡΡΡΠ°ΡΠΈΡ ΡΠΈΡΡΠ΅ΠΌΡ Π² ΠΌΠΈΡΠ΅
world.addSystem<MovementSystem>();
ΠΠΈΠ·Π½Π΅Π½Π½ΡΠΉ ΡΠΈΠΊΠ» ΡΠΈΡΡΠ΅ΠΌΡ
Π‘ΠΈΡΡΠ΅ΠΌΡ ΠΈΠΌΠ΅ΡΡ ΡΠ»Π΅Π΄ΡΡΡΠΈΠ΅ ΠΌΠ΅ΡΠΎΠ΄Ρ ΠΆΠΈΠ·Π½Π΅Π½Π½ΠΎΠ³ΠΎ ΡΠΈΠΊΠ»Π°:
- initialize(): ΠΡΠ·ΡΠ²Π°Π΅ΡΡΡ ΠΎΠ΄ΠΈΠ½ ΡΠ°Π· ΠΏΡΠΈ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΠΈ ΡΠΈΡΡΠ΅ΠΌΡ
- update(deltaTime): ΠΡΠ·ΡΠ²Π°Π΅ΡΡΡ ΠΊΠ°ΠΆΠ΄ΡΠΉ ΠΊΠ°Π΄Ρ Π΄Π»Ρ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ Π»ΠΎΠ³ΠΈΠΊΠΈ
- lateUpdate(deltaTime): ΠΡΠ·ΡΠ²Π°Π΅ΡΡΡ ΠΏΠΎΡΠ»Π΅ update Π²ΡΠ΅Ρ ΡΠΈΡΡΠ΅ΠΌ
- fixedUpdate(fixedDeltaTime): ΠΡΠ·ΡΠ²Π°Π΅ΡΡΡ Ρ ΡΠΈΠΊΡΠΈΡΠΎΠ²Π°Π½Π½ΡΠΌ Π²ΡΠ΅ΠΌΠ΅Π½Π½ΡΠΌ ΡΠ°Π³ΠΎΠΌ Π΄Π»Ρ ΡΠΈΠ·ΠΈΠΊΠΈ
- cleanup(): ΠΡΠ·ΡΠ²Π°Π΅ΡΡΡ ΠΏΡΠΈ ΡΠ½ΠΈΡΡΠΎΠΆΠ΅Π½ΠΈΠΈ ΡΠΈΡΡΠ΅ΠΌΡ
Π’ΠΈΠΏΡ ΡΠΈΡΡΠ΅ΠΌ
Π‘ΠΈΡΡΠ΅ΠΌΡ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ (Update Systems)
ΠΡΠΏΠΎΠ»Π½ΡΡΡΡΡ ΠΊΠ°ΠΆΠ΄ΡΠΉ ΠΊΠ°Π΄Ρ ΠΈ ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°ΡΡ ΠΎΡΠ½ΠΎΠ²Π½ΡΡ ΠΈΠ³ΡΠΎΠ²ΡΡ Π»ΠΎΠ³ΠΈΠΊΡ:
class AISystem : public ISystem {
private:
World* m_world;
EntityQuery m_query;
public:
AISystem(World* world) : m_world(world) {
m_query = m_world->createQuery()
.with<AIComponent>()
.with<TransformComponent>()
.build();
}
void update(float deltaTime) override {
m_query.forEach([this, deltaTime](Entity entity,
AIComponent& ai,
TransformComponent& transform) {
// ΠΠΎΠΈΡΠΊ Π±Π»ΠΈΠΆΠ°ΠΉΡΠ΅ΠΉ ΡΠ΅Π»ΠΈ
Entity target = findNearestTarget(entity, transform.position);
if (target.isValid()) {
ai.target = target;
// ΠΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΡ ΠΠ
updateBehavior(entity, ai, transform, target, deltaTime);
}
});
}
private:
Entity findNearestTarget(Entity self, const Vector3& position) {
// Π Π΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ ΠΏΠΎΠΈΡΠΊΠ° Π±Π»ΠΈΠΆΠ°ΠΉΡΠ΅ΠΉ ΡΠ΅Π»ΠΈ
// ...
return Entity();
}
void updateBehavior(Entity entity, AIComponent& ai,
TransformComponent& transform,
Entity target, float deltaTime) {
// Π Π΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΡ ΠΠ
// ...
}
};
Π‘ΠΈΡΡΠ΅ΠΌΡ ΡΠΈΠ·ΠΈΠΊΠΈ (Physics Systems)
ΠΡΠΏΠΎΠ»Π½ΡΡΡΡΡ Ρ ΡΠΈΠΊΡΠΈΡΠΎΠ²Π°Π½Π½ΡΠΌ Π²ΡΠ΅ΠΌΠ΅Π½Π½ΡΠΌ ΡΠ°Π³ΠΎΠΌ Π΄Π»Ρ ΡΡΠ°Π±ΠΈΠ»ΡΠ½ΠΎΠΉ ΡΠΈΠΌΡΠ»ΡΡΠΈΠΈ ΡΠΈΠ·ΠΈΠΊΠΈ:
class PhysicsSystem : public ISystem {
private:
World* m_world;
EntityQuery m_query;
PhysicsWorld m_physicsWorld;
public:
PhysicsSystem(World* world) : m_world(world) {
m_query = m_world->createQuery()
.with<RigidbodyComponent>()
.with<TransformComponent>()
.build();
// ΠΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ ΡΠΈΠ·ΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ ΠΌΠΈΡΠ°
m_physicsWorld.initialize();
}
void fixedUpdate(float fixedDeltaTime) override {
// Π‘ΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ ΡΡΠ°Π½ΡΡΠΎΡΠΌΠ°ΡΠΈΠΉ Ρ ΡΠΈΠ·ΠΈΡΠ΅ΡΠΊΠΈΠΌ ΠΌΠΈΡΠΎΠΌ
syncTransformsToPhysics();
// ΠΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ ΡΠ°Π³Π° ΡΠΈΠ·ΠΈΡΠ΅ΡΠΊΠΎΠΉ ΡΠΈΠΌΡΠ»ΡΡΠΈΠΈ
m_physicsWorld.step(fixedDeltaTime);
// Π‘ΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠ² ΡΠΈΠ·ΠΈΠΊΠΈ Ρ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ°ΠΌΠΈ ΡΡΠ°Π½ΡΡΠΎΡΠΌΠ°ΡΠΈΠΈ
syncPhysicsToTransforms();
// ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΡΡΠΎΠ»ΠΊΠ½ΠΎΠ²Π΅Π½ΠΈΠΉ
processCollisions();
}
private:
void syncTransformsToPhysics() {
// Π‘ΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ ΠΏΠΎΠ·ΠΈΡΠΈΠΉ ΠΈ ΠΏΠΎΠ²ΠΎΡΠΎΡΠΎΠ² ΠΈΠ· TransformComponent Π² ΡΠΈΠ·ΠΈΡΠ΅ΡΠΊΠΈΠΉ ΠΌΠΈΡ
// ...
}
void syncPhysicsToTransforms() {
// Π‘ΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠ² ΡΠΈΠ·ΠΈΡΠ΅ΡΠΊΠΎΠΉ ΡΠΈΠΌΡΠ»ΡΡΠΈΠΈ ΠΎΠ±ΡΠ°ΡΠ½ΠΎ Π² ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΡ
// ...
}
void processCollisions() {
// ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΏΡΠΎΠΈΠ·ΠΎΡΠ΅Π΄ΡΠΈΡ
ΡΡΠΎΠ»ΠΊΠ½ΠΎΠ²Π΅Π½ΠΈΠΉ
// ...
}
};
Π‘ΠΈΡΡΠ΅ΠΌΡ ΡΠ΅Π½Π΄Π΅ΡΠΈΠ½Π³Π° (Rendering Systems)
ΠΡΠ²Π΅ΡΠ°ΡΡ Π·Π° ΡΠ±ΠΎΡ ΠΈ ΠΎΡΠΏΡΠ°Π²ΠΊΡ Π΄Π°Π½Π½ΡΡ Π΄Π»Ρ ΡΠ΅Π½Π΄Π΅ΡΠΈΠ½Π³Π°:
class RenderSystem : public ISystem {
private:
World* m_world;
EntityQuery m_meshQuery;
EntityQuery m_cameraQuery;
RenderPipeline m_renderPipeline;
public:
RenderSystem(World* world) : m_world(world) {
m_meshQuery = m_world->createQuery()
.with<MeshRendererComponent>()
.with<TransformComponent>()
.build();
m_cameraQuery = m_world->createQuery()
.with<CameraComponent>()
.with<TransformComponent>()
.build();
// ΠΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ ΠΊΠΎΠ½Π²Π΅ΠΉΠ΅ΡΠ° ΡΠ΅Π½Π΄Π΅ΡΠΈΠ½Π³Π°
m_renderPipeline.initialize();
}
void lateUpdate(float deltaTime) override {
// ΠΠΎΠΈΡΠΊ Π°ΠΊΡΠΈΠ²Π½ΠΎΠΉ ΠΊΠ°ΠΌΠ΅ΡΡ
Entity mainCamera = findMainCamera();
if (!mainCamera.isValid()) return;
// Π‘Π±ΠΎΡ Π΄Π°Π½Π½ΡΡ
Π΄Π»Ρ ΡΠ΅Π½Π΄Π΅ΡΠΈΠ½Π³Π°
std::vector<RenderObject> renderObjects;
collectRenderObjects(renderObjects);
// ΠΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ ΡΠ΅Π½Π΄Π΅ΡΠΈΠ½Π³Π°
m_renderPipeline.render(mainCamera, renderObjects);
}
private:
Entity findMainCamera() {
// ΠΠΎΠΈΡΠΊ ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠΉ ΠΊΠ°ΠΌΠ΅ΡΡ
Entity result;
int highestPriority = -1;
m_cameraQuery.forEach([&](Entity entity, CameraComponent& camera, TransformComponent& transform) {
if (camera.isActive() && camera.getPriority() > highestPriority) {
highestPriority = camera.getPriority();
result = entity;
}
});
return result;
}
void collectRenderObjects(std::vector<RenderObject>& renderObjects) {
// Π‘Π±ΠΎΡ ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ² Π΄Π»Ρ ΡΠ΅Π½Π΄Π΅ΡΠΈΠ½Π³Π°
m_meshQuery.forEach([&](Entity entity, MeshRendererComponent& renderer, TransformComponent& transform) {
if (renderer.isVisible()) {
RenderObject obj;
obj.entity = entity;
obj.mesh = renderer.getMesh();
obj.material = renderer.getMaterial();
obj.transform = transform.getWorldMatrix();
obj.layer = renderer.getLayer();
renderObjects.push_back(obj);
}
});
}
};
Π‘ΠΈΡΡΠ΅ΠΌΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΎΠ³ΠΎ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡΠ° (UI Systems)
ΠΠ±ΡΠ°Π±Π°ΡΡΠ²Π°ΡΡ ΡΠ»Π΅ΠΌΠ΅Π½ΡΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΎΠ³ΠΎ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡΠ°:
class UISystem : public ISystem {
private:
World* m_world;
EntityQuery m_uiQuery;
UIManager m_uiManager;
public:
UISystem(World* world) : m_world(world) {
m_uiQuery = m_world->createQuery()
.with<UIElementComponent>()
.with<TransformComponent>()
.build();
// ΠΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ ΠΌΠ΅Π½Π΅Π΄ΠΆΠ΅ΡΠ° UI
m_uiManager.initialize();
}
void update(float deltaTime) override {
// ΠΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ UI ΡΠ»Π΅ΠΌΠ΅Π½ΡΠΎΠ²
m_uiQuery.forEach([&](Entity entity, UIElementComponent& ui, TransformComponent& transform) {
// ΠΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ ΠΏΠΎΠ·ΠΈΡΠΈΠΈ ΠΈ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ UI ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ°
ui.update(deltaTime);
});
// ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° Π²Π²ΠΎΠ΄Π° Π΄Π»Ρ UI
processUIInput();
}
void lateUpdate(float deltaTime) override {
// Π Π΅Π½Π΄Π΅ΡΠΈΠ½Π³ UI ΠΏΠΎΡΠ»Π΅ Π²ΡΠ΅ΠΉ ΠΎΡΡΠ°Π»ΡΠ½ΠΎΠΉ ΡΡΠ΅Π½Ρ
m_uiManager.render();
}
private:
void processUIInput() {
// ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° Π²Π²ΠΎΠ΄Π° Π΄Π»Ρ UI ΡΠ»Π΅ΠΌΠ΅Π½ΡΠΎΠ²
// ...
}
};
Π‘ΠΈΡΡΠ΅ΠΌΡ Π²Π²ΠΎΠ΄Π° (Input Systems)
ΠΠ±ΡΠ°Π±Π°ΡΡΠ²Π°ΡΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΈΠΉ Π²Π²ΠΎΠ΄:
class InputSystem : public ISystem {
private:
World* m_world;
InputManager m_inputManager;
EventBus* m_eventBus;
public:
InputSystem(World* world, EventBus* eventBus)
: m_world(world), m_eventBus(eventBus) {
// ΠΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ ΠΌΠ΅Π½Π΅Π΄ΠΆΠ΅ΡΠ° Π²Π²ΠΎΠ΄Π°
m_inputManager.initialize();
// Π Π΅Π³ΠΈΡΡΡΠ°ΡΠΈΡ ΠΎΠ±ΡΠ°Π±ΠΎΡΡΠΈΠΊΠΎΠ² Π²Π²ΠΎΠ΄Π°
registerInputHandlers();
}
void update(float deltaTime) override {
// ΠΠΏΡΠΎΡ ΡΡΡΡΠΎΠΉΡΡΠ² Π²Π²ΠΎΠ΄Π°
m_inputManager.pollDevices();
// ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° Π½Π°ΠΆΠ°ΡΠΈΠΉ ΠΊΠ»Π°Π²ΠΈΡ
processKeyboardInput();
// ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° Π²Π²ΠΎΠ΄Π° ΠΌΡΡΠΈ
processMouseInput();
// ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° Π²Π²ΠΎΠ΄Π° Π³Π΅ΠΉΠΌΠΏΠ°Π΄Π°
processGamepadInput();
}
private:
void registerInputHandlers() {
// Π Π΅Π³ΠΈΡΡΡΠ°ΡΠΈΡ ΠΎΠ±ΡΠ°Π±ΠΎΡΡΠΈΠΊΠΎΠ² Π²Π²ΠΎΠ΄Π°
m_inputManager.registerKeyCallback(KeyCode::W, [this](bool pressed) {
if (pressed) {
m_eventBus->publish<MoveEvent>(Vector3(0, 0, 1));
}
});
// ΠΡΡΠ³ΠΈΠ΅ ΠΎΠ±ΡΠ°Π±ΠΎΡΡΠΈΠΊΠΈ...
}
void processKeyboardInput() {
// ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° Π²Π²ΠΎΠ΄Π° Ρ ΠΊΠ»Π°Π²ΠΈΠ°ΡΡΡΡ
// ...
}
void processMouseInput() {
// ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° Π²Π²ΠΎΠ΄Π° ΠΌΡΡΠΈ
// ...
}
void processGamepadInput() {
// ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° Π²Π²ΠΎΠ΄Π° Π³Π΅ΠΉΠΌΠΏΠ°Π΄Π°
// ...
}
};
ΠΡΠΈΠΎΡΠΈΡΠ΅ΡΡ ΠΈ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ ΡΠΈΡΡΠ΅ΠΌ
Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° ΠΏΡΠΈΠΎΡΠΈΡΠ΅ΡΠΎΠ²
// Π Π΅Π³ΠΈΡΡΡΠ°ΡΠΈΡ ΡΠΈΡΡΠ΅ΠΌ Ρ ΠΏΡΠΈΠΎΡΠΈΡΠ΅ΡΠ°ΠΌΠΈ
world.addSystem<InputSystem>(100); // ΠΡΡΠΎΠΊΠΈΠΉ ΠΏΡΠΈΠΎΡΠΈΡΠ΅Ρ, Π²ΡΠΏΠΎΠ»Π½ΡΠ΅ΡΡΡ ΠΏΠ΅ΡΠ²ΠΎΠΉ
world.addSystem<AISystem>(50); // Π‘ΡΠ΅Π΄Π½ΠΈΠΉ ΠΏΡΠΈΠΎΡΠΈΡΠ΅Ρ
world.addSystem<MovementSystem>(30); // ΠΠΈΠ·ΠΊΠΈΠΉ ΠΏΡΠΈΠΎΡΠΈΡΠ΅Ρ
world.addSystem<PhysicsSystem>(20); // ΠΡΠ΅Π½Ρ Π½ΠΈΠ·ΠΊΠΈΠΉ ΠΏΡΠΈΠΎΡΠΈΡΠ΅Ρ
world.addSystem<RenderSystem>(-100); // ΠΡΡΠΈΡΠ°ΡΠ΅Π»ΡΠ½ΡΠΉ ΠΏΡΠΈΠΎΡΠΈΡΠ΅Ρ, Π²ΡΠΏΠΎΠ»Π½ΡΠ΅ΡΡΡ ΠΏΠΎΡΠ»Π΅Π΄Π½Π΅ΠΉ
ΠΠ°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ ΠΌΠ΅ΠΆΠ΄Ρ ΡΠΈΡΡΠ΅ΠΌΠ°ΠΌΠΈ
// ΠΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ ΡΠΈΡΡΠ΅ΠΌΡ Ρ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΡΠΌΠΈ
class CombatSystem : public ISystem {
private:
World* m_world;
HealthSystem* m_healthSystem;
AnimationSystem* m_animationSystem;
public:
CombatSystem(World* world) : m_world(world) {
// ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π·Π°Π²ΠΈΡΠΈΠΌΡΡ
ΡΠΈΡΡΠ΅ΠΌ
m_healthSystem = m_world->getSystem<HealthSystem>();
m_animationSystem = m_world->getSystem<AnimationSystem>();
if (!m_healthSystem || !m_animationSystem) {
Debug::logError("CombatSystem requires HealthSystem and AnimationSystem");
}
}
void update(float deltaTime) override {
// ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ Π΄ΡΡΠ³ΠΈΡ
ΡΠΈΡΡΠ΅ΠΌ
// ...
}
};
// Π Π΅Π³ΠΈΡΡΡΠ°ΡΠΈΡ ΡΠΈΡΡΠ΅ΠΌ Π² ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎΠΌ ΠΏΠΎΡΡΠ΄ΠΊΠ΅
world.addSystem<HealthSystem>();
world.addSystem<AnimationSystem>();
world.addSystem<CombatSystem>();
ΠΠΏΡΠΈΠΌΠΈΠ·Π°ΡΠΈΡ ΡΠΈΡΡΠ΅ΠΌ
ΠΠ°ΡΠ°Π»Π»Π΅Π»ΡΠ½ΠΎΠ΅ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅
class ParticleSystem : public ISystem {
private:
World* m_world;
EntityQuery m_query;
ThreadPool m_threadPool;
public:
ParticleSystem(World* world) : m_world(world) {
m_query = m_world->createQuery()
.with<ParticleComponent>()
.with<TransformComponent>()
.build();
// ΠΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ ΠΏΡΠ»Π° ΠΏΠΎΡΠΎΠΊΠΎΠ²
m_threadPool.initialize(std::thread::hardware_concurrency());
}
void update(float deltaTime) override {
// ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π²ΡΠ΅Ρ
ΡΡΡΠ½ΠΎΡΡΠ΅ΠΉ Ρ ΡΠ°ΡΡΠΈΡΠ°ΠΌΠΈ
auto entities = m_query.getEntities();
size_t entityCount = entities.size();
// ΠΠ°ΡΠ°Π»Π»Π΅Π»ΡΠ½Π°Ρ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΡΠ°ΡΡΠΈΡ
m_threadPool.parallelFor(0, entityCount, [&](size_t i) {
Entity entity = entities[i];
auto& particles = entity.getComponent<ParticleComponent>();
auto& transform = entity.getComponent<TransformComponent>();
// ΠΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ ΡΠ°ΡΡΠΈΡ
updateParticles(particles, transform, deltaTime);
});
}
private:
void updateParticles(ParticleComponent& particles,
const TransformComponent& transform,
float deltaTime) {
// ΠΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ ΡΠ°ΡΡΠΈΡ
// ...
}
};
ΠΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π΄Π°Π½Π½ΡΡ
class PathfindingSystem : public ISystem {
private:
World* m_world;
EntityQuery m_query;
NavigationMesh m_navMesh;
std::unordered_map<Entity, Path> m_pathCache;
float m_cacheTimeout = 1.0f;
public:
PathfindingSystem(World* world) : m_world(world) {
m_query = m_world->createQuery()
.with<AIComponent>()
.with<TransformComponent>()
.build();
// ΠΠ°Π³ΡΡΠ·ΠΊΠ° Π½Π°Π²ΠΈΠ³Π°ΡΠΈΠΎΠ½Π½ΠΎΠΉ ΡΠ΅ΡΠΊΠΈ
m_navMesh.load("assets/navmesh/level1.navmesh");
}
void update(float deltaTime) override {
// ΠΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ ΠΊΡΡΠ° ΠΏΡΡΠ΅ΠΉ
updatePathCache(deltaTime);
// ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° Π·Π°ΠΏΡΠΎΡΠΎΠ² Π½Π° ΠΏΠΎΠΈΡΠΊ ΠΏΡΡΠΈ
m_query.forEach([this](Entity entity, AIComponent& ai, TransformComponent& transform) {
if (ai.needsPath && ai.target.isValid()) {
// ΠΠΎΠΈΡΠΊ ΠΏΡΡΠΈ ΠΊ ΡΠ΅Π»ΠΈ
Vector3 start = transform.position;
Vector3 end = ai.target.getComponent<TransformComponent>().position;
// ΠΡΠΎΠ²Π΅ΡΠΊΠ° ΠΊΡΡΠ°
Path path;
if (tryGetCachedPath(entity, start, end, path)) {
ai.currentPath = path;
} else {
// ΠΡΡΠΈΡΠ»Π΅Π½ΠΈΠ΅ Π½ΠΎΠ²ΠΎΠ³ΠΎ ΠΏΡΡΠΈ
path = m_navMesh.findPath(start, end);
ai.currentPath = path;
// Π‘ΠΎΡ
ΡΠ°Π½Π΅Π½ΠΈΠ΅ Π² ΠΊΡΡ
cachePath(entity, start, end, path);
}
ai.needsPath = false;
}
});
}
private:
void updatePathCache(float deltaTime) {
// ΠΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ ΠΆΠΈΠ·Π½ΠΈ ΠΊΡΡΠΈΡΠΎΠ²Π°Π½Π½ΡΡ
ΠΏΡΡΠ΅ΠΉ
// ...
}
bool tryGetCachedPath(Entity entity, const Vector3& start,
const Vector3& end, Path& outPath) {
// ΠΠΎΠΏΡΡΠΊΠ° ΠΏΠΎΠ»ΡΡΠΈΡΡ ΠΏΡΡΡ ΠΈΠ· ΠΊΡΡΠ°
// ...
return false;
}
void cachePath(Entity entity, const Vector3& start,
const Vector3& end, const Path& path) {
// Π‘ΠΎΡ
ΡΠ°Π½Π΅Π½ΠΈΠ΅ ΠΏΡΡΠΈ Π² ΠΊΡΡ
// ...
}
};
Π‘ΠΈΡΡΠ΅ΠΌΠ½ΡΠ΅ Π³ΡΡΠΏΠΏΡ
// ΠΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ Π³ΡΡΠΏΠΏΡ ΡΠΈΡΡΠ΅ΠΌ
class PhysicsSystemGroup : public SystemGroup {
public:
PhysicsSystemGroup(World* world) : SystemGroup(world) {
// ΠΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΡΠΈΡΡΠ΅ΠΌ Π² Π³ΡΡΠΏΠΏΡ
addSystem<CollisionDetectionSystem>();
addSystem<RigidbodySystem>();
addSystem<ConstraintSystem>();
addSystem<PhysicsDebugSystem>();
}
};
// Π Π΅Π³ΠΈΡΡΡΠ°ΡΠΈΡ Π³ΡΡΠΏΠΏΡ ΡΠΈΡΡΠ΅ΠΌ
world.addSystemGroup<PhysicsSystemGroup>();
Π‘ΠΎΠ±ΡΡΠΈΡ ΠΈ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ
// ΠΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ ΡΠΎΠ±ΡΡΠΈΡ
struct CollisionEvent {
Entity entityA;
Entity entityB;
Vector3 contactPoint;
Vector3 contactNormal;
float impulse;
};
// Π‘ΠΈΡΡΠ΅ΠΌΠ°, ΠΏΡΠ±Π»ΠΈΠΊΡΡΡΠ°Ρ ΡΠΎΠ±ΡΡΠΈΡ
class CollisionSystem : public ISystem {
private:
World* m_world;
EventBus* m_eventBus;
PhysicsWorld m_physicsWorld;
public:
CollisionSystem(World* world, EventBus* eventBus)
: m_world(world), m_eventBus(eventBus) {
// ΠΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ ΡΠΈΠ·ΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ ΠΌΠΈΡΠ°
m_physicsWorld.initialize();
// Π Π΅Π³ΠΈΡΡΡΠ°ΡΠΈΡ ΠΎΠ±ΡΠ°ΡΠ½ΠΎΠ³ΠΎ Π²ΡΠ·ΠΎΠ²Π° Π΄Π»Ρ ΡΡΠΎΠ»ΠΊΠ½ΠΎΠ²Π΅Π½ΠΈΠΉ
m_physicsWorld.setCollisionCallback([this](const PhysicsContact& contact) {
// Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΡΠΎΠ±ΡΡΠΈΡ ΡΡΠΎΠ»ΠΊΠ½ΠΎΠ²Π΅Π½ΠΈΡ
CollisionEvent event;
event.entityA = contact.bodyA->getUserData<Entity>();
event.entityB = contact.bodyB->getUserData<Entity>();
event.contactPoint = contact.point;
event.contactNormal = contact.normal;
event.impulse = contact.impulse;
// ΠΡΠ±Π»ΠΈΠΊΠ°ΡΠΈΡ ΡΠΎΠ±ΡΡΠΈΡ
m_eventBus->publish<CollisionEvent>(event);
});
}
void fixedUpdate(float fixedDeltaTime) override {
// ΠΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ ΡΠ°Π³Π° ΡΠΈΠ·ΠΈΡΠ΅ΡΠΊΠΎΠΉ ΡΠΈΠΌΡΠ»ΡΡΠΈΠΈ
m_physicsWorld.step(fixedDeltaTime);
}
};
// Π‘ΠΈΡΡΠ΅ΠΌΠ°, ΠΏΠΎΠ΄ΠΏΠΈΡΡΠ²Π°ΡΡΠ°ΡΡΡ Π½Π° ΡΠΎΠ±ΡΡΠΈΡ
class DamageSystem : public ISystem {
private:
World* m_world;
EventBus* m_eventBus;
EventSubscription m_collisionSubscription;
public:
DamageSystem(World* world, EventBus* eventBus)
: m_world(world), m_eventBus(eventBus) {
// ΠΠΎΠ΄ΠΏΠΈΡΠΊΠ° Π½Π° ΡΠΎΠ±ΡΡΠΈΡ ΡΡΠΎΠ»ΠΊΠ½ΠΎΠ²Π΅Π½ΠΈΠΉ
m_collisionSubscription = m_eventBus->subscribe<CollisionEvent>(
[this](const CollisionEvent& event) {
// ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΡΡΠΎΠ»ΠΊΠ½ΠΎΠ²Π΅Π½ΠΈΡ
processCollision(event);
}
);
}
~DamageSystem() {
// ΠΡΠΏΠΈΡΠΊΠ° ΠΎΡ ΡΠΎΠ±ΡΡΠΈΠΉ
m_collisionSubscription.unsubscribe();
}
private:
void processCollision(const CollisionEvent& event) {
// ΠΡΠΎΠ²Π΅ΡΠΊΠ° Π½Π°Π»ΠΈΡΠΈΡ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠΎΠ² Π·Π΄ΠΎΡΠΎΠ²ΡΡ ΠΈ ΡΡΠΎΠ½Π°
if (event.entityA.hasComponent<HealthComponent>() &&
event.entityB.hasComponent<DamageComponent>()) {
auto& health = event.entityA.getComponent<HealthComponent>();
auto& damage = event.entityB.getComponent<DamageComponent>();
// ΠΡΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ ΡΡΠΎΠ½Π°
float damageAmount = damage.value * event.impulse * 0.1f;
health.currentHealth -= damageAmount;
// ΠΡΠΎΠ²Π΅ΡΠΊΠ° ΡΠΌΠ΅ΡΡΠΈ
if (health.currentHealth <= 0) {
// ΠΡΠ±Π»ΠΈΠΊΠ°ΡΠΈΡ ΡΠΎΠ±ΡΡΠΈΡ ΡΠΌΠ΅ΡΡΠΈ
m_eventBus->publish<DeathEvent>({event.entityA, event.entityB});
}
}
}
};
Π‘Π»Π΅Π΄ΡΡΡΠΈΠ΅ ΡΠ°Π³ΠΈ
Π’Π΅ΠΏΠ΅ΡΡ, ΠΊΠΎΠ³Π΄Π° Π²Ρ ΠΎΠ·Π½Π°ΠΊΠΎΠΌΠΈΠ»ΠΈΡΡ Ρ ΡΠΈΡΡΠ΅ΠΌΠ°ΠΌΠΈ Π² Wudgine, ΡΠ΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΠ΅ΠΌ: