Skip to content

Commit

Permalink
YAGNI: Simplify asset workflow, remove reflected factories, etc. (#33)
Browse files Browse the repository at this point in the history
* [WIP] Simplify Enemy spawn flow, fix race in `Resources`.

* Remove `EnemySpawner`, `EnemyFactoryBuilder`.

* Remove `WorldSpec`, add `AssetManifest`.

Hard-code manifest in game.cpp.

* Refactor asset loading into a dedicated scene.

* Remove `setup()`, can be done in constructors now.

* Misc cleanup.
  • Loading branch information
karnkaul committed Apr 22, 2024
1 parent 4557121 commit b3ad726
Show file tree
Hide file tree
Showing 36 changed files with 276 additions and 488 deletions.
24 changes: 0 additions & 24 deletions assets/worlds/playground.json

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include <spaced/game/asset_list.hpp>
#include <spaced/game/asset_loader.hpp>
#include <spaced/assets/asset_list.hpp>
#include <spaced/assets/asset_loader.hpp>
#include <spaced/services/resources.hpp>

namespace spaced {
Expand Down Expand Up @@ -37,31 +37,12 @@ auto AssetList::add_audio_clip(std::string uri) -> AssetList& {
return *this;
}

auto AssetList::read_world_spec(std::string_view const uri) -> WorldSpec {
if (uri.empty()) { return {}; }

auto const json = m_loader.load_json(uri);
if (!json) { return {}; }

auto ret = WorldSpec{};
ret.name = json["name"].as_string();
ret.background_tint = json["background_tint"].as_string();

if (auto const& player = json["player"]) {
ret.player.tint = player["tint"].as_string();
ret.player.exhaust_emitter = player["exhaust_emitter"].as_string();
ret.player.death_emitter = player["death_emitter"].as_string();
add_particle_emitter(ret.player.exhaust_emitter);
add_particle_emitter(ret.player.death_emitter);
}

for (auto const& enemy_factory : json["enemy_factories"].array_view()) {
add_particle_emitter(enemy_factory["death_emitter"].as<std::string>());
for (auto const& death_sfx : enemy_factory["death_sfx"].array_view()) { add_audio_clip(death_sfx.as<std::string>()); }
ret.enemy_factories.push_back(enemy_factory);
}

return ret;
void AssetList::add_manifest(AssetManifest manifest) {
for (auto& uri : manifest.textures) { add_texture(std::move(uri), false); }
for (auto& uri : manifest.mip_mapped_textures) { add_texture(std::move(uri), true); }
for (auto& uri : manifest.fonts) { add_font(std::move(uri)); }
for (auto& uri : manifest.audio_clips) { add_audio_clip(std::move(uri)); }
for (auto& uri : manifest.particle_emitters) { add_particle_emitter(std::move(uri)); }
}

auto AssetList::build_task_stages() const -> std::vector<AsyncExec::Stage> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma once
#include <bave/loader.hpp>
#include <spaced/assets/asset_manifest.hpp>
#include <spaced/async_exec.hpp>
#include <spaced/game/world_spec.hpp>
#include <spaced/services/services.hpp>
#include <set>

Expand All @@ -18,7 +18,7 @@ class AssetList {
auto add_particle_emitter(std::string uri) -> AssetList&;
auto add_audio_clip(std::string uri) -> AssetList&;

auto read_world_spec(std::string_view uri) -> WorldSpec;
void add_manifest(AssetManifest manifest);

[[nodiscard]] auto build_task_stages() const -> std::vector<AsyncExec::Stage>;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include <bave/graphics/particle_system.hpp>
#include <bave/json_io.hpp>
#include <bave/logger.hpp>
#include <spaced/game/asset_loader.hpp>
#include <spaced/assets/asset_loader.hpp>
#include <mutex>

namespace spaced {
Expand Down
File renamed without changes.
13 changes: 13 additions & 0 deletions src/spaced/spaced/assets/asset_manifest.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include <vector>

namespace spaced {
struct AssetManifest {
std::vector<std::string> textures{};
std::vector<std::string> mip_mapped_textures{};
std::vector<std::string> fonts{};
std::vector<std::string> audio_clips{};
std::vector<std::string> particle_emitters{};
};
} // namespace spaced
4 changes: 2 additions & 2 deletions src/spaced/spaced/async_exec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
namespace spaced {
using namespace std::chrono_literals;

AsyncExec::AsyncExec(std::span<Task const> tasks) {
AsyncExec::AsyncExec(std::vector<Task> tasks) {
if (tasks.empty()) { return; }

m_total = static_cast<int>(tasks.size());
enqueue(tasks);
}

AsyncExec::AsyncExec(std::span<Stage> stages) {
AsyncExec::AsyncExec(std::vector<Stage> stages) {
if (stages.empty()) { return; }
std::move(stages.begin(), stages.end(), std::back_inserter(m_stages));
m_total = std::accumulate(m_stages.begin(), m_stages.end(), 0, [](int count, auto const& tasks) { return static_cast<int>(tasks.size()) + count; });
Expand Down
4 changes: 2 additions & 2 deletions src/spaced/spaced/async_exec.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ class AsyncExec {

struct Status;

explicit AsyncExec(std::span<Task const> tasks);
explicit AsyncExec(std::span<Stage> stages);
explicit AsyncExec(std::vector<Task> tasks);
explicit AsyncExec(std::vector<Stage> stages);

auto update() -> Status;

Expand Down
54 changes: 0 additions & 54 deletions src/spaced/spaced/game/enemies/basic_creep_factory.cpp

This file was deleted.

29 changes: 0 additions & 29 deletions src/spaced/spaced/game/enemies/basic_creep_factory.hpp

This file was deleted.

2 changes: 1 addition & 1 deletion src/spaced/spaced/game/enemies/creep.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace spaced {
class Creep : public Enemy {
public:
explicit Creep(Services const& services, bave::NotNull<IEnemyDeathListener*> listener) : Enemy(services, listener, "Creep") {}
explicit Creep(Services const& services) : Enemy(services, "Creep") {}

void tick(bave::Seconds dt, bool in_play) override;

Expand Down
21 changes: 21 additions & 0 deletions src/spaced/spaced/game/enemies/creep_factory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include <bave/core/random.hpp>
#include <spaced/game/enemies/creep.hpp>
#include <spaced/game/enemies/creep_factory.hpp>
#include <spaced/services/resources.hpp>
#include <spaced/services/styles.hpp>

namespace spaced {
using bave::random_in_range;
using bave::Seconds;

auto CreepFactory::spawn_enemy() -> std::unique_ptr<Enemy> {
auto ret = std::make_unique<Creep>(get_services());
if (!m_tints.empty()) {
auto const& rgbas = get_services().get<Styles>().rgbas;
auto const tint_index = random_in_range(std::size_t{}, m_tints.size() - 1);
ret->shape.tint = rgbas[m_tints.at(tint_index)];
}
ret->health = m_initial_health;
return ret;
}
} // namespace spaced
16 changes: 16 additions & 0 deletions src/spaced/spaced/game/enemies/creep_factory.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once
#include <djson/json.hpp>
#include <spaced/game/enemy_factory.hpp>

namespace spaced {
class CreepFactory : public EnemyFactory {
public:
using EnemyFactory::EnemyFactory;

[[nodiscard]] auto spawn_enemy() -> std::unique_ptr<Enemy> final;

private:
std::array<std::string_view, 2> m_tints{"orange", "milk"};
float m_initial_health{2.0f};
};
} // namespace spaced
21 changes: 9 additions & 12 deletions src/spaced/spaced/game/enemy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,41 +11,38 @@ using bave::RoundedQuad;
using bave::Seconds;
using bave::Shader;

Enemy::Enemy(Services const& services, bave::NotNull<IEnemyDeathListener*> listener, std::string_view const type)
: health_bar(services), m_layout(&services.get<ILayout>()), m_listener(listener), m_type(type) {
Enemy::Enemy(Services const& services, std::string_view const type) : m_layout(&services.get<ILayout>()), m_health_bar(services), m_type(type) {
static constexpr auto init_size_v = glm::vec2{100.0f};
auto const play_area = m_layout->get_play_area();
auto const y_min = play_area.rb.y + 0.5f * init_size_v.y;
auto const y_max = play_area.lt.y - 0.5f * init_size_v.y - 50.0f;
setup(init_size_v, random_in_range(y_min, y_max));

health_bar.set_style(services.get<Styles>().progress_bars["enemy"]);
m_health_bar.set_style(services.get<Styles>().progress_bars["enemy"]);
}

auto Enemy::take_damage(float const damage) -> bool {
if (is_destroyed()) { return false; }
health.inflict_damage(damage);
if (health.is_dead()) { m_listener->on_death(EnemyDeath{.position = shape.transform.position, .points = points}); }
return true;
}

void Enemy::force_death() {
health = 0.0f;
health_bar.set_progress(0.0f);
m_listener->on_death(EnemyDeath{.position = shape.transform.position});
m_health_bar.set_progress(0.0f);
}

void Enemy::tick(Seconds const dt, bool const /*in_play*/) {
health_bar.position = shape.transform.position;
health_bar.position.y += 0.5f * shape.get_shape().size.y + 20.0f;
health_bar.size = {shape.get_shape().size.x, 10.0f};
health_bar.set_progress(health.get_hit_points() / health.get_total_hit_points());
health_bar.tick(dt);
m_health_bar.position = shape.transform.position;
m_health_bar.position.y += 0.5f * shape.get_shape().size.y + 20.0f;
m_health_bar.size = {shape.get_shape().size.x, 10.0f};
m_health_bar.set_progress(health.get_hit_points() / health.get_total_hit_points());
m_health_bar.tick(dt);
}

void Enemy::draw(Shader& shader) const {
shape.draw(shader);
health_bar.draw(shader);
m_health_bar.draw(shader);
}

void Enemy::setup(glm::vec2 max_size, float y_position) {
Expand Down
11 changes: 7 additions & 4 deletions src/spaced/spaced/game/enemy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
namespace spaced {
class Enemy : public IDamageable, public bave::IDrawable {
public:
explicit Enemy(Services const& services, bave::NotNull<IEnemyDeathListener*> listener, std::string_view type);
explicit Enemy(Services const& services, std::string_view type);

[[nodiscard]] auto get_bounds() const -> bave::Rect<> override { return shape.get_bounds(); }
auto take_damage(float damage) -> bool override;
Expand All @@ -28,22 +28,25 @@ class Enemy : public IDamageable, public bave::IDrawable {
void setup(glm::vec2 max_size, float y_position);

[[nodiscard]] auto get_layout() const -> ILayout const& { return *m_layout; }
[[nodiscard]] auto get_death_listener() const -> IEnemyDeathListener& { return *m_listener; }

void inspect() {
if constexpr (bave::debug_v) { do_inspect(); }
}

bave::RoundedQuadShape shape{};
ui::ProgressBar health_bar;
Health health{};
std::int64_t points{10};

std::string death_emitter{"particles/explode.json"};
std::vector<std::string> death_sfx{"sfx/bubble.wav"};

private:
virtual void do_inspect();

bave::NotNull<ILayout const*> m_layout;
bave::NotNull<IEnemyDeathListener*> m_listener;

ui::ProgressBar m_health_bar;

std::string_view m_type{};
bool m_destroyed{};
};
Expand Down
14 changes: 12 additions & 2 deletions src/spaced/spaced/game/enemy_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,18 @@

namespace spaced {
using bave::NotNull;
using bave::Seconds;

IEnemyFactory::IEnemyFactory(NotNull<Services const*> services) : m_services(services), m_audio(&services->get<IAudio>()) {}
EnemyFactory::EnemyFactory(NotNull<Services const*> services) : m_services(services), m_audio(&services->get<IAudio>()) {}

void IEnemyFactory::play_death_sfx() { m_audio->play_any_sfx(m_death_sfx); }
auto EnemyFactory::tick(Seconds const dt) -> std::unique_ptr<Enemy> {
if (spawn_rate <= 0s) { return {}; }

m_elapsed += dt;
if (m_elapsed >= spawn_rate) {
m_elapsed = 0s;
return spawn_enemy();
}
return {};
}
} // namespace spaced
Loading

0 comments on commit b3ad726

Please sign in to comment.