Reputation:
I'm writing a 2d game and want to make it right from the beginning. I'm not entirely sure how to solve multiple inheritance and casting.
Lets say for instance that i have a game world. I create a class called Environment which all the objects will inherit.
Now if i create another class called Brick and Ground, this two classes are going to inherit the functions from environment. Now lets say i want to be able to explode the Brick upon contact, but the ground class won't ever use it, so it would be a waste to create a function like that in the environment class and make it virtual.
So i though, maybe if i cast the environment object back to the derived class and call its functions from there: (Only a example below to demonstrate the though process)
#include <iostream>
#include <string>
#include <vector>
#include <limits>
class Environment
{
protected:
std::string name;
int health;
int damage;
public:
Environment(std::string _name)
{
this->name = _name;
}
virtual std::string GetType() = 0;
std::string GetName()
{
return this->name;
}
void Spawn()
{
std::cout << this->name << ": Spawning object..." << std::endl;
}
int GetHealth()
{
return this->health;
}
void TakeDamage(int _damage)
{
this->health-=_damage;
}
virtual void Update() = 0;
virtual void Render() = 0;
virtual ~Environment(){}
};
class Ground : public Environment
{
private:
public:
Ground(std::string _name) : Environment(_name)
{
}
std::string GetType()
{
return "Ground";
}
void Update()
{
std::cout << "Updating: " << this->name << std::endl;
}
void Render()
{
}
};
class Brick : public Environment
{
private:
public:
Brick(std::string _name) : Environment(_name)
{
this->name = "Brick Ansikte";
this->health = 100;
}
std::string GetType()
{
return "Brick";
}
void Update()
{
std::cout << "Updating: " << this->name << std::endl;
}
void Render()
{
}
void Explode()
{
std::cout << "Exploding BRICK" << std::endl;
}
};
int main()
{
std::vector<Environment*> listEnvironment;
listEnvironment.push_back(new Ground("Ground fool"));
listEnvironment.push_back(new Brick("Ground tun"));
for(auto& env : listEnvironment)
{
std::cout << "HP: " << env->GetHealth() << std::endl;
env->Spawn();
env->Update();
if (env->GetType() == "Brick")
{
std::cout << "BRICK TYPE WAS FOUND, DYNAMIC CAST" << std::endl;
static_cast<Brick*>(env)->Explode();
}
delete env;
}
listEnvironment.empty();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
return 0;
}
Is this solution good or bad? Or should i create yet another base helper class that holds all the different type of actions that can occur in the game? Like explode, bounce etc etc?
Best regards nilo
Upvotes: 0
Views: 116
Reputation: 10727
There are two solutions:
Use dynamic_cast
:
if (Brick* brick = dynamic_cast<Brick*>(env))
{
std::cout << "BRICK TYPE WAS FOUND, DYNAMIC CAST" << std::endl;
brick->explode();
}
Add another method to Environment
named something like MaybeExplode
(that's actually a really bad name, but I can't really think of something better):
virtual void MaybeExplode() {}
Now, override it in Brick
:
void MaybeExplode()
{
std::cout << "Exploding BRICK" << std::endl;
}
Then unconditionally call MaybeExplode
in your for
loop:
...
env->Spawn();
env->Update();
env->MaybeExplode();
delete env;
This is not wasteful. It's really how OO design is. The person asking for an explosion doesn't know what env
is.
Also, dynamic_cast
is actually slower than a virtual function, so, if anything, your current version is more wasteful.
Upvotes: 2