Let's imagine I have some class hierarchy for a game. Like this: PhysicalObject is parent of BaseUnit is parent of BaseArcher and so on... And each hierarchy level adds its own stats.
Any PhysicalObject - has speed and size. Any BaseUnit - hit points.
I want it to be used as enums, so:
enum class PhysicalObjectStats { speed, size };
enum class BaseUnitStats { hitpoints };
Of course, BaseArcher must include stats of PhysicalObject and BaseUnit. How to make such an architecture? I've tried to implement it in such a simple way, using just map and dynamic polymorphism, but it doesn't compile (with a good reason, honestly):
https://coliru.stacked-crooked.com/a/8496247abd3e6f41
Also, I've tried to create a specific StatsHolder class and it works, but looks awful in all possible use-cases (setting and getting values). Actually, I cannot get value outside of class without down-casting:
https://coliru.stacked-crooked.com/a/46f4f4aec6a22c57
In addition, my stats may be not only int, but also bool and some other types. I tried to use std::variant for them and it works fine, but I don't want to think about it, using "GetStats()" method. So, ideally I want to have such an interface:
std::shared_ptr<PhysicalObject> unit = std::make_shared<BaseUnit>();
unit->SetValue<int>(PhysicalObjectStats::speed);
std::cout << unit->GetValue<bool>(BaseUnitStats::HasArmor);
And this makes the task even more difficult. Any suggestions of how it may be done in a good way?
CodePudding user response:
I don't see the point of map and enum whereas regular member seems to do the job:
struct PhysicalObject
{
int speed = 0;
int size = 0;
};
struct BaseUnitStats : PhysicalObject
{
// or composition:
// physicalObject PhysicalObject;
int hitpoints = 0;
};
For your StatsHolder
class:
template<class StatsEnum>
struct StatsHolder
{
void SetStats(StatsEnum stat, int value) { stats[stat] = value; }
int GetStats(StatsEnum stat) const { return stats.at(stat); }
private:
std::map<StatsEnum, int> stats;
};
Derived classes have to use using
:
struct BaseUnit : PhysicalObject, StatsHolder<BaseUnitStats> {
using PhysicalObject::GetStats;
using StatsHolder<BaseUnitStats>::GetStats;
using PhysicalObject::SetStats;
using StatsHolder<BaseUnitStats>::SetStats;
BaseUnit() { SetStats(BaseUnitStats::hitpoints, 300); }
};