I've recently begun working on a tile-based tactical game, and I have questions regarding a couple of key moments.
Is it feasible to organize the tilemap as an array of containers, which can hold the game objects? What are the pros and cons of such an approach? Should I make a single container class for all the game objects, such as characters, walls, and terrain, or try to break it down to separate container types for every single game object class?
Any examples of a container capable of storing objects of several different classes will be greatly appreciated.
Answer
Is it feasible to organize the tilemap as an array of containers, which can hold the game objects? What are the pros and cons of such an approach?
I would use std::vector
or similar. I don't see the need for an array of containers or for multidimensional containers (unless perhaps you have multiple independent layers of tiles). Even a simple plain array of Tile
objects would work fine, if you must (although I find the use of vectors far more convenient. You can use a single-dimensional container as a 2D one just fine, you simply make it contain width * height
items and to access the tile at (x,y)
use indexing math like container[x + (y * width)]
. Generally one will encapsulate this indexing math behind a tilemap class or some other kind of abstraction.
The reason for preferring a single-dimensional vector is that a multi-dimensional vector gives you no tangible, significant benefit over a single-dimensional one, but a single-dimensional vector provides the additional benefit of keeping the elements all contiguous in memory, resulting in better locality of reference and cache coherency. A vector-of-vectors-style multi-dimensional array does not have this property, nor does the sane form of a dynamically-allocated multi-dimensional plain array (although one with compile-time bounds will, but that is of limited utility in this scenario when you'll presumably want to read your data from a file eventually).
Of course you could achieve that coherency via pooling allocators as well.
Should I make a single container class for all the game objects, such as characters, walls, and terrain, or try to break it down to separate container types for every single game object class?
If every object in your game can be treated exactly the same, then it may be more efficient to create a common base interface for them, and manage them all as a big list of that base interface. As the complexity of your game objects grows, this may be less true and it may be more beneficial to split them out into different lists based on type or other criteria.
As a rule of thumb, if you keep objects that will be processed similarly with each other organizationally, you'll be able to process them more efficiently. "Processing" here includes not just what they do when they Update()
every frame, but also how long they exist in the game world, what their lifetime management policy is, whether or not they must be processed in a certain order, et cetera. Keeping them together may also improve locality of reference and allow you to spread processing out onto multiple threads, which can be a big win for performance.
Any examples of a container capable of storing objects of several different classes will be greatly appreciated.
Almost any container can; it matters more what you store rather than what you store it in. If you take advantage of polymorphism, you can implement a base class or interface
struct Base{
virtual void DoThings() = 0;
};
which everything to be stored together inherits from
struct ChildA : Base {
void DoThings() { /* ... */ }
};
struct ChildB : Base {
void DoThings() { /* ... */ }
};
you can create a std::vector
(as just one example) and store pointers to ChildA
and ChildB
in that vector and treat them uniformly by way of their common interface.
You should also consider that its generally good practice to divorce your rendering and game logic code. The effect of storing different classifications of game object in different ways should have no impact on the organization of your rendering queue; you'd still want that to operate on (essentially) a single list of its own type of object (for example, sprites in a 2D game). The most straightforward way to achieve this is to store a pointer or reference to the renderable component (sprite, or some higher level construct if needed) inside the game logic components.
No comments:
Post a Comment