Problem space
I have WxH elements (cells) in a grid. These are UI elements but this shouldn’t matter.
The actual W and H values are irrelevant. These are known integers once the game starts, and won’t change during runtime.
These cells render as flat boxes with varying alpha values, and behave like “motion sensors”. If something goes over them they instantly become opaque. If there is nothing on them their alpha slowly fades back to zero. That’s the whole deal and yes, sounds very trivial on paper.
However, the actual product of W*H can be a high number, so I decided not to update each of these cells per frame. Instead what I thought of doing is to make a “hot list” where I add only cells that are known to be activated, and then I foreach through every cell in this list, diminish their alphas, and remove those that hit zero through decay.
What I want to accomplish
I can implement this just fine, however I want to get rid of the frame allocations that produce constant garbage. (Currently it’s around 300 bytes.)
Here’s my current solution in more detail
The trigger elements simply move across the screen. I have a way to convert their position to a hash value. No hash collisions are possible by design. This makes it possible to also not care about W or H.
The grid collection has all of its cell GameObjects preallocated but set as inactive. It also has two dictionaries, one that maps hash → cell’s alpha, and another that maps hash → cell’s mesh renderer. These two are practically static, and allocate only once.
Each frame, grid collection’s update does the following (in this order):
- blanket decay of everything in the “hot list” (each active cell has it’s alpha diminished slightly)
- cell activation (where some cell’s alpha is set to 1)
- actual update of the visuals according to whatever is still in the “hot list” (this uses the second dictionary to insta-reach for the mesh renderer and update its material).
I also make sure to call SetActive as appropriate on the actual GameObjects to remove the burden from the engine’s update loop.
This performs very satisfactorily, however, no matter how I implement this, I can’t think of a way where I don’t produce garbage. In the last version, the garbage is introduced because of adding/removing from the “hot list”.
(Btw when I say “hot list” it is currently just a HashSet.)
Other things I’ve tried
The biggest issue that prevents me from solving this elegantly is not being able to modify the dictionary in one pass because that’s how foreach works. For the latest version, I made a dedicated collection, however it still maintains a dictionary (hash → alpha), and two additional hash sets (‘active set’ and ‘key set’), so that I can foreach over ‘key set’ while I update the other two in one go.
I was also thinking about an ordinary list (instead of a dictionary) where I just swap the elements to maintain the active ones on the top, and then separately keep track of this active count. This would solve the garbage problem and is great for everything, however I can’t find the cell that is supposed to be activated in O(1). And maintaining both a dictionary and a list is PITA because I then need another dictionary to map hash → index.
I’ve also tried using Linq’s ToDictionary, however, even though the code is tiny and clever (with no other intermediate collections required), this produces a fresh dictionary instance per frame.
I don’t mind sacrificing memory, I just want to get rid of the intermittent frame garbage if at all possible, while also keeping the solution relatively simple.
Is this even possible to do without making a specialized data structure like a priority queue or a binary heap or whatever? In that case I will just pronounce it “good enough” and move on. I am astonished that such a trivial task turns out to be so hard to implement in a straightforward manner.