The games has many instances of the same kind of entity but each instace can have unique variable settings, position, components and stuff.
How do I add many instances of the same Entity?
In other words, something like a prefab where we use a base entity definition to spawn instances.
The most common and simplest method is to create an Entity Factory (which is probably a State System, though an App System or Singleton could also work) which provides methods for creating different Entity types. Variable settings can simple be provided as parameters. Depending on the size of your game you may want to have separate Entity Factories for different types of entity. For example:
class EnemyEntityFactory final : public CSCore::StateSystem
CSCore::EntityUPtr CreateSoldier(const CSCore::Colour& in_uniformColour,
f32 in_health, Gun in_gun) const;
CSCore::EntityUPtr CreateElite(const CSCore::Colour& in_uniformColour,
f32 in_health, Gun in_mainGun, Gun in_secondaryGun) const;
CSCore::EntityUPtr CreateHeavy(const CSCore::Colour& in_uniformColour,
f32 in_health, f32 in_armour, Gun in_gun) const;
CSCore::EntityUPtr CreateTank(const CSCore::Colour& in_tankColour,
f32 in_armour) const;
For every new soldier that you now want you can just call CreateSoldier with the appropriate parameters. The CSPong example project has a more complete example of this.
You could even make this data driven by also providing a generic CreateEnemy method which takes Json as a parameter. The json could specify the type of enemy allowing CreateEnemy to relay the given parameters on to the correct factory method:
"UniformColour": "0.0 1.0 0.0 1.0",
The Factory method is good for many circumstances, but it isn't always the best solution. Sometimes you don't want to directly expose instantiation of objects. An example of this may be bullets: there are a lot of them and they are short lived so it wouldn't be very efficient to instantiate them on the fly. Instead a system could create a pool of bullets up front and expose these:
class BulletManager final : public CSCore::StateSystem
void OnInit() override;
CSCore::EntityUPtr CreateBullet() const;
const u32 k_maxBullets = 100;
u32 m_currentIndex = 0;
std::array<CSCore::EntitySPtr, k_maxBullets> m_bulletPool;
CSCore::EntitySPtr next = m_bulletPool[m_currentIndex];
if (m_currentIndex >= k_maxBullets)
m_currentIndex = 0;
for (auto& bullet : m_bulletPool)
bullet = CreateBullet();
//Create and return bullet entity
This is a pretty basic example (you would probably want to have some way of ensuring that bullets weren't already in use) but hopefully it demonstrates the general idea.
Hope that helps!