Optimizations of bullets, houses and belts :-)

Hello there,

I am able to draw many buildings with mesh instancing, I am able to move reasonable amounts of bullets (2000) with actors using traces in front of them instead of collisions, but I don’t know where to go from there:

Will it help, for example, if I were to store some array of vectors that would represent bullet positions, and a separate array of bullet velocities, and a separate array of their direction? If so, why?

How should I think about/design things, that need to work with a larger scale? I don’t want to do the next Factorio or CIties skylines, but few hundreds of actors are really limiting for everything, so I am looking for like general concepts, for things like:

  • 100000 “AI” actors/buildings in Cities Skylines
  • thousands of bullets fired from a WW2 fighter plane
  • thousands of items on belts in Factorio
  • armies in the Total War series

What are the general principles of games with these kinds of scales?

I’m asking here because I went through a significant amount of tutorials and I am always left wanting for more. My own tryouts are slowly getting me there, but they are really time-consuming. Typical strategy game tutorial focuses on camera and a basic game loop, but ends before any scale is reached - this applies pretty much for everything and I don’t know where to look for more. What are the basic concepts needed for say Total War armies? What are the basic programming principles that make Factorio possible?

PS: I know that I won’t make the next Factorio, but I would like to be able to move low thousands of “actors”, instead of low hundreds.

PPS: Right now, I am pretty much just prototyping with blueprints, but I assumed that programmers would be focused here, so I am asking here.

EDIT with ‘example’: If you were to build your own Cities Skylines, you could easily do that with like 100 buildings and 100 players, but you would set yourself up with some hard limits that way. What would be the basics of your design so that you would be able to grow to their numbers? Where, how and why would you leave the basic actors behind?

A few years back, I was toying with OpenGl and It was fun to just optimize my forest to be big. (In the end, I’ve had chunks of the world, each held its own part of the forest and the LOD was implemented as reducing the number of instances as you were further away (so the forest was less dense) The tree was super simple, but you can see how far away you actually still see it: Imgur: The magic of the Internet. That terrain was also procedurally generated on the fly, along with the textures (which is part of why the ground is so ugly :smiley: ). So yeah, I like toying with the optimizations, but the thing is:

It was easy with plain CPP and OpenGL, whereas Unreal has its own way of doing things and its own optimizations in place. I could just reproduce this forest in Unreal, but I would prohibit myself from using basically anything that is already there, just using Unreal as a renderer.

If I use the flying bullets as an example:

  • I can spawn group of bullets instead of one bullet (say salvo from a wing of a plane could be 4 bullets next to each other with one position)
  • I can (maybe?) spawn particle with the same velocity vector and just kill that particle when there is a hit and otherwise I wouldn’t need to communicate with the GPU while the bullet just travels through the air.
  • I can just hold the array of the positions and the velocities of the bullets so that I can update their positions and check for hits in a more optimized manner.
  • I can maybe check for collisions in some super optimized way (I would expect that this, at the very least, Unreal does in some way - like using distance for the first check and using more detailed algorithms only when in the range…)
  • I can have bullet pools and in/active switch, so that I don’t need to create/destroy objects.

But now, I am using some singleton bullet manager instead of bullet actors, I am using my own collision handling, I am doing my own view frustum culling with instancing, I am using my own solution instead of the provided garbage collector… What the Unreal engine does at that point? Should I really sidestep it “so much” in performance heavy places, or am I just ignoring how to use it properly?

This is my reason for posting this. Is this the right way? Should I have a collection of actors, or a collection of position transforms, or just a collection of vectors, or just a collection of floats where I know that a group of three is a vector? How do I find the performance differences between these states? If I disable drawing, physics, etc. of an Actor, why is his velocity update still way slower than just a custom collection of velocities?

Unreal is a multi purpose engine build for “small” levels (not open world). You can’t produce a engine that does everything perfect. You can’t even provide a single component that does everything perfect (related to what it can do), cause if it can do everything, there are a lots of conditionals that reduce performance.

If you need performance, you need to know the specifics of what you want to do and then can optimize in that direction. E.g. for a 2D game a transform could look like this

struct FTransform2D
  FVector2D location;
  float rotation;
  float scale;  // Assuming uniform scale

With that every transform operation is a lot cheaper than using unreals 3D transform with arbitrary axis scale and 3D rotation.

With all the possible render options unreal provides, you might move much more data around and process it than you actually need.

We develop for VR currently and using a GTX 1080 graphics card. We found that 500 draw calls (like 500 static mesh components with cube that only contains one material) is our draw call limit,
not modifying any render code. Though we dynamically create objects at runtime and depending on the size of the objects (vertex/triangle count) we reached 20.000 objects by merging objects with the same material together (by utilizing a custom version of RuntimeMeshComponent and some additional classes). In case of a city builder like cities skyline, I would do something similar. Like: Subdivide the world into grid tiles, and per grid tile merge buildings together. This might require a special material setup to merge buildings of different type/evolution state.

Already thought about how this conveyer stuff works (Satisfactory, which is build with Unreal). But they definitly have some custom stuff going. Objects on a belt could just be arrays of floats determining the distance on the belt, including a index or something to determin the object type. The belt movement could be processed in parallel using the ParallelFor function of Unreal to run code threaded (belts do not interfer each other, though there should be no problem), but syncronously. In case of Satisfactory you need to handle how much a connected factory consumes and if it can consume (internal item stack full), but should be doable. Would not assume that they actually have collision for the objects on the conveyor belt. Picking stuff from the belt can be done by determining the distance on the spline (belt) the player is looking at and then evaluate the value with the item array for that belt and see if there is a item in range of the look-at-distance (float distance computation in a sorted array -> cheap). Pass the belt arrays to the render thread and batch render the belt items (don’t ask me how that works).

In case of animation you want to look into computing an animation once and use it for multiple skeletal actors. Means multiple actors have the exact same animation. In a battle you might want to split the actors with the same animation across the battlefield to make it look more dynamic. I think Unreal did something in that direction already (never worked with skeletal actors). And if they have the exact same animation state, you might also batch render them like a instanced static mesh component. Meshes for Actors in a game like TotalWar are not so much detailed. If you cut blend weights on vertices and stuff like that, animation computation should be pretty cheap as well. No clue how they deal with damaging the correct unit in case of attack, but probably a spatial grid system to cut of lookup cost. By distributing animation evenly you also split up the number of lookups for what unit needs to be damaged (only units that reach the damage state of the animation need to compute the lookup). Or you define the target before attacking, which would actually be the better case (still using spatial grid to find a suitable target).

Thank you very much. So if I were to spawn thousands of bullets (say artillery barrage), the best way would be to ignore actors altogether, create some dedicated structs(FVector3D location, FVector3D velocity), create some optimized lists of them (say linked list), calculate their next position (ideally with threads, maybe things like SIMD, etc) and just give to the Unreal the outcome (say create an instanced mesh and use this for instance transformations)?

Is there some glaring mistake that I would make? Isn’t a singleton-like bullet manager a bad code practice? I am imagining it now as a gun, that asks for some singleton bullet handler to create bullet for it and maybe tell the gun if a bullet hits something.

I sometimes hear people say that singletons are ■■■■ and you should not make stuff like that. I don’t realy know whats their problem, I love them and it clearly states their shall only be this one instance. There might be some problems, e.g. if you want to subclass the manager and exchange the singleton with that subclass can be a bit tricky/impossible.

What you definitly need to take care of is creating new objects. That is pretty expensive cause of all the registration stuff that is going on. Spawning every bullet anew, also means lots of garbage to be collected, which also consumes lots of ressources. What you definitely need is a Object Pool if you still want to use components/actors. You can create that on you own, or use this: Object Pool Plugin in Code Plugins - UE Marketplace (I gues that is what you ment by using a manager)

But yeah, you can also just keep an array or something with bullet information and use a InstancedStaticMeshComponent where you update the instances based on your array or use some custom render code similar to InstancedStaticMeshComponent. You definitely want to do that in C++. Looping over thousands of instance data in Blueprint will crush your performance.

I wish so badly I knew what you guys were talking about lol. I am trying to optimize my First Person Shooter by instancing bullet projectiles, which is how I got here. Unfortunately, if you have already answered my question, it is way over my head and beyond my skill level at this point.

it seems unlikely to me that a FPS game would have so many bullets flying around that it impacts performance to any significant degree?

If you have a singler player game, this probably does not matter if you stick to Rifles/Mgs. But if you have some hardcore guns that fire 1000 bullets a second, it definitely matters.

If you work on a Battlefield with 64players, you realy should thing about what you are doing.

Honestly, if your design requires that you actually simulate firing 1000 bullets a second, with accurate time and physics simulations, you more likely have a design problem. Can you achieve the goal with 1/2 that, 1/4 that, 1/10th that? probably. At the very least, I’d seriously question it. You can’t realistically expect even 1000 hitscans per second firing with infinite range to function, you’ve got to have sane limitations somewhere.

Of course, if you’re going to jam 64-100 or more players into a small area, you have rather specialized circumstances for which your game server is going to need to deal with things differently. Like PUBG needs to do some very, very, very different things to be able to do what they do in a way that I would consider acceptable. That said, it still basically comes down to deciding what the clients need to know about … and the server’s just going to have to be big enough to deal with it.

To address the more specific problem of “having a whole lot of bullet projectiles”, you’ve got many different pieces of that that you can address – if your problem is in spawning a lot of things, you use an object pool to address the spawn rate problem. If your problem is in simulating the physics on those things, you need to simplify the physics. If your problem is all of the above, and you’ve just got far too many things happening, you have to question your design. Of course, multiplayer adds significantly more mess to that.