0

This is similar to these questions: Is it possible to initialise two classes, that require pointers to each other, at the same time?

Two classes that refer to each other

But I am looking for a more general or "best practices" solution.

This is a problem that often comes up in game design: when many objects need information from other objects and one of them will inevitably be the "most downstream" (in terms of header includes) than any other element. Seeing as there are thousands of games out there, I am assuming there is some "good" solution to this.

In particular, I have a virtual base class "GameObject" to which all my game objects belong: players, bullets, zombies... and a map of "GameObject*" that stores all game objects. My GameState class holds this map and all Game objects retain the same static pointer to the map. Each GameObject also has a vector<GameObject*> called "collisions" that holds a ptr to each GameObject that collided with it since the last update.

Class GameObject {
    private:
    static int objID;
    int id;

    //maps
    static std::unordered_map<int, GameObj*>* objs;

    public:
    //list of collisions with other objects
    std::vector<GameObj*> collisions;


//Accessors - some generic accessors
unsigned int getID() const;
ObjType getType() const;
sf::Vector2f getPos() const;
virtual const sf::FloatRect& getGlobalBounds() const = 0;

//And the key function, update
virtual STATE update() = 0;

}

My GameState class contains a method which calls Player::update() (for all players, its a non-Static method), Zombies::update() for all zombies and so on. Update() is a pure virtual function of GameObject.

If Player.h includes Zombie.h then I can go into my player class, iterate through my vector of bullets and check if any of them has collided with a zombie. I can delete the bullet, add to the players score, etc. as necessary.

However, in my Zombie class, I can deduce that the zombie has collided with a bullet but I cannot deduce how much damage that bullet does, or anything else about the bullet because the zombie doesn't know what a bullet is (i.e., I cant dynamic cast GameObj* to Bullet* and use accessor methods). So how can we solve this problem.

Solutions:

  • It occurred to me that a primitive fix may be to include "Bullet.h" in for both Zombies and Players - which may work but does not fix the larger problem - say the player did direct damage to the zombie, for instance.

  • A "forward declaration" of the class did not work for me here. The compiler still gets angry over a dynamic cast from GameObject* to Bullet*

  • Passing all bullet/zombie information through (via the wrapper gameState class) is possible but cumbersome, not to mention I'm using a pure virtual function for all my game objects, most of which would not make use of this information being passed.

  • I could expand my GameObject class to hold more information about a GameObject like its damage. However, for some GameObjects this information would be extraneous.

  • Finally, I could do everything I need to do to the zombie in the Player class, (via dynamic casting) but this feels disjunct, why should the player be directly modifying the zombie?

Any other clever solutions?

  • 3
    Don't say "compiler angry", include the *exact* error you're getting. Normally you can dynamically cast to a parent class without issues, but going from a parent class to a specific subclass is not recommended. – tadman Feb 01 '21 at 20:24
  • 2
    It's also `class` not `Class`. C++ is case sensitive and it's rubbed off on a lot of us. – tadman Feb 01 '21 at 20:25
  • Why are you persisting collisions within the thing itself? Those are usually very short lived and having them baked into your structures is going to be punishing from a performance perspective. It's also odd that you're using a pointer to a Standard Library container. Unless these are shared, avoid pointers. If they are shared, use an appropriate [pointer wrapper like `std::shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr). – tadman Feb 01 '21 at 20:27
  • Persisting collisions? Like, retaining a ptr to the colliding object? Well, it feels like a tradeoff, there are many colliding objects and each obj must take a different action if it is collided with. If I retain the ptr, I know these objects have collided. Else, if I test for collision in Class A -> perform action, then I would have to check for collisions all over again in Class -> B. After each game loop, of course, collisions from that loop are cleared. – J. Henry Welsh Feb 01 '21 at 20:35
  • Not an answer to your question; but in all likelyhood, your design is poor if you're in this state. You probably want something like an engine class that asks the players for their bullets, and then asks the zombies if any of them hit. This way the player class and zombie class never need each other. When you need to ask a question like this - do it another way. – UKMonkey Feb 01 '21 at 20:35
  • Compiler is not angry. Compiler is above silly human things like emotion. Compiler is the superior being. – user4581301 Feb 01 '21 at 20:35
  • I mean what is the purpose of the `collisions` property? That seems extraneous and possibly wasteful to the point of being absolutely crippling. It massively complicates your memory management issues, as any time you delete an object you will have to **exhaustively search through every object's collisions array and remove it** which will be extremely painful. If you want to go from frames per second to seconds per frame, this is how you do it. My advice: Don't. – tadman Feb 01 '21 at 20:36
  • If this only survives one frame then that's an even better reason to remove it entirely. This will be an allocation nightmare as you constantly reassign things. What you want to do is compute your collisions once, and store that in scratch memory. That you can just re-write each frame without concern. You just trash it, or even just write over it. – tadman Feb 01 '21 at 20:39
  • @tadman, yes, you're correct, I thought it was clear from the question that the compiler rejects a dynamic cast to some A* if A* has not yet been defined, that's the purpose of the question. To your other complaints, proper syntax is desirable, but this is more pseudo code than compiable. – J. Henry Welsh Feb 01 '21 at 20:40
  • We prefer compilable code as it can be useful to drop this into an editor and work it around to make a solution. Trivial issues like `Class` just get in the way of understanding. Any veteran C++ programmer becomes *extremely* attuned to little mistakes like this and they can be hugely distracting. Single character mistakes cause a lot of problems so we need to be hyper-alert for them. – tadman Feb 01 '21 at 20:41
  • @tadman, Well, seem my above comment - I only want to check for collisions 1 TIME per game loop, after collisions are found, they must be stored SOMEWHERE to do work with them. I am not familiar with scratch memory, but will look into it. – J. Henry Welsh Feb 01 '21 at 20:42
  • Normally you don't cast from a base to a child, you just call functions defined on the base and if they're overruled by `virtual` functions on the child then it all works out. What is the motivation behind casting here? – tadman Feb 01 '21 at 20:42
  • If you want to build engines an absolutely essential item in your library is the [Game Engine Book](https://www.gameenginebook.com) which talks about how you can reduce allocations to zero in many cases by cleverly re-using scratch memory. You can allocate an array of collision objects in advance, then re-use them each frame. Pointers to segments of this array can be passed to any functions that need to compute the consequences of such collisions. See also: [Real-Time Collision Detection](https://realtimecollisiondetection.net). – tadman Feb 01 '21 at 20:43
  • @UKMonkey, That is the solution I had originally, for zombies to check specifically if collide with a bullet. However, zombies also need to check for collisions with players and towers, so why not have 1 single collisions checking loop (where objects can be subdivided using a tree to objects in a progressively smaller quadrants) and then perform that expensive collision check, instead of doing collision checks in multiple different places? – J. Henry Welsh Feb 01 '21 at 20:46
  • @tadman, casting was just my premier way of accessing child object methods, but you're right overloaded virtual methods sound more elegant. I'm a newer programmer and still developing an intuition for all this. Clearly this is an elementary project and not a serious game engine, but the provided reading seems useful so I will navigate down that path. Thank you for your help. – J. Henry Welsh Feb 01 '21 at 20:51
  • No worries at all! Just trying to advise on things that can be a real trap if you're not careful. If you architect your classes properly then all the functions you need will be defined on the parent class, even if the parent class just has a dummy/empty implementation of it. The child classes can `virtual` override whatever they need to do there. – tadman Feb 01 '21 at 20:54
  • @J.HenryWelsh I'd suggest that it's your engine needs to check if zombies collide with a range of boxes, and then needs to establish if it means that the zombie gets hurt, stop, turn upside down or anything else... and your zombie only knows "these are the parts of me you can collide with" – UKMonkey Feb 02 '21 at 00:21

0 Answers0