1
\$\begingroup\$

I'm in the early stages of developing a game in C++.

As of currently, I've defined a base GameObject class that is the base class for any interactive item in my map (e.g. enemies, loot, projectiles, etc.). When these items are created, I want to give them to an ObjectManager class. The ObjectManager class is responsible for iterating over each object and calling abstract methods like "update" or "render".

Originally, I planned on storing all objects (or references to the objects) in a single container. I would then iterate over this container.

However, I'm starting to have second thoughts about whether or not this is a good idea. I've come up with two reasons why this might be a bad idea:

1.) Regarding object collision, it makes sense to only perform calculations between certain objects on whether or not they've collided. For example, I would want to determine if the main character has collided with a loot item. However, I would never to want to calculate if a loot item has collided with another loot item (at least in my case).

2.) Object pools. Referencing this article, it has come to my attention that continuously allocating/de-allocating memory is expensive. Because of this, it is a common technique to use object pools that request memory upfront and then manage that memory without actually deleting it. One of the comments made in the article is that the objects that exist within the pool must all be the same size (or each pool slot must be big enough to accommodate the largest object).

Both of these examples make me think having a single container that houses all GameObject is a bad idea.

Instead, it seems like it would make more sense to have different containers for each type GameObject type. For example, it would make sense to have a container for enemies, loot, projectiles, etc. This way I would have more control in iterating/comparing only on objects that make sense for certain object types (e.g. don't try to calculate collisions between two loot items).

The problem I'm facing however is that I don't know how to break up these containers. Should all projectiles go in a single container? However, what if I later decide to create a "fire projectile" that performs some kind of "burn" logic. Would this require a new container to then to iterate over all fire projectiles? Essentially, I don't know how granular my containers should be.

I haven't come across any reference material on this topic. Any advice would be appreciated.

\$\endgroup\$
  • \$\begingroup\$ You mention collisions. Are you using a specific physics engine for this, or is your game simple enough to not need one? \$\endgroup\$ – Ed Marty Jul 3 '18 at 6:42
2
\$\begingroup\$

The problem I'm facing however is that I don't know how to break up these containers. Should all projectiles go in a single container? However, what if I later decide to create a "fire projectile" that performs some kind of "burn" logic. Would this require a new container to then to iterate over all fire projectiles? Essentially, I don't know how granular my containers should be.

You don't know yet because you are still in the early stage of developing your game. What you are doing right now is "premature optimization". You are trying to guess where the performance bottlenecks of your game could be and fix them before they become a problem. But you can not know yet if those guesses are correct. You might currently spend hours on overengineering a solution which will make further development much more complicated and could in the end just save you <1% of CPU time. Or even none at all because after playtesting you realize that fire projectiles don't really fit into your game and you remove the whole game mechanic.

It is usually a far better strategy to start with a simple solution first, measure the FPS, use a profiler to find the true performance eaters in your code and fix those.

That being said, it can be a good performance optimization to have an object pool in form of an array which keeps the actual data and then a bunch of secondary data structures which keep pointers to that data. If you notice that you spend a lot of CPU time filtering objects by a specific property, create another vector of pointers to game objects which have that property. If you spend a lot of CPU time looking for objects in a specific area, create a spatial hash, Quadtree or similar data structure organized by location.

The cost for this is that all these secondary data structures need to be kept up to date. Not only does this require CPU cycles. You can also encounter a lot of weird bugs when you forget to do that.

Other architectural pattern you might want to look into are the entity - component - system pattern and data oriented design. The idea is that you don't have an object pool for all game objects but in fact a separate object pool for each component of a game object, while complete game objects are composites of different components. But this would be a very different approach than your current object-oriented design.

I am looking forward to playing your game.

\$\endgroup\$
0
\$\begingroup\$

The simplest is a std::vector for each type:

std::vector<Projectile> bullets;
std::vector<ItemEntity> loot;
std::vector<CharEntity> mobs;
//...

If you reserve the upper limit up front then you won't have to worry about reallocations mid frame. If you replace the allocator you can make it assert should it attempt to anyway.

You can remove an object from the vector quickly by swap-with-back-and-pop_back or add an alive flag and reuse a pointer-sized chunk for a free list. Projectiles would use the swap method while smarter objects that need referential stability (the enemies) would use the freelist.

You can often avoid creating a new object type by adding a variable or flag to mark it as one. For example for the fire projectile you would add an effect enum that can be set to NONE, BURN, POISON, MIND_CONTROL, etc. and on collision you would then apply the correct effect with a switch statement.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.