I have a class of Enemy
that i want to be the base class for all enemy types and also pure abstract one. At this point, all of its members and methods should be shared by the derived classes. Especially, there is the method loadTexture
that uses the static member texture
.
class Enemy
{
int hp;
int damage;
//
// all other fields
//
static TextureClass* texture; //needs to be static because every instance
//of enemy uses the same texture
public:
static void loadTexture()
{
CreateTextureFromFile("somefilepath",&texture); //needs to be static
// because textures are loaded before any instance is crated
}
void draw()
{
DrawToScreen(&texture, ...many data members passed here...);
}
};
TextureClass* Enemy::texture = nullptr;
Now, if i would like to make the Enemy
abstract and create different enemy types, the obvious choice would be inheritance. So i create:
class EnemyA : Enemy
{
static TextureClass* texture;
};
class EnemyB : Enemy
{
static TextureClass* texture;
};
But now, how do i load the texture for each of those? I can't use loadTexture
defined in base obviously. So the only choice besides writing the same method X times (where X is the number of enemy types) is to remove loadTexture
from base and create a global function, right? Same goes for draw
, i would need to redefine it for every derived, even though it would look exactly the same...
TL;DR The loadTexture
and draw
have exact same body regardless of the enemy type, but they use the static field that is different in each one. Is there a way to define something like an uniform method, that when called in derived class would use the field from the derived, not the base?
Thank You for any answers.
As mentioned by BЈовић, if you want to use inheritance to have the loadTexture() and draw() methods reference the right Texture for your class, the answer probably lies in using CRTP to implement your Enemy class, like follows:
template<typename TDerivedEnemy>
class Enemy
{
int hp;
int damage;
//
// all other fields
//
static TextureClass* texture; //needs to be static because every instance
//of enemy uses the same texture
public:
static void loadTexture()
{
CreateTextureFromFile("somefilepath",&texture); //needs to be static
// because textures are loaded before any instance is crated
}
void draw()
{
DrawToScreen(&texture, ...many data members passed here...);
}
};
Once that is done, you can declare your concrete enemy classes like follows:
class EnemyA : public Enemy<EnemyA>
{
public:
typedef Enemy<EnemyA> tBase;
...
};
class EnemyB : public Enemy<EnemyB>
{
public:
typedef Enemy<EnemyB> tBase;
...
};
then, in your implementation file, you also need to define the static variables for both EnemyA and EnemyB:
TextureClass* EnemyA::tBase::texture = nullptr;
TextureClass* EnemyB::tBase::texture = nullptr;
The name "Curiously Recurring Template Pattern" comes from the fact that EnemyA and EnemyB inherit from a template class where they already are parameter templates, hence recursion.
Edit: as discussed in the comment, this approach leads to EnemyA and EnemyB having no common base class, which makes it impossible to refer to them in a uniform way, i.e. you can't declare for instance a
std::vector< EnemyA* OR EnemyB* ??? > enemies;
because there simply isn't a common base class. To get around this, you can declare a common abstract base class like follows:
class EnemyBase {
public:
virtual void draw() = 0;
}
then make your template implementation inherit from it:
template<typename TDerivedEnemy>
class Enemy : public EnemyBase
{
...
};
Which allows you to do this:
std::vector<EnemyBase*> enemies;
//fill the enemies vector
...
for (auto enemy : enemies)
{
enemy->draw();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With