I am currently in development of a little game project but I've run into a pretty significant issue. I have an array of class 'Entity' which is supposed to represent a map that holds only entities. I have an Entity class with a 'name' variable so far. I also have a 'Player' class that inherits from 'Entity' but the Player class also has a health variable. I can insert the player into the level by setting a level slot equal to the player object (because player is an entity). However, I cannot extract the player's health (using a player getHealth() function) because the level pointer is of type 'Entity'.
I tried looking up type conversions but I couldn't get the principle down. Is there a better way to do this whole process? My goal is to have many inherited classes from Entity that can all exist in the level, but have different members depending on their types.
My goal is to have many inherited classes from Entity that can all exist in the level, but have different members depending on their types.
That's a fine idea. It lets you process all the entities in the level and I expect you have good reasons to do that.
One way to handle this is to have class Entity store an entityType that tells which type of entity it really is. Then you can check if the Entity is a Player and, if so, cast Entity to Player and access the health.
But you might also find that it makes more sense to store the Player(s) on a level in a separate container so you access the players directly as needed. Of course, that makes it harder to process all items on the level. It's a trade off.
One technique is to create a function that will call a callback function on every item in the level.
This way you can store the entities in any way that makes sense. If you add a new entity somewhere, you just have to change forEachEnt() and poof! All the code that processes all the entities will work.
@JLBorges
Thank you for the fast response! Oh boy, I hope I am not way in over my head here but after reading the code, I feel like I can only understand a little bit of it. I think I have an idea of what you are saying but out of curiosity, why did you choose to use structs? If that makes a big difference that as.
@dhayden
Your first suggestion about checking the entityType was something I tried to do at first. I couldn't quite get the hang of casting it to another type though. I'll have to keep practicing. Your second suggestion sounds very efficient, but I may need to practice a bit more to understand it. I'm not a complete beginner, but sometimes struggle with OOP having not come from it in my experience.
> why did you choose to use structs? If that makes a big difference that as.
It does not make any difference.
"The keywords (class and struct) are identical
(except for the default member access and the default base class access.)" https://en.cppreference.com/w/cpp/language/class
> I feel like I can only understand a little bit of it.
There are two aspects in the code with which you may not be familiar:
The idea of having a separate class has_health is from a C++ idiom called mix-in: has_health is a "mix-in class"
The model that is used here is the first model mentioned in this article:
"A mixin models some small functionality slice and is not intended for standalone use. Rather, it is supposed to be composed with some other class needing this functionality
One use of mixins in object-oriented languages involves classes and multiple inheritance. In this model, a mixin is represented as a class, which is then referred to as a "mixin class," and we derive a composed class from a number of mixin classes using multiple inheritance." https://www.drdobbs.com/cpp/mixin-based-programming-in-c/184404445
(Though very often, mix-ins are implemented in C++ using the second model and CRTP)
Mix-ins are commonly used in C++ as a more flexible alternative to C-style type fields and callbacks.