I'm trying to make a game with inheritance & interfaces and I'm wondering if my system could be working without ECS. (I explain at the end why I'm not using components).
To make it easier to understand there are three core class "families" in the code:
Engine related classes:
Graphics, Inputs, Audio, Physics,...
Using E~~~ as a prefix for each class
Interfaces:
I'm using them like properties, they don't contain data, they're contracts, like C# interfaces (and Java I believe?):
Transformable, Destroyable, ...
Using I~~~ as a prefix for each class
"Concrete" classes:
They contain data and implements the interfaces. The could be Player
, Bullet
, etc.
Using C~~~ as a prefix for each class
Plus a class named Engine that contains every E~~~ class so it makes it easy to access "Engine elements".
Engine-related code:
class EInput {
public:
enum { keyCount = 64 };
char state[keyCount];
char previousState[keyCount];
};
class EGraphics {
public:
int width, height;
};
class EPhysics {
public:
float gravity;
};
class EEngine {
public:
EInput* input;
EGraphics* graphics;
EPhysics* physics;
EEngine() {
input = new EInput();
graphics = new EGraphics();
physics = new EPhysics();
}
~EEngine() {
delete input;
delete graphics;
delete physics;
}
};
Interface-related code:
class ITransformable {
public:
virtual float getX() = 0;
virtual float getY() = 0;
virtual void setX(float x) = 0;
virtual void setY(float x) = 0;
virtual ~ITransformable() {}
};
class IDestroyable {
public:
virtual float getHp() = 0;
virtual void setHp(float hp) = 0;
virtual void hurt(float amount) = 0;
virtual void kill() = 0;
virtual bool isAlive() = 0;
virtual ~IDestroyable() {}
};
class IEngine {
public:
virtual void provideEngine(EEngine* engine) = 0;
virtual ~IEngine() {}
};
class ITeam {
public:
virtual int getTeam() = 0;
virtual void setTeam(int team) = 0;
virtual ~ITeam() {}
};
Concrete classes-related code:
class CCharacter : public IEngine, public ITransformable, public IDestroyable, public ITeam {
private:
/* IEngine */
EEngine* engine;
/* ITransformable */
float x, y;
/* IDestroyable */
float hp;
/* ITeam */
int team;
public:
CCharacter() : engine(nullptr), x(0.f), y(0.f), hp(0.f) {}
/* IEngine */
void provideEngine(EEngine* engine) override { this->engine = engine; }
/* ITransformable */
virtual float getX() override { return x; }
virtual float getY() override { return y; }
virtual void setX(float x) override { this->x = x; }
virtual void setY(float x) override { this->y = y; }
/* IDestroyable */
virtual float getHp() override { return hp; }
virtual void setHp(float hp) override { this->hp = hp; }
virtual void hurt(float amount) override { this->hp -= amount; }
virtual void kill() override { this->hp = 0.f; }
virtual bool isAlive() override { return this->hp > 0.f; }
/* ITeam */
virtual int getTeam() override { return this->team; };
virtual void setTeam(int team) override { this-> team = team; };
virtual void update() = 0;
virtual void draw() = 0;
};
class CPlayer : public CCharacter {
public:
CPlayer() {
setHp(100.f); //...
}
void update() override {}
void draw() override {}
};
enum { T_NONE, T_ALLY, T_ENEMY };
class CAIPlayer : public CCharacter {
private:
int team;
public:
CAIPlayer() : team(T_NONE) {}
void update() override {}
void draw() override {}
};
And a game would be similar to this, removing the game loop:
int main() {
EEngine engine;
//Initialize some values
{
EGraphics& g = *engine.graphics;
g.width = 512;
g.height = 288;
EPhysics& p = *engine.physics;
p.gravity = 0.4f;
}
//Create our characters, a factory would be used, this is just to show the idea
//Our hero
CPlayer sam; sam.provideEngine(&engine);
//Two friendlies
CAIPlayer lambert; lambert.provideEngine(&engine);
lambert.setTeam(T_ALLY);
CAIPlayer wilkes; wilkes.provideEngine(&engine);
wilkes.setTeam(T_ALLY);
wilkes.kill();
//The bad guy
CAIPlayer nikoladze; nikoladze.provideEngine(&engine);
nikoladze.setTeam(T_ENEMY);
}
- Is this code clean?
What are the limits of such a system? I mean, I don't see any duplicated behaviour.
I read some really nice articles and questions on the net, including Stack Overflow, about entity-components-systems and how amazing it is, but it seems (to me) that it is overdone by people, the code is a big mess with templates everywhere and yet there are no results on the screen.
I know that this code might be weak compared to ECS, but at which point?
I'm trying to keep it simple.