Skip to content

Commit c1e812f

Browse files
feat: add component registry and basic gameplay components (#19)
* feat: implement component registration system for game components * feat: add game component classes for collider, enemy, health, and player * refactor: use std::function for factory map --------- Co-authored-by: AhmedAmrNabil <ahmedamr24680@gmail.com>
1 parent d34b246 commit c1e812f

14 files changed

Lines changed: 251 additions & 10 deletions

CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,19 @@ add_executable(GAME_APPLICATION
136136
src/common/components/free-camera-controller.cpp
137137
src/common/components/movement.cpp
138138

139+
src/game/components/collider.cpp
140+
src/game/components/enemy.cpp
141+
src/game/components/health.cpp
142+
src/game/components/player.cpp
143+
src/game/game-registration.cpp
144+
139145
src/common/systems/forward-renderer.cpp
140146
)
141147

142148
target_include_directories(GAME_APPLICATION
143149
PRIVATE
144150
src/common
151+
src/game
145152
src/states
146153
)
147154

src/common/components/component-deserializer.hpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include "../ecs/component-registry.hpp"
34
#include "../ecs/entity.hpp"
45
#include "camera.hpp"
56
#include "free-camera-controller.hpp"
@@ -8,20 +9,24 @@
89

910
namespace our {
1011

12+
inline void registerCommonComponents() {
13+
static bool initialized = false;
14+
if (initialized) return;
15+
16+
ComponentRegistry::registerType<CameraComponent>();
17+
ComponentRegistry::registerType<FreeCameraControllerComponent>();
18+
ComponentRegistry::registerType<MovementComponent>();
19+
ComponentRegistry::registerType<MeshRendererComponent>();
20+
21+
initialized = true;
22+
}
23+
1124
// Given a json object, this function picks and creates a component in the given entity
1225
// based on the "type" specified in the json object which is later deserialized from the rest of the json object
1326
inline void deserializeComponent(const nlohmann::json& data, Entity* entity) {
27+
registerCommonComponents();
1428
std::string type = data.value("type", "");
15-
Component* component = nullptr;
16-
if (type == CameraComponent::getID()) {
17-
component = entity->addComponent<CameraComponent>();
18-
} else if (type == FreeCameraControllerComponent::getID()) {
19-
component = entity->addComponent<FreeCameraControllerComponent>();
20-
} else if (type == MovementComponent::getID()) {
21-
component = entity->addComponent<MovementComponent>();
22-
} else if (type == MeshRendererComponent::getID()) {
23-
component = entity->addComponent<MeshRendererComponent>();
24-
}
29+
Component* component = ComponentRegistry::create(type, entity);
2530
if (component) component->deserialize(data);
2631
}
2732

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#pragma once
2+
3+
#include <functional>
4+
#include <string>
5+
#include <unordered_map>
6+
7+
#include "entity.hpp"
8+
9+
namespace our {
10+
11+
class ComponentRegistry {
12+
private:
13+
template <typename T>
14+
static Component* createComponent(Entity* entity) {
15+
return entity->addComponent<T>();
16+
}
17+
18+
static inline std::unordered_map<std::string, std::function<Component*(Entity*)>> factories = {};
19+
20+
public:
21+
template <typename T>
22+
static void registerType() {
23+
factories[T::getID()] = createComponent<T>;
24+
}
25+
26+
static Component* create(const std::string& type, Entity* entity) {
27+
auto it = factories.find(type);
28+
29+
if (it == factories.end()) return nullptr;
30+
31+
return it->second(entity);
32+
}
33+
};
34+
35+
} // namespace our

src/game/components/collider.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include "collider.hpp"
2+
3+
static gameplay::ColliderShape parseColliderShape(const std::string& value, gameplay::ColliderShape fallback) {
4+
if (value == "Sphere") return gameplay::ColliderShape::Sphere;
5+
if (value == "Capsule") return gameplay::ColliderShape::Capsule;
6+
return fallback;
7+
}
8+
9+
namespace gameplay {
10+
11+
void ColliderComponent::deserialize(const nlohmann::json& data) {
12+
if (!data.is_object()) return;
13+
shape = parseColliderShape(data.value("shape", std::string("Sphere")), shape);
14+
layer = data.value("layer", layer);
15+
radius = data.value("radius", radius);
16+
height = data.value("height", height);
17+
isTrigger = data.value("isTrigger", isTrigger);
18+
}
19+
20+
} // namespace gameplay

src/game/components/collider.hpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#pragma once
2+
3+
#include <ecs/component.hpp>
4+
5+
namespace gameplay {
6+
7+
enum class ColliderShape { Sphere, Capsule };
8+
9+
class ColliderComponent : public our::Component {
10+
public:
11+
ColliderShape shape = ColliderShape::Sphere;
12+
std::string layer = "default";
13+
float radius = 0.5f;
14+
float height = 1.0f;
15+
bool isTrigger = false;
16+
17+
static std::string getID() {
18+
return "Collider";
19+
}
20+
21+
void deserialize(const nlohmann::json& data) override;
22+
};
23+
24+
} // namespace gameplay

src/game/components/enemy.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include "enemy.hpp"
2+
3+
static gameplay::EnemyType parseEnemyType(const std::string& type, gameplay::EnemyType fallback) {
4+
if (type == "Brute") return gameplay::EnemyType::Brute;
5+
if (type == "Charger") return gameplay::EnemyType::Charger;
6+
if (type == "Flyer") return gameplay::EnemyType::Flyer;
7+
return fallback;
8+
}
9+
10+
namespace gameplay {
11+
12+
void EnemyComponent::deserialize(const nlohmann::json& data) {
13+
if (!data.is_object()) return;
14+
type = parseEnemyType(data.value("enemyType", std::string("Brute")), type);
15+
moveSpeed = data.value("moveSpeed", moveSpeed);
16+
acceleration = data.value("acceleration", acceleration);
17+
turnSpeed = data.value("turnSpeed", turnSpeed);
18+
aggroRange = data.value("aggroRange", aggroRange);
19+
attackRange = data.value("attackRange", attackRange);
20+
separationRadius = data.value("separationRadius", separationRadius);
21+
preferredDistance = data.value("preferredDistance", preferredDistance);
22+
scoreValue = data.value("scoreValue", scoreValue);
23+
}
24+
25+
} // namespace gameplay

src/game/components/enemy.hpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#pragma once
2+
3+
#include <ecs/component.hpp>
4+
5+
namespace gameplay {
6+
7+
enum class EnemyType { Brute, Charger, Flyer };
8+
9+
class EnemyComponent : public our::Component {
10+
public:
11+
EnemyType type = EnemyType::Brute;
12+
float moveSpeed = 2.0f;
13+
float acceleration = 8.0f;
14+
float turnSpeed = 6.0f;
15+
float aggroRange = 15.0f;
16+
float attackRange = 1.5f;
17+
float separationRadius = 1.0f;
18+
float preferredDistance = 4.0f;
19+
int scoreValue = 100;
20+
21+
static std::string getID() {
22+
return "Enemy";
23+
}
24+
25+
void deserialize(const nlohmann::json& data) override;
26+
};
27+
28+
} // namespace gameplay

src/game/components/health.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include "health.hpp"
2+
3+
namespace gameplay {
4+
5+
void HealthComponent::deserialize(const nlohmann::json& data) {
6+
if (!data.is_object()) return;
7+
maxHealth = data.value("maxHealth", maxHealth);
8+
currentHealth = data.contains("currentHealth") ? data["currentHealth"].get<float>() : maxHealth;
9+
invulnerabilityTimer = data.value("invulnerabilityTimer", invulnerabilityTimer);
10+
isDead = data.value("isDead", isDead);
11+
}
12+
13+
} // namespace gameplay

src/game/components/health.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#pragma once
2+
3+
#include <ecs/component.hpp>
4+
5+
namespace gameplay {
6+
7+
class HealthComponent : public our::Component {
8+
public:
9+
float maxHealth = 100.0f;
10+
float currentHealth = 100.0f;
11+
float invulnerabilityTimer = 0.0f;
12+
bool isDead = false;
13+
14+
static std::string getID() {
15+
return "Health";
16+
}
17+
18+
void deserialize(const nlohmann::json& data) override;
19+
};
20+
21+
} // namespace gameplay

src/game/components/player.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#include "player.hpp"
2+
3+
namespace gameplay {
4+
5+
void PlayerComponent::deserialize(const nlohmann::json& data) {
6+
if (!data.is_object()) return;
7+
radius = data.value("radius", radius);
8+
height = data.value("height", height);
9+
}
10+
11+
} // namespace gameplay

0 commit comments

Comments
 (0)