Video Coming Soon...
64: Pulling it All Together
We're going to use everything we've learned so far to create an ECS based combat engine.
The Code
I think I need to make the systems a class to make sure they use them, instead of a namespace/module style like I like to do:
View Source file ex64.cpp Only
#include <fmt/core.h>
#include <string>
#include <random>
#include <memory>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <ranges>
#include "dinkyecs.hpp"
using std::shared_ptr, std::make_shared, std::string;
using namespace DinkyECS;
std::random_device RNG;
std::mt19937 GENERATOR(RNG());
struct Combat {
int hp{0};
bool dead{false};
};
struct Weapon {
std::string name;
int damage;
};
struct Actor {
string name;
};
struct Game {
World world;
Entity player_id;
};
namespace systems {
int random_number(int from, int to) {
std::uniform_int_distribution rand(from, to);
return rand(GENERATOR);
}
Entity create_player(World& world) {
// player system
auto player = world.entity();
world.set<Combat>(player, {.hp=30});
world.set<Weapon>(player, {.name="Sword", .damage=30});
world.set<Actor>(player, {.name="Zed"});
return player;
}
void spawn_enemies(World& world, int count) {
for(int i = 0; i < count; i++) {
auto rat_id = world.entity();
int hp = random_number(3, 10);
int damage = random_number(1,5);
world.set<Combat>(rat_id, {.hp=hp});
world.set<Weapon>(rat_id, {.damage=damage});
world.set<Actor>(rat_id, {fmt::format("Rat {}", rat_id)});
}
}
int run_combat(World& world) {
int actor_count = 0;
// combat system
world.query<Combat, Weapon>([&](auto id, auto& combat, auto& weapon) {
// ideally we'd want to get some other actor to fight
auto& actor = world.get<Actor>(id);
actor_count++;
int damage = random_number(1, weapon.damage);
combat.hp -= damage;
fmt::println("{}: hp={}; damage={}; id={}",
actor.name, combat.hp, damage, id);
if(combat.hp <= 0) combat.dead = true;
});
return actor_count;
}
void cull_dead(World& world) {
// death system
std::vector<Entity> is_dead;
world.query<Combat>([&](auto id, auto& combat) {
if(combat.dead) is_dead.push_back(id);
});
for(auto id : is_dead) {
auto& actor = world.get<Actor>(id);
fmt::println("Removing dead: {}", actor.name);
world.remove<Combat>(id);
world.remove<Actor>(id);
world.remove<Weapon>(id);
}
}
bool player_wins(World& world, Entity player) {
if(auto combat = world.get_if<Combat>(player)) {
return combat->hp > 0;
} else {
return false;
}
}
}
int main(int argc, char* argv[]) {
Game game;
game.player_id = systems::create_player(game.world);
systems::spawn_enemies(game.world, 10);
while(systems::run_combat(game.world) > 1) {
systems::cull_dead(game.world);
}
if(systems::player_wins(game.world, game.player_id)) {
fmt::println("You won!");
} else {
fmt::println("You lose!");
}
}
The Breakdown
line of code- Description.
The Discussion
Blah blah.
Further Study
- Do this next.
Register for Learn C++ the Hard Way
Register to gain access to additional videos which demonstrate each exercise. Videos are priced to cover the cost of hosting.