# How best to structure/manage hundreds of 'in-game' characters

I've been making a simple RTS game, which contains hundreds of characters like Crusader Kings 2, in Unity. For storing them the easiest option would be to use scriptable objects, but that isn't a good solution as you cannot create new ones at runtime.

So I created a C# class called "Character" which contains all data. Everything is working fine, but as the game simulates it constantly creates new characters and kill some characters (as in-game events happen). As the game continuously simulates it creates 1000s of characters. I added a simple check to make sure a character is "Alive" while processing its function. So it helps performance, but I can't remove "Character" if he/she is dead, because I need his/her information while creating the family tree.

Is a list is the best way to save data for my game? Or it will give problems once there are 10000s of a character created? One possible solution is to make another list once the list reaches a certain amount and move all dead characters in them.

• One thing I've done in the past when I need a subset of a character's data after its demise is to create a "tombstone" object for that character. The tombstone can carry the information I need to look up later, but it can be smaller and iterated less often because it doesn't need constant simulation like a living character. – DMGregory Oct 10 at 12:28
• Is the game like CK2, or is it only the part about having a lot of characters? I understood it as the whole game being like CK2. In that case a lot of the answers here are not incorrect and contain good know-how, but they miss the point of the question. It doesn't help that you called CK2 a real-time strategy game when it actually is a grand strategy game. That might seem nitpicky, but it's very relevant to the issues you're facing. – R. Schmitz Oct 11 at 10:24
• For example, when you mention "1000s of characters", people are thinking of 1000s of 3D models or sprites on the screen at the same time - so, in Unity, 1000s of GameObjects. In CK2, the maximum amount of characters I saw at the same time was when I looked at my court and saw 10-15 people there (I didn't play very far though). Just as well, an army with 3000 soldiers is only one GameObject, displaying the number "3000". – R. Schmitz Oct 11 at 10:25
• @R.Schmitz Yes I should have made that part clear every Character does not have gameobject attach to them. Whenever necessary like moving character from one point to other. A separate entity is created which contains all information of that Character with Ai logic. – paul p Oct 11 at 11:35

There are three things you should consider:

1. Does it actually cause a performance problem? 1000s is, well, not many actually. Modern computers are awfully fast and can handle a lot of stuff. Monitor how much time character processing is taking and see whether it's actually going to cause a problem before worrying too much about it.

2. Fidelity of currently minimally active characters. A frequent mistake of beginner Game Programmers is to obsess over precisely updating off-screen characters in the same way as on-screen ones. This is a mistake, no-one cares. Instead you need to seek to create the impression that off-screen characters are still acting. By reducing the amount of update characters that are off-screen receive you can dramatically decrease processing times.

3. Consider Data-Oriented-Design. Instead of having 1000 character objects and calling the same function for each, have an array of the data for the 1000 characters and have one function loop over the 1000 characters updating each in turn. This kind of optimisation can dramatically improve performance.

• Entity / Component / System works well for this. Build each "system" for what you need, have it keep the thousands or tens-of-thousands of characters (their components), and provide the "character ID" to the system. This allows you to keep the different data-models separate, and smaller, and you can also remove dead characters from systems that don't need them. (You can also completely unload a system, if you're not using it at the moment.) – 202_accepted Oct 10 at 19:58
• By reducing the amount of update characters that are off-screen receive you can dramatically increase processing times. Dont you mean decrease? – Tejas Kale Oct 11 at 6:18
• @TejasKale: Yes, corrected. – Jack Aidley Oct 11 at 8:40
• A thousand characters isn't a lot, but when each of them is constantly checking to see if they can castrate each of the others it does start to have a major impact on overall performance... – curiousdannii Oct 11 at 10:58
• Always best to actually check, but it is generally a safe working assumption that Romans are gonna want to castrate each other ;) – curiousdannii Oct 11 at 12:13

In this situation, I'd suggest using Composition:

The principle that classes should achieve polymorphic behavior and code reuse by their composition (by containing instances of other classes that implement the desired functionality)

In this case, it sounds like your Character class has become god-like, and contains all the details for how a character operates at all stages of it's lifecycle.

For example, you note that the dead characters are still required - as they're used in the family trees. However, it's unlikely that all the information and functionality of your alive characters, is still needed just to display them in a family tree. They may for example, simply need names, date of birth and a portrait icon.

The solution, is to split the separate parts of your Character into sub-classes, that the Character owns an instance of. For example:

• CharacterInfo may be a simple data-structure with the name, date of birth, date of death and faction,

• Equipment may have all the items your character has, or their current assets. It may also have the logic that manages these in functions.

• CharacterAI or CharacterController may have all the information needed about the character's current goal, their utility functions etc. And it may also have the actual update-logic that co-ordinates the decision making/interaction between it's individual parts.

Once you've split the character up, you no longer need to check an Alive/Dead flag in the update loop.

Instead, you'd simply make an AliveCharacterObject that has the CharacterController, CharacterEquipment and CharacterInfo scripts attached. To "kill" the character, you simply remove the parts that are no longer relevant (such as the CharacterController) - it will now not waste memory, or processing time.

Note, how the CharacterInfo is likely the only data actually needed for the family tree. By decomposing your classes into smaller pieces of functionality - you can more easily keep that small object of data after death, without needing to keep the entire AI-driven character.

It's worth mentioning that this paradigm is the one Unity was built to use - and is why it handles things with lots of separate scripts. Building large god-objects is rarely the best way to handle your data in Unity.

When you have a large amount of data to handle and not every data-point is represented by an actual game object, then it is usually not a bad idea to forego Unity-specific classes and just go with plain old C# objects. That way you minimize overhead. So you seem to be on the right track here.

Storing all characters, living or dead, in one List (or array) can be useful because the index in that list can serve as a canonical character ID. Accessing a list position by index is a very fast operation. But it might be useful to keep a separate list of the IDs of all living characters, because you will likely need to iterate those far more often than you will need the dead characters.

As your implementation of your game mechanics makes progress, you might also want to look at what other kind of searches you perform the most. Like "all living characters in a specific location" or "all living or dead ancestors of a specific character". It might be beneficial to create some more secondary data-structures optimized for these kinds of queries. Just remember that each of them must be kept up-to-date. This requires additional programming and will be a source of additional bugs. So only do it if you expect a notable performance increase.

CKII "prunes" characters from its database when it deems them as unimportant to save resources. If your pile of dead characters consumes too many resources in a long-running game, then you might want to do something similar (I don't want to call this "garbage collection". Maybe "respectful incremator"?).

If you actually have a game object for every character in the game, then the new Unity ECS and Jobs system might be useful to you. It is optimized for handling a large number of very similar game objects in a performant way. But it forces your software architecture into some very rigid patterns.

By the way, I really like CKII and the way it simulates a world with thousands of unique AI-controlled characters, so I am looking forward to playing your take on the genre.

• Hello, Thanks for the reply. All calculations are made by One single GameObject Manager. I am only assigning game objects to Individual Actors when necessary(like showing Army of Character moves from one position to other). – paul p Oct 11 at 3:11
• Like "all living characters in a specific location" or "all living or dead ancestors of a specific character". It might be beneficial to create some more secondary data-structures optimized for these kinds of queries. From my experience with CK2 modding, this is close to how CK2 handles the data. CK2 seems to use indexes that are essentially basic database indexes, which make it faster to find characters for a specific situation. Instead of having a list of characters, it seems to have an internal database of characters, with all the drawbacks and benefits that entails. – Morfildur Oct 11 at 8:48

You don't need to be simulating/updating thousands of characters when only a few are near the player. You only need to update what the player can actually see at the current point in time, so characters that are further away from the player should be suspended until the player is closer to them.

If this doesn't work because your game mechanics require distant characters to show passage of time, you can update them in one "big" update when the player gets closer. If your game mechanics require each character to actually respond to in-game events as they happen, no matter where the character is in relation to the player or the event, then it might work to reduce the frequency at which characters further from the player are updated (i.e. they are still updated in sync with the rest of the game, but not as often, so there will be a slight delay before distant characters respond to an event but this is unlikely to cause a problem or even be noticed by the player). Alternatively you might want to use a hybrid approach, such as only updating nearby characters in response to an event that would affect them but letting characters far from both the player and the event remain suspended.

• it's an RTS. We should assume that at any given time, a considerable number of units is actually on screen. – Tom Oct 11 at 7:17
• In an RTS the world needs to continue while the player is not looking. A big update would take just as long but would be in a big burst when you move the camera. – PStag Oct 11 at 22:52

# Clarification of the question

I've been making a simple RTS game, which contains hundreds of characters like Crusader Kings 2 in a Unity.

In this answer, I am assuming that the whole game is supposed to be like CK2, instead of only the part about having a lot of characters. Everything you see on the screen in CK2 is easy to do and will neither endanger your performance nor be complicated to implement in Unity. The data behind it, that's where it gets complex.

# No-functionality Character classes

So I created a C# class called "Character" which contains all data.

Good, because a character in your game is just data. What you see on the screen is just a representation of that data. These Character classes are the very heart of the game and as such are in danger of becoming "god objects". So I would advise extreme measures against that: Remove all functionality from those classes. A method GetFullName() that combines the first and last name, OK, but no code that actually "does something". Put that code into dedicated classes that do one action; e.g. a class Birther with a method Character CreateCharacter(Character father, Character mother) will turn out much cleaner than having that functionality in the Character class.

# Don't store data in code

For storing them the easiest option would be to use scriptable objects

No. Store them in JSON format, using Unity's JsonUtility. With those no-functionality Character classes, it should be trivial to do. That will work for the initial setup of the game as well as for storing it in savegames. However, it's still a boring thing to do, so I just gave the easiest option in your situation. You could also use XML or YAML or any format really, as long as it can still be read by humans when it's stored in a text file. CK2 does the same, actually most games do. It's also excellent setup for allowing people to mod your game, but that's a thought for much later.

# Think abstract

I added a simple check to make sure character is "Alive" while processing [...] but I can't remove "Character" if he is dead because I need his info while creating the family tree.

This one is easier said than done, because it often collides with the natural way of thinking. You're thinking in a "natural" way, of a "character". However, in terms of your game, it seems like there are at least 2 different types of data that "is a character": I'll call it ActingCharacter and FamilyTreeEntry. A dead character's FamilyTreeEntry doesn't need to be updated and probably needs a whole lot less data than an active ActingCharacter.

I'm going to speak from a little bit of experience, going from a rigid OO-design to an Entity-Component-System (ECS) design.

A while back I was just like you, I had a bunch of different types of things that had similar properties and I built out various objects and tried to use inheritance to solve it. A very smart person told me don't do that, and instead, use Entity-Component-System.

Now, ECS is a big concept, and it's tough to get right. There's a lot of work that goes into it, properly building entities, components, and systems. Before we can do that, though, we need to define the terms.

1. Entity: this is the thing, the player, animal, NPC, whatever. It's a thing that needs components attached to it.
2. Component: this is the attribute or property, such as a "Name" or "Age", or "Parents", in your case.
3. System: this is the logic behind a component, or a behaviour. Typically, you build one system per component, but that's not always possible. Additionally, sometimes systems need to influence other systems.

So, here's where I would go with this:

First and foremost, create an ID for your characters. An int, Guid, whatever you like. This is the "Entity".

Second, start thinking about the different behaviours you have going on. Things like the "Family Tree" — that's a behaviour. Instead of modeling that as attributes on the entity, build a system that holds all that information. The system can then decide what to do with it.

Likewise, we want to build a system for "Is the character alive or dead?" This is one of the most important systems in your design, because it influences all the others. Some systems can delete the "dead" characters (such as the "sprite" system), other systems can internally re-arrange things to better support the new status.

You'll build out a "Sprite" or "Drawing" or "Rendering" system, for example. This system will have the responsibility of determining what sprite the character needs to be displayed with, and how to display it. Then, when a character dies, remove them.

Additionally, an "AI" system that can tell a character what to do, where to go, etc. This should interact with many of the other systems, and make decisions based on them. Again, dead characters can probably be removed from this system, since they're not really doing anything anymore.

Your "Name" system and "Family Tree" system should probably keep the character (alive or dead) in memory. This system needs to recall that info, regardless of what the state of the character is. (Jim is still Jim, even after we bury him.)

This also gives you the benefit of changing when a system reacts more efficiently: the system has it's own timer. Some systems need to fire rapidly, some don't. This is where we start getting into what makes a game run efficiently. We don't need to recalculate the weather every millisecond, we can probably do that every 5 or so.

It also gives you more creative leverage: you can build a "Pathfinder" system that can handle the calculation of a path from A-to-B, and can update as-necessary, allowing the Movement system to say "where do I need to go next?" We can now fully separate these concerns, and reason about them more effectively. Movement doesn't need to find the path, it just needs to get you there.

You'll want to expose some parts of a system to the outside. In your Pathfinder system you'll probably want a Vector2 NextPosition(int entity). This way, you can keep those elements in tightly-controlled arrays or lists. You can use smaller, struct types, which can help you keep components in smaller, contiguous memory blocks, which can make system updates much faster. (Especially if external influences to a system are minimal, now it only needs to care about it's internal state, such as Name.)

But, and I cannot stress this enough, now an Entity is just an ID, including tiles, objects, etc. If an entity doesn't belong to a system, then the system won't track it. This means we can create our "Tree" objects, store them in the Sprite and Movement systems (the trees won't move, but they have a "Position" component), and keep them out of the other systems. We no longer need a special list for trees, since rendering a tree is no different than a character, aside from paperdolling. (Which the Sprite system can control, or the Paperdoll system can control.) Now our NextPosition can be slightly rewritten: Vector2? NextPosition(int entity), and it can return a null position for entities it doesn't care about. We also apply this to our NameSystem.GetName(int entity), it returns null for trees and rocks.

I'll draw this to a close, but the idea here is to give you some background on ECS, and how you can really leverage it to give you a better design on your game. You can increase performance, decouple unrelated elements, and keep things in a more organized fashion. (This also pairs well with functional languages / setups, like F# and LINQ, which I highly recommend checking out F# if you haven't already, it pairs very well with C# when you use them in conjunction.)

• Hello, Thanks for such a detail response. I am only using One GameObject Manager which contains the reference to all other In-Game Character. – paul p Oct 11 at 3:16
• Developing in Unity revolves around entities called GameObject that don't do much but have a list of Component classes which do the actual work. There was a paradigm shift concerning ECSs roundabout a decade ago, as putting the acting code in separate system classes is cleaner. Unity recently implemented such a system, too, but their GameObject system is and always was very much an ECS. OP is already using an ECS. – R. Schmitz Oct 17 at 9:18

As you are doing this in Unity, the easiest approach is this:

• create one game object per character or unit type
• save these as prefabs
• you can then instantiate a prefab whenever needed
• when a character is killed, destroy the gameobject and it won't take up any CPU or memory anymore

In your code, you can keep references to the objects in something like a List to save you from using Find() and its variations all the time. You are trading CPU cycles for memory, but a list of pointers is pretty small so even with a few thousand objects in it that should not be much of a problem.

As you progress through your game, you will find that having individual game objects gives you tons of advantages, including navigation and AI.