Efficient way to have 60k UI elements in scene

I have a huge talent tree in my game, total around 10k nodes. Each node contains the following:

  1. Icon object
  2. Frame object
  3. Border object (to highlight when selected etc)
  4. Text object
  5. Question mark object which shows up when selected
  6. Each node is connected by at least 1 line object
  7. Animator on main object which gets disabled once animation stops playing
    Below is example how it looks like:

Zoom out

Even more zoom out:

I am showing only those objects that are within screen borders by checking whether rectangle contains position of given node, I do it for every node per frame and enable/disable the object as well as their line if it’s connected.
The problem is, when I am panning the sphere grid (yes, this is mobile game) on mid-range phone, the FPS drops from 120 to 60 and the reason is that somehow disabled objects are still consuming performance. Disabling the dynamic culling mentioned above does not make difference and is not the cause of this drop. I tried deleting all nodes outside screen view and the performance improved drastically (constant 120fps even when panning).

  1. Why does it make such a huge difference and why having inactive objects give such a huge drop when panning the screen? Is it because their position is updated regardless if they are disabled or no?
  2. Given what I described, do you think there is a way to optimize it further? I tried implementing object pooling but the act of changing icon, text, talent details etc. makes the performance very bad each time object pops in or out. Should I look into replacing scrollview panning into maybe worldspace canvas + second camera?
  3. Is it possible to use multithreading in this case?

All tips are highly appreciated. Thank you.

1 Like

Are you using UGUI or UI Elements for this? Regardless, there’s a few things I’d consider doing here since you’re targeting mobile. I’d absolutely try object pooling, but before that I think you should do some more aggressive culling.

First, I wouldn’t have 60k UI components in the first place. This is dramatic overkill. Set up an object pool that contains exclusively the maximum amount of nodes that can be in a single sphere (as in the circles containing the individual nodes) and its immediate neighbouring spheres. There’s a few ways you can handle this but you’ll want to get a little into graph theory here for your specific use case and I won’t be a lot of help there. In an ideal world this will probably take your 60k down to maybe around 100 with some conservative estimates.

I’m assuming that you have some sort of data structure that represents your entire grid that underpins the whole UI rather than it being more explicit links, but if not you’ll want to get on that immediately. Ideally, this should have each sphere separate from the next because this will help with the more aggressive optimization. Instead of updating everything, only the currently active sphere and its neighbours should receive any consideration at all. No more enabling or disabling, simply rebuild the 100 or so connections within and between those four spheres.

To make sure that the overall view itself updates, I’d do some LOD stuff. Whenever a new ability is activated/deactivated, generate a relatively low-resolution imposter image so that when you zoom out, you’re just looking at a collection of textured quads instead of anything else. When the player zooms in, start displaying the standard talent view instead. If you keep tweaking the resolution of the imposters, you should be able to find something that’ll work without any visible pixelization too.

4 Likes

aside from what @Murgilod said, I would like to point out that inactive is not the same as non-existent. Inactive objects absolutely still consume resources. This is the primary reason why pooling and more aggressive culling is being suggested.

I imagine that along with all the visual elements there are scripts etc and other components on those UI objects.

When zoomed out you want to literally just draw them and do absolutely nothing else, ideally directly without using game objects if possible. If you have to use game objects, try to have them un-nested or with as little nesting as possible, at least when zoomed out. When you zoom in to a close enough level where you can actually interact and see them properly, that’s where you want to bring the scripts and everything else into play.

Also I am not sure if that final image is actually from within the game, but if it is there is absolutely no need to let the player zoom out that far. If you have to zoom that far, you need to basically construct an entirely different object as the “zoomed out” tree that is made up of minimally nested purely visual objects.

1 Like

Yeah, I was gonna say, if you just rendered each cluster to its own 256x256 render texture, refreshing it only when something on the graph changed, you’d show the pre-rendered version whenever you’re zoomed out just a little.

2 Likes

I can see the benefit of zooming out that far if things are organized like that. If double-tap or something can quickly zoom in that can be a pretty substantial navigation aid.

1 Like

I agree but then I am also thinking, wouldn’t you just lose track of what is what when you get completely zoomed out? Like you would remember “its roughly over there on that sphere” the thing you are looking for, but even then it’ll probably get confusing fast unless you have a good memory. But without playing the game its hard to know for sure.

Definitely when focus testing, if players are not actually using the zoom to this level then OP should just remove it - but OP should focus test first to see

It depends on how it’s laid out. If Magic Attack is 12 o’clock, Magic Defence is 3 o’clock, Physical Attack is 6 o’clock, and Physical Defence is 9 o’clock (or some other configuration) then the player should be able to extrapolate where things are and make mental connections that way. Like, we have to take into consideration that this is a game that has a 10k node skill tree and the type of person who would be playing that in the first place.

1 Like

Color coding and some decorative elements would help with that.

3 Likes

Yes, and also from a game design point of view! Having this many skills completely destroys the experience of unlocking a new skill. Hey, I’ve unlocked skill #47,384 today. Yay! :roll_eyes:

Each of these skills will have a miniscule effect, both on gameplay and the player. In fact, I would argue it will be regarded as a chore managing this many skills.

Nevertheless: Level of Detail!
You can show one of these circles, or maybe a circle of circles meaning the six of them that go together. All the rest are represented not as actual UI elements but as a single icon that represents that part of the skill tree. If the user zooms out fully, all of these branches are drawn as mere icons.

Only if the user selects that other part of the skill tree and/or zooms in on one do you draw them as actual UI elements.

All the above… mobile would limit what I say next, depending on what devices you want to support…. but you could put this all on the GPU with drawmeshinstancedindirect or something and upload what’s unlocked through a buffer in change… (even the animations you can run on the gpu)

Tell that to Path of Exile: https://www.pathofexile.com/passive-skill-tree

You know, massively popular game, huge skill tree being one of it’s defining features.

6 Likes

You can’t actually say that for certain. I count a perimeter of 45 perimeter spheres and we don’t know what (if any) level cap there is here. Each sphere has what appears to be at least one core or extant ability associated with it, and I imagine the content of those cores are different ways to synergize with that ability. There seems to be around five clusters of spheres that are related to one another, so there’s probably further synergy there as well. Depending on the build the player is going for, I imagine they’d probably choose to either focus on specific paths rather than filling out literally every option.

Also, the OP asked for technical help, volunteering half assed game design advice in the shape of “why are you even doing that, lol” is more or less condescending.

2 Likes

Each class have different starting position and can unlock up to 5 node groups (you can see starting position as the top most node with character icon). Player can choose to develop character all the way in it’s core class going down in the node grid or have an option to go for multiclassing by taking nodes of neighbouring class and taking some of their abilities or passives. This way each character can go either full on their class or go for multiclassing with one of the 2 neighbouring classes creating new “sub-class”. For example Rogue could multiclass with Warrior creating subclass “Thug” to get heavy armor proficiences and be able to use other weapons than dagger to not rely on backstabs but rather direct confrontation etc.

Thanks everyone for your suggestions. I will try to offload work from CPU to GPU and implement proper object pooling by keeping all the class instances in some array rather than on the node object and initializing given instance if it becomes visible or screen, while node objects will come from pool.

2 Likes