I'm currently in the process of making a simple RTS style game in c++.
What i'm wondering is how to handle the creation of new units in the game (ie. making marines from the barrack). How would i store these units?
I was thinking of having a class 'unit' which would then be inherited by specific unit types (ie. marines, firebats, etc) but if i create an array for these (ie. Marines myMarines[20]) that will create a hard cap on these units.
How do i create such an array that can be expanded at will? Thank you!
The standard library provides them std::vector
template for dynamically resizable arrays. A std::vector<Marine>
would be the most straightforward alternative to Marines myMarines[20]
.
However, you probably don't want a separate list for each unit type. It is highly likely that you will want to store all units in the same list, regardless of their type. std::vector<Unit>
would sound like the obvious solution but it is not. The problem is that std::vector
stores the objects by value. The following would not work right:
std::vector<Unit> v;
v.push_back(Marine("John Doe"));
The problem is that the Marine
object will be copied into a Unit
object, which is what the vector stores. This kind of copy results in a what is known as slicing: all the Marine specific members will be lost, and only those that exist in Unit
will be stored.
One solution to this problem is to store pointers in the vector because copying pointers does not change the objects they point to. But that brings other problems. To store pointers, this means you'll need to allocate the objects dynamically. And that means that now you are responsible for destroying those objects manually. That's a tiresome and error-prone task.
The solution is to store in the vector objects that destroy the dynamically allocated objects automatically, instead of pointers. These objects are known as smart pointers. The simplest one that exists in the standard library is std::unique_ptr
.
std::vector<std::unique_ptr<Unit>> v;
v.emplace_back(new Marine("John Doe"));
This is a C++11 feature. If your compiler doesn't support it you can find alternatives in the Boost libraries. Boost even includes a container that acts pretty much like a std::vector
of std::unique_ptr
s: boost::ptr_vector
. That would be another alternative.
You will probably benefit from using an std::vector
here. This will allow you to add and remove items at will, and handles dynamic memory allocation internally (without concerning you over the nitty-gritty details!).
Say you want to store a list of marines (denoted by an imaginary class CMarine in the following example):
std::vector<CMarine> marinesList;
Now to add a marine simply do this:
marinesList.push_back( CMarine( <whatever-its-constructor-takes> ) );
To access this marine you can do something like this:
CMarine& marine = marinesList.at( 0 );
marine.someVar = 33;
marine.doMethod();
(I use a reference since CMarine could very well be too bulky to pass around by value efficiently)
You can also loop through all the marines with an iterator like so:
for ( std::vector<CMarine>::iterator _it = marinesList.begin();
_it != marinesList.end(); ++_it );
{
CMarine& marine = *_it;
// Now you can do something with this marine reference
}
UPDATE:
If CMarine
is polymorphic, that is, it inherits from a superclass (maybe something like CUnit
in your case), and you have a 'global' vector of all units - Georg Fritzsche rightly noted that object slicing could occur (if we are storing by value). Instead you might be better off with a vector of CUnit
(smart) pointers like this:
std::vector<std::unique_ptr<CUnit>> unitsList;
// To add a marine:
unitsList.push_back( new CMarine( <whatever-its-constructor-takes> ) );
Read more about vectors here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With