Managing many individuals (Rendering and AI)

Greetings !

You may have guessed what my problem is from the title, but first of all let me give you some context so you understand my needs better.

I am a student in IT who’s about to start a one year long student project with 4 friends of mine who are also students. We are going to make a game, and it’s a pretty ambitious project to be honest : multiplayer might be where we go too far given our skill and timeframe. But no matter.

The game is about players fighting in 1v1 matches that can last between 10 and 20 minutes. Players will be able to summon soldiers to help them control key areas and will also control a “main” character directly, so it’s not an RTS (meaning there are no “squads” or “group” mechanics, each soldier is a singular entity, at least gameplay wise). Neutral soldiers will also be spawned as a “game ender”.

This means there will surely be MANY soldiers, probably in the hundreds, on screen towards the end of the game, and given it is a multiplayer game based on reflexes a lot… we cannot afford to have any performance problems or lag.

After making a simple prototype that simulates 1200 soldiers in real time, I ran two tests :

  • One in which the soldiers were Idling (their AI was not turned on), so obviously this was mainly to test how Unity and my computer (I have to take that into account, my computer is very good and I think quite a bit higher than average, at least in RAM and GPU, GPU is an intel core i7 with 4Gz frequency). The game was smooth at 70 FPS, but I expect rendering to be much more expensive later, as there will be more complex animations, particle effects, lots of spawning and despawning…

  • One in which the soldiers had their AI turned on, and fought a 600 vs 600 battle. Now my targeting system was being a very big pain in the buttox : I use a coroutine so I believe it does limit the impact it has on FPS but still, the game was running at about 30 FPS when all the soldiers were alive, and then gradually went up as both sides started getting losses.

The AI of a soldier is a simple FSM where they either “Stand by”, just going to a random point and then generating another random point upon reaching the current one, or “Fight”, going towards their target until in attack range, and then proceed to attack the target if this soldier is not currently being struck at.

Each AI is always searching through the entire array of soldiers on the map, so that must be most of the processing power is going (at least the profiler says so), also each soldier is constantly raycasting forward to make sure they do not go through another soldier. The terrain is flat so I didn’t give them any physics, just force them to always stay at a certain level. If raycasting isn’t too costly I might use one to make them ‘stick’ to the terrain bellow them.

So, here’s my problem. Long story short, I’m trying to figure out how to render and manage many individual soldiers, so that your average gamer PC could handle at least a thousand soldiers in real time each having at the very least a few hundred faces.

For the rendering part, I’ve explored and searched a bit and stumbled upon videos like this one :

and also a thread he made about how he achieves that.

The thing is, he’s not very precise as to how it all works, but I’ve picked a few things up.

So now I’m trying to learn how to use Graphics.Drawmesh instead of using Gameobjects for each soldier, but I’m still trying to figure out how I can use it to render animated objects without having to store tens of models representing frames of each animation. Also that means I won’t be able to use mecanim, which is going to make things even more difficult.

But the problem I’m foreseeing with this is, how on earth am I going to allow players to target soldiers with spells if I can’t use raycasts ? Since there won’t be any soldier gameobjects, there also won’t be any colliders associated.

All in all, I’m pretty confused and probably trying to solve an above-my-league problem as I often do. Nevertheless, I’d greatly appreciate if one of you could give me a hand.

Oh, and feel free to tell me that this is beyond what a bunch of students can do in a year. I think I already know but it’ll be clearer if someone with experience tells me :wink:

Thanks for reading that wall-o-text. Cheers !

Hello Hoshiqua,

Each AI is always searching through the entire array of soldiers on the map

Have each AI only do heavier decision making processes every Nth frame. For say 60 AIs you could just process one AI per frame so each makes decisions at 1Hz rate. for more you may want 2 or 3 AIs doing their processing per frame. Per frame each AI should only do extremely simple optimised stuff, like move toward a target point. To accomplish this hold your enemy gameobjects in a list that you step through per frame and then call like loEnemy[iIndex].DoDecisionMaking(). The fast per frame stuff… keep vars cached so like onUpdate for each enemy only does:

fT = Time.deltaTimeK
transformEnemy.Translate(fDX
fT, 0, fDY*fT, Space.world)

You set its heading orientation and calculate fDX,fDY during decision process, you store transformEnemy during onStart.

Do not create anything on the stack during onUpdate, not even a float or an int, cache everything.

how on earth am I going to allow players to target soldiers with spells if I can’t use raycasts ?

Child a very simple collider mesh (box) around each soldier with its MeshRenderer disabled. You can even still achieve accurate hit detection with this: Raycast against the coarse box, if hit then do second raycast againt single hires enemy mesh (enemyX) way below your map out of view. This single hires enemy mesh collider is shared for all real enemies. Transform your ray vector from enemySpace to enemyXSpace to accomplish this. I used this technique to get accurate hit detection on mobile game. You can even do it against animated models: you need to have an array of prebaked meshes out of view and look them up depending on real enemies animation cursor pos.

Also: Use aggressive LODing, and if you want animation consider model swapping in a low LOD model with a very low bone skeleton, no point in animating hands at over 10m away.

Assuming constant camera height during game and the fact you are running all enemies over XZ plane: Optimum solution would be to bake out a large atlas of sprites with anim loops for like 16 headings relative to camera for distant enemies, render with cutout shader. You could accomplish this automatically by building a script tool that manipulates model and steps through animations as you render a cam to a texture atlas. As you’re on desktop you can afford a few 8192^2 textures for this. If you use fairly overhead global lighting and put eyecandy in with emmisive maps you can get away with this trick until enemy gets quite close.

Oh and use alpha textured quad shadows until you get close, and then swap for softer real shadows if you really must up close.

I can’t use sprites because the game is in third person, but otherwise thanks a lot for your help.
I’ve thought about something that would allow me to not have to make LOD models and animations for each enemy model, as it wouldn’t be very useful anyway as each NPC has a pretty simple skeleton (one for the body, one for the head, one for the weapon and one for the shield) : For each mesh “type” I keep one GameObject with the SkinedMeshRenderer going and also an animation component. On demand each NPC can ask for what ‘his’ mesh looks like at a certain frame of an animation. This way I could have enemies update their mesh with animation deform every every say, 10 frames instead of every frame : that luxury would be for close up NPCs which would be managed by the regular Unity system anyway.

Thanks a lot again ! If anyone else has anything to say about this, feel free, I could use all the help and insight I can get :slight_smile:

1 Like

Why is it searching through the entire array? This means the cost of a query increases with the number of AI agents on screen.

This creates an exponential O(n^2) growth in cost as a new agent is added since not only do you have N+1 agents to process, but every other agent is concerned with N+1 agents.

Why would an agent be concerned about an agent on the other side of the map? Rather instead it should only be concerned about the agents near it.

There’s various ways to sort out your agents so that you can efficiently find near agents and only deal with them. The information needed though will depend on how you’d sort such a large set… so what is it you’re searching through the entire array for?

Well, basically I just use GameObject.FindObjectsOfType(NPCClass) and then I check that they’re close enough and a valid target. Then if the distance to that target is shorter than the shortest distance found so far, it’s set as the new target. I had thought about how I could sort them, but I can’t get my head around how exactly I could do it. Mind to elaborate ?

DO NOT use GameObject.FindObjectsOfType… that is super slow. Especially if you’re doing it every time the agent ‘thinks’.

For 1, it creates a new array that needs to be garbage collected. And furthermore it’s just a slow method in general with unity having to go through all the objects and finding your object of type.

Usually what I do if I need a quick reference to all objects of a type is I create a static collection in the script, and on Awake I add it to the collection, and ondestroy I remove it.

Then you can just directly access that static collection.

Personally I use a HashSet for this because it’s O(1) operation to add/remove/contains.

Now as for having a collection that can easily find agents nearby.

dynamic bsp trees are a little tough… so you could fake it.

Lets say we know we have a statically sized map, and we split that map into sections. Then we can have while an agent moves around… and it notices it has moved to a new section of the screen, it updates the set for each section that it’s left one and entered another (if its a grid, this is a simple division test)… as well as remembering what section of the map it’s in currently.

Then when it finds nearby entities… it just looks at the section it’s in… ignoring all other sections, because well… those sections don’t matter, they’re not near the agent anyways.

This is an extremely interesting topic, and I actually posted this earlier(slightly different, mine was achieving hundreds of animations on mobile): Playing hundreds of animations on mobile - Unity Engine - Unity Discussions

Eventually I settled on an interesting tactic. Since my main drag was Skinned Mesh Renderers, I decided that I needed to use static meshes. First I created a script to convert animations to static meshes (I’ll revise the code and add an explanation tomorrow, but that’s the jist of it). Then I created a script to cycle through those static meshes to give the illusion that they were animated meshes. For this I needed a cheap method to enable-disable meshes multiple times a second, which I found through painful trial-and-error. This let me dramatically expand the amount of animated objects I could show in my mobile project. Finally, I found this handy function that lets me disable mesh renderers that aren’t seen by the camera. Note that the Editor camera will also trigger it, but by looking in the Profiler I can tell it works.

I think that overly aggressive LOD, nitpicking scripts and smart occlusion culling is the way to go. Perhaps even have completely static meshes after a certain distance. I’d focus on optimizing the skinned meshes over scripts, but that advice is only from my (fairly limited) personal experience.

You can tell that game is designed to not render objects not seen by the camera. At the start of the video, it’s pushing 50-60FPS when looking at the buildings, but once he moves into battle it drops to 20. I’ve found that disabling objects that aren’t seen by the camera is one of the most effective techniques to squeeze more FPS. However, when he zooms out at the end, he gets over 30FPS even though he’s seeing all the soldiers. This shows that the developer was so aggressive in LOD that showing all the soldiers actually increased the FPS.

Also, let’s note that the video was made on a Nvidia GTX 980Ti that the gamer overclocked the s*** out of. I feel sorry for the card when I see so many “bright spot” artifacts from extreme overclocking.

You guys rock !

I think I can figure out how to convert animations to static meshes, given the animation, the bone deformation “map” on the mesh and the bone data of the said animation, should be easy enough actually. However I’ve never done that before so I will shamelessly look at your work :stuck_out_tongue:

As for the targetting, I thought about a “cell” system exactly like the one you described, lord, but never bothered testing it. It would work very well with our game actually, since the maps are of a fixed size and usually rectangle shaped.
However, what happens if a NPC is on the border of a section ? Shouldn’t it at least check the neighbouring sections as well ?

And thanks for sharing this knowledge about GameObject.GetObjectsOfType. I knew I should have been wary about it, looked too easy.

If anyone else has any insight to add, I STILL need all the help I can get. This forum is heaven :smile:

Yeah, you can do that.

Another option is that you can overlap the areas… so that it removes after significantly leaving a region, but adds immediately entering a region.

The amount that is ‘significant’ is your buffer region.