Damage numbers on 100s of enemies - performance question

Hello guys, i hope someone can help with that

*i already have particle system working for hit feedback on enemies, and i manage to maintain the FPS quite good while using a pre-wamred pool of emitters and a cleanup system that cycle thru that pool. So i do have some basic experience with the particle system and the ECS system.

For the spawning of damage numbers of enemies,
I try to find a way to show damage numbers on enemies while keeping performence . 100s of enemies might get hit at a single frame,

In unreal i used niagara Particles with UV pooling, with mass. object pooling the numbers with the GPU itself fed by the cpu mass ECS.

What is the best way to do that in reality kit ? System is very high level, im not sure what is the best approach to begin with to keep good FPS.

even if i need to use AI to write some custom shader, i still need help to design the overall system (should i put the logic inside an ECS system with a dedicated emitter with a custom metal shader for the particles?)

Thank you guys

Hello @just_some_game_dev, thank you for your question!

I can't say this is the absolute best solution for your use case, but have you looked into LowLevelMesh? The basic idea is that you can create a single mesh with detached quads that sit above your enemies. Each quad has UVs calculated so that its material reads from one texture (an atlas) that contains every digit, 0 through 9. See Creating a plane with low-level mesh and Generating interactive geometry with RealityKit for more details about how to build custom meshes at runtime.

A few details that help:

  • Put positions and UVs in separate vertex buffers. UVs are only written once when a number appears, then each frame you only rewrite positions.
  • Give the mesh a fixed capacity and update its parts so only the live quads are drawn.
  • Use a transparent unlit material with depth writes turned off, so overlapping digits don't hide each other.

To have the quads of your LowLevelMesh face the camera, update the positions of the vertices directly in a system, building each quad's corners from the camera's right and up directions. Note that BillboardComponent rotates the whole entity, so it doesn't apply here, because every damage number is part of a single mesh.

MeshInstancesComponent was another option I considered, as it can draw the same mesh many times in one draw call. However, the only thing you can vary per instance is the transform, so you'd need a separate instanced quad for each digit and one instance per digit of each number. LowLevelMesh is more direct.

When it comes to custom Metal shaders, the solution above doesn't require any, because the atlas lookup on the digits texture is done by the vertex data. It may be possible to optimize this further by moving the vertex writing to a compute kernel that runs on the GPU, but I highly recommend starting with a Swift version and only attempting GPU optimizations once you are confident your Swift system has become a bottleneck.

If you'd like this to be more built-in and straightforward, please send us a feature request using Feedback Assistant, describing your use case. See Bug Reporting: How and Why? for tips on creating tickets. If you post the ticket number here, I can make sure it gets seen by the right people.

Thank you!