From 50066db8efb81c431afbba14456659ca80a147c8 Mon Sep 17 00:00:00 2001 From: HumanoidSandvichDispenser Date: Mon, 16 Mar 2026 00:09:43 -0700 Subject: [PATCH] Add main loop --- as6/components/PhysicsComponent.cpp | 9 +++ as6/components/SpawnComponent.cpp | 86 ++++++++++++++++++++++++++++- as6/components/SpawnComponent.hpp | 21 ++++++- as6/scene/GameplayScene.cpp | 48 ++++++++-------- 4 files changed, 139 insertions(+), 25 deletions(-) diff --git a/as6/components/PhysicsComponent.cpp b/as6/components/PhysicsComponent.cpp index 4293e83..9c0f7ec 100644 --- a/as6/components/PhysicsComponent.cpp +++ b/as6/components/PhysicsComponent.cpp @@ -2,6 +2,7 @@ #include "Entity.hpp" #include "components/GravityWellComponent.hpp" +#include "components/MeterComponent.hpp" #include "components/NullZoneComponent.hpp" #include "components/TransformComponent.hpp" @@ -85,6 +86,14 @@ bool PhysicsComponent::ComputeWellDeltaVelocity(double px, double py, double dt, return false; } + // Probe cannot receive well acceleration when meter is depleted. + if (context && entity == context->probeEntity && context->hudEntity) { + auto meter = context->hudEntity->GetComponent(); + if (meter && meter->get().value <= 0.0f) { + return false; + } + } + if (IsInsideNullZone(px)) { return false; } diff --git a/as6/components/SpawnComponent.cpp b/as6/components/SpawnComponent.cpp index d520d96..d820348 100644 --- a/as6/components/SpawnComponent.cpp +++ b/as6/components/SpawnComponent.cpp @@ -1,5 +1,87 @@ #include "components/SpawnComponent.hpp" -void SpawnComponent::Setup() {} -void SpawnComponent::Update(float) {} +#include "Entities.hpp" +#include "Entity.hpp" +#include "components/CollectibleComponent.hpp" +#include "components/HazardComponent.hpp" +#include "components/NullZoneComponent.hpp" +#include "components/ScrollComponent.hpp" +#include "components/ScrollableComponent.hpp" +#include "components/TransformComponent.hpp" + +#include "raylib.h" + +#include + +void SpawnComponent::Setup() { + // Start a bit beyond the initial handcrafted segment. + cursorWX = 1500.0f; +} + +void SpawnComponent::Update(float) { + if (!context || !context->entities) { + return; + } + + auto scroll = entity->GetComponent(); + if (!scroll) { + return; + } + + const float cameraX = scroll->get().scrollX; + const float spawnLimit = cameraX + spawnAheadDistance; + + while (cursorWX < spawnLimit) { + const float r = static_cast(GetRandomValue(0, 99)); + if (r < 55.0f) { + const float y = static_cast(GetRandomValue(48, GetScreenHeight() - 48)); + auto star = CreateStar(cursorWX, y); + star->SetContext(context); + context->entities->push_back(star); + } else if (r < 88.0f) { + const float y = static_cast(GetRandomValue(42, GetScreenHeight() - 42)); + auto asteroid = CreateAsteroid(cursorWX, y); + asteroid->SetContext(context); + context->entities->push_back(asteroid); + } else { + const float width = static_cast(GetRandomValue(70, 140)); + auto zone = CreateNullZone(cursorWX, width); + zone->SetContext(context); + context->entities->push_back(zone); + } + + const float gap = + static_cast(GetRandomValue(static_cast(minGap), static_cast(maxGap))); + cursorWX += gap; + } + + const float cullX = cameraX - despawnBehindDistance; + for (auto &candidate : *context->entities) { + if (!candidate || candidate.get() == entity || candidate->queuedForFree) { + continue; + } + + const bool isSpawnedGameplayObject = + candidate->GetComponent().has_value() || + candidate->GetComponent().has_value() || + candidate->GetComponent().has_value(); + if (!isSpawnedGameplayObject) { + continue; + } + + auto scrollable = candidate->GetComponent(); + if (scrollable) { + if (scrollable->get().worldX < cullX) { + candidate->QueueFree(); + } + continue; + } + + auto transform = candidate->GetComponent(); + if (transform && transform->get().x < cullX) { + candidate->QueueFree(); + } + } +} + void SpawnComponent::Cleanup() {} diff --git a/as6/components/SpawnComponent.hpp b/as6/components/SpawnComponent.hpp index 7a33239..e9dcd76 100644 --- a/as6/components/SpawnComponent.hpp +++ b/as6/components/SpawnComponent.hpp @@ -3,10 +3,29 @@ #include "Component.hpp" /** - * Placeholder for spawn cursor/state while spawning system evolves. + * Spawns and despawns gameplay objects to keep the run effectively infinite. */ struct SpawnComponent : public Component { + /** + * Next world-space X coordinate where a spawn roll will happen. + */ float cursorWX = 0.0f; + /** + * How far ahead of current scroll we keep content generated. + */ + float spawnAheadDistance = 1200.0f; + /** + * How far behind the camera entities are culled. + */ + float despawnBehindDistance = 180.0f; + /** + * Minimum spacing between spawn rolls. + */ + float minGap = 160.0f; + /** + * Maximum spacing between spawn rolls. + */ + float maxGap = 320.0f; void Setup() override; void Update(float dt) override; diff --git a/as6/scene/GameplayScene.cpp b/as6/scene/GameplayScene.cpp index 14f9a2e..dd2eb13 100644 --- a/as6/scene/GameplayScene.cpp +++ b/as6/scene/GameplayScene.cpp @@ -20,30 +20,32 @@ void GameplayScene::Enter() { return; } - context.entities->push_back( + context.entities->emplace_back( CreateDebris(transform->get().x - 4.0f, transform->get().y, -22.0f, -36.0f)); - context.entities->push_back( + context.entities->emplace_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()); - entities.push_back(CreateGravityWell()); - entities.push_back(CreateProbe()); - entities.push_back(CreateStar(900.0f, 120.0f)); - entities.push_back(CreateAsteroid(1100.0f, 330.0f)); - entities.push_back(CreateNullZone(1280.0f, 120.0f)); - entities.push_back(CreateHUD()); + context.worldEntity = entities.emplace_back(CreateWorld()).get(); - if (auto meter = entities.back()->GetComponent()) { - meterValue = meter->get().value; + context.wellEntity = entities.emplace_back(CreateGravityWell()).get(); + + context.probeEntity = entities.emplace_back(CreateProbe()).get(); + + entities.emplace_back(CreateStar(900.0f, 230.0f)); + entities.emplace_back(CreateNullZone(1280.0f, 120.0f)); + + context.hudEntity = entities.emplace_back(CreateHUD()).get(); + + if (context.hudEntity) { + auto meter = context.hudEntity->GetComponent(); + if (meter) { + 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) { @@ -56,11 +58,6 @@ void GameplayScene::Enter() { void GameplayScene::Update(float 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(); } @@ -70,8 +67,15 @@ void GameplayScene::Draw() { DrawSceneOutline(); DrawText("Gravity Surfing", 14, 12, 20, Color{230, 238, 255, 255}); - auto worldScroll = entities[0]->GetComponent(); - auto probeTransform = entities[2]->GetComponent(); + std::optional> worldScroll; + if (context.worldEntity) { + worldScroll = context.worldEntity->GetComponent(); + } + + std::optional> probeTransform; + if (context.probeEntity) { + probeTransform = context.probeEntity->GetComponent(); + } if (worldScroll) { DrawText(TextFormat("scrollX: %.2f", worldScroll->get().scrollX), 14, 40, 16,