Best way to store all entities in C++ game - c++

I'm trying to create a way to hold all entities in my C++ game, arrays wouldn't work since they are limited to one type. I need to store anything with the class Entity, and all it's derivatives in it. I've been trying all day to get a way to store all game entities in a way I can just loop through them all and draw them. Still haven't found a solution.

Assuming Entity is some base class that many things derive from, you can have a container (any container is fine, but std::vector is a good place to start unless you have some other specific requirements).
class Entity
{
public:
virtual void Draw() = 0;
};
class Atom : public Entity
{
public:
void Draw() override {}
};
class Environment : public Entity
{
public:
void Draw() override {}
};
int main()
{
std::vector< std::shared_ptr<Entity> > entities;
entities.push_back(std::make_shared<Atom>());
entities.push_back(std::make_shared<Environment>());
// Draw entities:
for (size_t ent = 0; ent < entities.size(); ++ent)
{
entities[ent]->Draw();
}
return 0;
}

You might be able to use std::vector. It has a lot of built-in functions for simple data manipulation, and you can use it with any type.

Related

How can I access to derived members from a pure virtual base class function?

I want to have a Collider interface class in which will have a overloaded -> operator to have access directy to the BoxCollider derived class. I want to have access to the members of box collider through the interface and chnage the type of collider at run-time.
So I thought of using templates:
template<typename T>
class ColliderV2 {
public:
virtual T* operator ->() = 0;
};
class BoxColliderV2 : public ColliderV2<BoxColliderV2> {
public:
float width;
float height;
BoxColliderV2* operator ->() {
return this;
}
};
int main()
{
ColliderV2<BoxColliderV2>* col = new BoxColliderV2;
(*col)->width = 1;
}
This works. But templates , as far as I know, will generate a brand new Collider class in compile-time filling T with Box Collider, correct? Thats why it worked. But later it prevents me from changing the collider type. I also thought of just making a virtual Collider class with Collider* operator->() ; overload in the derived class BoxCollider* operator->() ;
But if I tried :
Collider<BoxCollider>* col = new BoxCollider;
(*col)->width = 1; // won't work
doesn't work since Collider is not BoxCollider. And I don't want to dynamic_cast every possible collider type I could have. So, what can be done here?
As you've already found out, this doesn't work. Templates and runtime behavior are kind of contradicting mechanics. You can't create a common base class and let it act like a generic pointer to give you access to its derived types' members.
An interface specifies a contract against which you can code. You don't code against a specific implementation but the interface, so the interface has to provide all the members that you'd like to access. In your case this would result in width and height beeing part of ColliderV2 instead of BoxColliderV2. However this defeates the logic you are trying to mimic.
There are a few approaches that you can take:
Either make your collider type a variant, like
using ColliderType = std::variant<BoxColliderV2, MyOtherCollider, ...>;
and check for the actual type when you want to access the member
ColliderType myCollider = /* generate */;
if (auto boxCollider = std::get_if<BoxColliderV2>(&myCollider); boxCollider)
boxCollider->width = 0;
Or, keep the base class that you have, remove the operator-> and the template and do a dynamic cast on it:
ColliderV2* col = new BoxColliderV2;
if (auto boxCollider = dynamic_cast<BoxColliderV2*>(col); boxCollider)
boxCollider->width = 0;
You can also hide details like width or height behind more generic functions that are part of the interface. For example:
class ColliderV2 {
public:
virtual void setBounds(float width, float height) = 0;
};
class BoxColliderV2 : public ColliderV2 {
public:
void setBounds(float width, float height) override {
this->width = width;
this->height = height;
}
private:
float width;
float height;
};
int main()
{
ColliderV2* col = new BoxColliderV2;
col->setBounds(1, 1);
}
What you are trying to do is discouraged by C++. What you are trying to do is to change the type of something based on the return value of a function. The type system is designed to stop you from writing code like this.
One important restriction of a function is that can only return one type-of-thing. You can return one of a list of things if you wrap those possibilities in a class, and return that. In C++17, a ready-made class for this is std::variant. The restriction on this is that the list of things must be fixed (or a closed-set). If you want an arbitrary set of return values (open-set), you must use a different approach. You must restate your problem in terms a function that is done on the return value.
class BoxColliderV2 : public MyBaseCollider {
public:
void SetWidth(float new_width) override;
};
You may find this video useful. The bit of interest starts at around 40 minutes (but watch the whole video if you can). If you are interested in advice, I would suggest starting with std::variant, and if it works, move to virtual functions. Problems like collision detection get really complicated really quickly, and you will almost certainly require double dispatch at some stage. Start simple, because it's only going to get more complicated.
These excerpts from the ISO-Guidelines may help
1. When you change the semantic meaning of an operator, you make it
harder for other programmers to understand you code. guideline.
2. Dynamic casting is verbose and ugly, but deliberately so, because dynamic casting is dangerous, and should stand out. guideline
I think you are approaching the problem from the wrong direction. The purpose of an interface is that you don't have to know about the exact type or the implementation.
For example: You are using Axis-Aligned Bounding Boxes for collision detection. So, even if your CircleCollider uses a radius, you are still able to calculate its width and height from it. Now, you don't have to worry about if you are dealing with a BoxCollider or a CircleCollider, you have everything to make a Bounding Box.
class Collider
{
public:
virtual float x() const = 0;
virtual float y() const = 0;
virtual float width() const = 0;
virtual float height() const = 0;
};
class BoxCollider : public Collider
{
// Implementation...
};
class CircleCollider : public Collider
{
// Implementation...
};
Of course, you are maybe using something else, and not AABBs. I just wanted to demonstrate how you can use interfaces effectively.

C++ Disallowing certain functions if object is of a type

I've got a weird quality of life problem:
Entity* Bob = new Camera where Entity (abstract) is a type of object that I want in an vector array. And Camera is the specific object.
I have no problems with getting Camera to inherit and implement the functions of Entity into camera. the Camera runs fine. The problem arises when I create another object with Entity* Joe = new Cube;
Now I want to be able to group together Joe and Bob, but Bob, being a moving object, I want him to inherit the ProcessKeyboard(); function, so I can use it when using Bob. But Joe doesn't move, so I'd rather Joe not even have access to the ProcessKeyboard(); function.
Now, I could make Joe's ProcessKeyboard function do nothing, but I'd rather the compiler not allow me to to present it as an option for Joe to do.
I think my main question is what should I be looking up to solve this?
I believe the most logical solution would be:
class Entity
{
virtual void Update() = 0;
}
class Camera : public Entity
{
void Update() override {
// ...
}
void ProcessKeyboard() {
// ...
}
}
class Cube : public Entity
{
void Update() override {
// ...
}
}
Then you store entities and cameras separately:
vector<Entity*> entities; // pointers to all your entities are here
vector<Camera*> cameras; // additionally track Camera objects
for(auto entity : entities) {
entity->Update();
}
for(auto camera : cameras) {
camera->ProcessKeyboard();
}
...I want him to inherit the ProcessKeyboard(); function, so I can use
it when using Bob.
As you see, in order to use ProcessKeyboard() you don't need to inherit it; Also I must say, that because ProcessKeyboard() is not virtual there are no additional vtables involved (only for Entity) and no vtable lookup occur on ProcessKeyboard() call. And it is good.
However, if there are a lot of different "movable" objects and you don't care about performance so much... The OOP-style solution is obvious: just create an "interface" for movable objects.
class IMovable
{
virtual void ProcessKeyboard() = 0;
}
class Camera : public Entity, public IMovable
{
void Update() override {
// ...
}
void ProcessKeyboard() override {
// ...
}
}
Usage:
vector<Entity*> entities; // the same approach
vector<IMovable*> movable; // additionally track all movable objects
for(auto entity : entities) {
entity->Update();
}
for(auto object : movable) {
object ->ProcessKeyboard();
}
Besides of everything was said, there is also a dynamic_cast but in this particular case, it sounds like last-minute decision making...

Using the strategy pattern if the concrete strategy depends on the concrete parameter type

I'm currently working with a System/Data hierarchy implemented like this:
class SystemData
{
}
class SystemDataA : public SystemData
{
int x;
}
class SystemDataB : public SystemData
{
float y;
}
class System
{
virtual SystemData* getData() = 0;
virtual Result computeData(SystemData*) = 0;
}
class SystemA : public System
{
// really returns SystemDataA
SystemData* getData() override;
Result computeData(SystemData*) override;
}
class SystemB : public System
{
// really returns SystemDataB
SystemData* getData() override;
Result computeData(SystemData*) override;
}
In the end there is a controller class which does sth similar to this:
void foo()
{
for(auto& s : systemVec)
{
SystemData* data = s->getData();
FinalResult final = s->computeData(data);
}
}
Whereas now each specific system dynamic_casts back to the concrete type it is able to process. This seems like pretty bad design and I'd like to refactor this into sth more reasonable. My first idea was to just implement the computation algorithm inside the SystemData classes and then just do:
SystemData* data = s->getData();
FinalResult final = data->compute();
but does it really belong there?
It appears more intuitive to have a separate algorithm hierarchy, probably implemented with the strategy pattern. However then I again have the problem of losing runtime type info of the data because all algorithms get passed the abstract data type and in the end will have to dynamic cast and do nullptr and error checks again. So is it still better to implement the algorithm inside the data classes itself? Can I maybe still implement the hierarchy in a separate module and just add function pointers or a similar construct to the data class? Is there a completely different solution I'm not aware of?

Updating data members of different derived classes of the same base class within a vector

I am writing a 3D gridded model in C++ which has different cell types, all stored within a vector that is in a Grid class. I have defined a base GridCell class and I also have two derived classes GridCell1 and GridCell2.
Now in setting up the model, I read in a text file that tells me how to fill my gridCell vector (std::vector<gridCell*> gridCellVector) in the Grid class; meaning it tells me what types of derived cells to push_back into my gridCellVector.
Then I read in another input file that contains initial state variable information for each GridCell in my Grid, in the order laid out by the 1st input file.
Each derived class (GridCell1 and GridCell2) has some state variables (private data members) that the other doesn't. How can I (or is it possible to) access and update/initialize/set the derived class' data members as I read in the second input file?
I've tried a couple different things and seem only able to return my get/set functions defined in the GridCell base class. I can't figure out how to access the functions in the derived classes when working with each derived GridCell as I step through the vector.
Edit: I am surprised people haven't mentioned downcasting, other than saying not to use dynamic_cast. I always know the type of GridCell I am updating because I keep track of what has been loaded into the vector when reading in the first input file. Since i am always certain of the type of GridCell, isn't dynamic_cast safe?
Double Edit:. Because I pass the GridCell objects to other functions that need to reference the data members and functions specific to the appropriate GridCell instance of the passed object, I'm realizing the design (of many parts) of my model does not currently pass muster. So, for now, I'm giving up on the idea of having to ride the GridCelltypes at all and will just create one huge GridCell class that fits all my needs. This way I can fill, and then access, whatever data members and functions I need later on down the line.
If you're sure you want to use a two-step process, I suggest you give GridCell a pure virtual init method:
virtual void init(istream &) = 0;
then implement it in each derived class. Its purpose is to read data from the file and initialize the initial state variables.
Single pass
As others have said, it may be best to read both files at once and do the derived class specific initialization at the same time as creating the derived classes:
std::unique_ptr<GridCell> createGridCell1(std::istream& init) {
auto cell = std::make_unique<GridCell1>();
int value;
init >> value;
cell->setGridCell1State(value);
return cell;
}
std::unique_ptr<GridCell> createGridCell2(std::istream& init) {
// similarly to CreateGridCell1()...
}
std::vector<GridCell::Ptr> createCells(std::istream& types, std::istream& init) {
std::vector<GridCell::Ptr> cells;
std::string type;
while (types >> type) {
if (type == "GridCell1")
cells.push_back(createGridCell1(init));
else
cells.push_back(createGridCell2(init));
}
return cells;
}
int main() {
auto types = std::istringstream("GridCell1 GridCell2 GridCell1 GridCell1");
auto init = std::istringstream("1 2.4 2 3");
auto cells = createCells(types, init);
for (auto& cell : cells)
cell->put();
}
Live demo.
Two pass with Visitor
If you must do the initialization in a second pass you could use the Visitor pattern. You have some sort of GridCellVisitor that knows how to visit all the different kinds of grid cells:
class GridCellVisitor {
protected:
~GridCellVisitor() = default;
public:
virtual void visit(GridCell1& cell) = 0;
virtual void visit(GridCell2& cell) = 0;
};
and your grid cells know how to accept a GridCellVisitor:
class GridCell1 : public GridCell {
int state = 0;
public:
void setGridCell1State(int value) { state = value; }
void accept(GridCellVisitor& visitor) override { visitor.visit(*this); }
};
class GridCell2 : public GridCell {
double state = 0.0;
public:
void setGridCell2State(double value) { state = value; }
void accept(GridCellVisitor& visitor) override { visitor.visit(*this); }
};
This way you can separate the responsibility of initializing the grid cells with an input stream from the grid cells themselves and avoid having to do fragile downcasts on the grid cells:
class GridCellStreamInitializer : public GridCellVisitor {
std::istream* in;
public:
GridCellStreamInitializer(std::istream& in) : in(&in){}
void visit(GridCell1& cell) override {
int value;
*in >> value;
cell.setGridCell1State(value);
}
void visit(GridCell2& cell) override {
double value;
*in >> value;
cell.setGridCell2State(value);
}
};
int main() {
auto in = std::istringstream("GridCell1 GridCell2 GridCell1 GridCell1");
auto cells = createCells(in);
auto init = std::istringstream("1 2.4 2 3");
auto streamInitializer = GridCellStreamInitializer(init);
for (auto& cell : cells)
cell->accept(streamInitializer);
}
Live demo.
The downside is GridCellVisitor must be aware of all different kinds of grid cells so if you add a new type of grid cell you have to update the visitor. But as I understand it your code that reads the initialization file must be aware of all the different kinds of grid cells anyway.
Your vector<gridCell*> knows only the base class of its elements and can hence only call gridCell functions.
I understand that your approach, is to first fill the vector with pointer to cells of the correct derived type, and never the base type. Then for each cell, you read class dependent data.
The easiest way, if you don't want to change approach
The cleanest way would be to define a virtual load function in the base cell:
class gridCell {
...
virtual bool load (ifstream &ifs) {
// load the common data of all gridCells and derivates
return ifs.good();
}
};
The virtual function would be overriden by teh derived cells:
class gridCell1 : public gridCell {
...
bool load (ifstream &ifs) override {
if (gridCell::load(ifs)) { // first load the common part
// load the derivate specific data
}
return ifs.good();
}
};
Finally, you can write your container loading function:
class Grid {
...
bool load (ifstream &ifs) {
for (auto x:gridCellVector)
if (!x->load(ifs))
break; // error ? premature end of file ? ...
}
};
The cleanest way ?
Your problem looks very much like a serialisation problem. You load grids, may be you write grids as well ? If you control the file format, and perform the creation and loading of the cells in a single pass, then you don't need to reinvent the wheel and could opt for a serialisation library, like boost::serialization.

How to "skip" certain child class functions

I wasn't sure how to exactly title this, but I am trying to figure out something with polymorphism.
So basically, I want to have an array of the parent class (object) that holds a bunch of it's child classes (ones that are and aren't collidable). However, I want to be able to put this array into a loop and run the collision function for only the collidable child class, but since the other child class doesn't have a collide function, how can I do this?
(Looks something like this)
class Object
{
protected:
Image image; // Pseudo code to make point
public:
void Collision() = 0;
//Constructor/Destructor
Object(void);
~Object(void);
};
class Collidable : Object
{
private:
Position myPosition; // Pseudo code to make point
public:
void Collision(); // Has collision function for parent class
//Constructor/Destructor
Collidable(void);
~Collidable(void);
};
class Uncollidable : Object
{
private:
Position myPosition; // Pseudo code to make point
public:
// No collision function for parent class
//Constructor/Destructor
Uncollidable(void);
~Uncollidable(void);
};
int main()
{
Collidable collide1, collide2, collide3;
Uncollidable uncollide1, uncollide2, uncollide3;
Object *objects[] { collide1, collide2, uncollide1, uncollide2, uncollide3, collide3 };
for(int i = 0; i < 6; i++)
{
objects[i].Collide(); // Should not work.
}
return 0;
}
^(this was just an example to help show my question, do pardon some of the syntax errors if any)
I'm pretty sure, however, that something like this would be an error since void Collide() doesn't exist in the Uncollidable class. So how might I be able to still run the void Collide() function in the loop while avoiding error? Or is something like this impossible and I just have to make two separate arrays?
I hope I explained my question well.
(I tried to research this, but every time I tried I just got sent to the basics of polymorphism)
You can just do this:
for(int i = 0; i < 6; i++)
{
Collidable c = dynamic_cast<Collidable*>(objects[i]);
if(c != nullptr) // dynamic_cast will return null if objects[i] is not of type Collidable
c->Collide(); // Should work.
}
In your code there is one bug, you have made Collide() pure virtual in class Object, but you are not overriding it in Uncollidable. It will not work. Either override it in Uncollidable (which is inappropriate), or give a default body to Object::Collide() (which is inappropriate also).
There is a better design, put all the common interface in Object, separate out different behaviors in other interface. It will lead to good OO design ( compliant with IS-A relationship)
class Object
{
protected:
Image image; // Pseudo code to make point
public:
Object(void);
~Object(void);
//other common interface
};
class Collidable // this is an interface that represent 'collidable' behavior
{
public:
virtual void Collision() = 0;
}
class CollidableObject : public Object, public Collidable
{ ... }
class UncollidableObject : public Object
{ ... }
Note: Object must be inherited publicly, otherwise you will not be able to treat object os CollidableObject and UncollidableObject as object of Object.