Refactor components

master
John Montagu, the 4th Earl of Sandvich 2026-03-15 21:02:16 -07:00
parent 773329605b
commit 728f84325c
Signed by: sandvich
GPG Key ID: 9A39BE37E602B22D
4 changed files with 112 additions and 61 deletions

View File

@ -112,6 +112,8 @@ struct NullZoneComponent : public Component {
struct ColliderComponent : public Component {
float radius = 8.0f;
bool monitoring = false;
bool monitorable = true;
std::vector<std::function<void(Entity &)>> onCollision;
void Setup() override {}
@ -125,7 +127,35 @@ struct ColliderComponent : public Component {
onCollision.push_back(std::move(callback));
}
void Update(float) override {}
void Update(float) override {
if (!monitoring || !context || !context->entities || entity->queuedForFree) {
return;
}
auto selfTransform = entity->GetComponent<TransformComponent>();
if (!selfTransform) {
return;
}
for (auto &other : *context->entities) {
if (!other || other.get() == entity || other->queuedForFree) {
continue;
}
auto otherCollider = other->GetComponent<ColliderComponent>();
auto otherTransform = other->GetComponent<TransformComponent>();
if (!otherCollider || !otherTransform || !otherCollider->get().monitorable) {
continue;
}
const float dx = selfTransform->get().x - otherTransform->get().x;
const float dy = selfTransform->get().y - otherTransform->get().y;
const float r = radius + otherCollider->get().radius;
if ((dx * dx + dy * dy) <= (r * r)) {
EmitCollision(*other);
}
}
}
void Cleanup() override {}
};
@ -166,22 +196,35 @@ struct ProjectionComponent : public Component {
void Cleanup() override {}
};
struct CollectibleComponent : public Component {
bool collected = false;
struct LifetimeComponent : public Component {
float remaining = 0.0f;
void Setup() override {}
void Update(float dt) override {
remaining -= dt;
if (remaining <= 0.0f) {
entity->QueueFree();
}
}
void Cleanup() override {}
};
struct CollectibleComponent : public Component {
void Setup() override {
auto selfCollider = entity->GetComponent<ColliderComponent>();
if (!selfCollider) {
return;
}
selfCollider->get().monitoring = true;
selfCollider->get().AddCollisionListener([this](Entity &other) {
if (collected || !context || !context->probeEntity || &other != context->probeEntity) {
if (entity->queuedForFree || !context || !context->probeEntity ||
&other != context->probeEntity) {
return;
}
collected = true;
context->EmitCollectiblePicked(*entity);
entity->QueueFree();
if (!context->hudEntity) {
return;
@ -208,6 +251,8 @@ struct HazardComponent : public Component {
return;
}
selfCollider->get().monitoring = true;
selfCollider->get().AddCollisionListener([this](Entity &other) {
if (!context || !context->probeEntity || &other != context->probeEntity) {
return;

View File

@ -19,6 +19,7 @@ std::shared_ptr<Entity> CreateProbe() {
auto &collider = e->AddComponent<ColliderComponent>();
collider.radius = 7.0f;
collider.monitorable = true;
auto &probeState = e->AddComponent<ProbeStateComponent>();
probeState.spawnX = 96.0f;
@ -107,6 +108,32 @@ std::shared_ptr<Entity> CreateAsteroid(float x, float y) {
return e;
}
std::shared_ptr<Entity> CreateDebris(float x, float y, float vx, float vy, float ttl = 1.25f) {
auto e = std::make_shared<Entity>();
auto &t = e->AddComponent<TransformComponent>();
t.x = x;
t.y = y;
auto &physics = e->AddComponent<PhysicsComponent>();
physics.vx = vx;
physics.vy = vy;
physics.speedCap = 260.0f;
auto &lifetime = e->AddComponent<LifetimeComponent>();
lifetime.remaining = ttl;
auto &render = e->AddComponent<RenderComponent>();
render.draw = [e]() {
auto transform = e->GetComponent<TransformComponent>();
if (!transform) {
return;
}
DrawCircleV({transform->get().x, transform->get().y}, 3.0f, Color{245, 196, 104, 220});
};
return e;
}
std::shared_ptr<Entity> CreateNullZone(float x, float width) {
auto e = std::make_shared<Entity>();
auto &t = e->AddComponent<TransformComponent>();

View File

@ -150,7 +150,19 @@ inline void GameplayScene::Enter() {
meterValue = 60.0f;
context.onPlayerDeath = [this]() { wantsDeathScene = true; };
context.AddCollectiblePickedListener([this](Entity &) { ++collectedCount; });
context.AddCollectiblePickedListener([this](Entity &collectible) {
++collectedCount;
auto transform = collectible.GetComponent<TransformComponent>();
if (!transform || !context.entities) {
return;
}
context.entities->push_back(
CreateDebris(transform->get().x - 4.0f, transform->get().y, -22.0f, -36.0f));
context.entities->push_back(
CreateDebris(transform->get().x + 4.0f, transform->get().y, 24.0f, -28.0f));
});
context.AddMeterChangedListener([this](float, float newValue) { meterValue = newValue; });
entities.push_back(CreateWorld());
@ -164,10 +176,29 @@ inline void GameplayScene::Enter() {
if (auto meter = entities.back()->GetComponent<MeterComponent>()) {
meterValue = meter->get().value;
}
context.entities = &entities;
context.worldEntity = (entities.size() > 0 && entities[0]) ? entities[0].get() : nullptr;
context.wellEntity = (entities.size() > 1 && entities[1]) ? entities[1].get() : nullptr;
context.probeEntity = (entities.size() > 2 && entities[2]) ? entities[2].get() : nullptr;
context.hudEntity = (!entities.empty() && entities.back()) ? entities.back().get() : nullptr;
for (auto &entity : entities) {
if (!entity) {
continue;
}
entity->SetContext(&context);
}
}
inline void GameplayScene::Update(float dt) {
UpdateAllSystems(entities, context, dt);
UpdateAllSystems(entities, dt);
context.worldEntity = (entities.size() > 0 && entities[0]) ? entities[0].get() : nullptr;
context.wellEntity = (entities.size() > 1 && entities[1]) ? entities[1].get() : nullptr;
context.probeEntity = (entities.size() > 2 && entities[2]) ? entities[2].get() : nullptr;
context.hudEntity = (!entities.empty() && entities.back()) ? entities.back().get() : nullptr;
if (wantsDeathScene) {
manager.QueueSceneChange<DeathScene>();
}
@ -195,12 +226,7 @@ inline void GameplayScene::Draw() {
DrawText(TextFormat("stars: %i", collectedCount), 14, 100, 16, Color{255, 223, 86, 255});
for (auto &entity : entities) {
if (!entity) {
continue;
}
auto collectible = entity->GetComponent<CollectibleComponent>();
if (collectible && collectible->get().collected) {
if (!entity || entity->queuedForFree) {
continue;
}

View File

@ -1,67 +1,20 @@
#pragma once
#include "Components.hpp"
#include "Entity.hpp"
#include "GameContext.hpp"
#include <algorithm>
#include <memory>
#include <vector>
void UpdateAllSystems(std::vector<std::shared_ptr<Entity>> &entities, GameContext &context,
float deltaTime) {
context.entities = &entities;
context.worldEntity = (entities.size() > 0 && entities[0]) ? entities[0].get() : nullptr;
context.wellEntity = (entities.size() > 1 && entities[1]) ? entities[1].get() : nullptr;
context.probeEntity = (entities.size() > 2 && entities[2]) ? entities[2].get() : nullptr;
context.hudEntity = (!entities.empty() && entities.back()) ? entities.back().get() : nullptr;
void UpdateAllSystems(std::vector<std::shared_ptr<Entity>> &entities, float deltaTime) {
for (auto &entity : entities) {
if (!entity) {
continue;
}
if (entity->context != &context) {
entity->SetContext(&context);
}
entity->Update(deltaTime);
}
for (size_t i = 0; i < entities.size(); ++i) {
auto &a = entities[i];
if (!a) {
continue;
}
auto aTransform = a->GetComponent<TransformComponent>();
auto aCollider = a->GetComponent<ColliderComponent>();
if (!aTransform || !aCollider) {
continue;
}
for (size_t j = i + 1; j < entities.size(); ++j) {
auto &b = entities[j];
if (!b) {
continue;
}
auto bTransform = b->GetComponent<TransformComponent>();
auto bCollider = b->GetComponent<ColliderComponent>();
if (!bTransform || !bCollider) {
continue;
}
const float dx = aTransform->get().x - bTransform->get().x;
const float dy = aTransform->get().y - bTransform->get().y;
const float r = aCollider->get().radius + bCollider->get().radius;
if ((dx * dx + dy * dy) <= (r * r)) {
aCollider->get().EmitCollision(*b);
bCollider->get().EmitCollision(*a);
}
}
}
auto remover = [](const std::shared_ptr<Entity> &entity) {
if (!entity) {
return true;