Seems like I’m writing these things on Mondays most of the time now…
Didn’t get a whole lot done over the weekend, at least not visibly. Most of the work was under the hood or maintenance. I repaired the puzzles which mix the “dice” and “dot” mechanics, after having changed the way the dice mechanics work. The combined puzzle sequence stills need a lot of work to be enjoyable to solve, but at least the puzzles aren’t actively broken anymore.
I got them working again through a small change to how puzzle solutions are validated. Previously, with the dot mechanic, whenever the color of two dots needed to be checked, the code would use an arbitrary number which specified which color dot we were talking about. (i.e. 60 = blue dot, 61 = yellow dot, 62 = red dot, etc.) The change I decided to make is to have it actually directly compare the color values of the two symbols in question. (Technically I compare an integer hash of the color to avoid the == operator overhead of Unity’s built-in color class) Directly comparing colors has a couple benefits when it comes to simplifying the code and also makes future puzzle possibilities much easier.
I didn’t end up revamping the entire puzzle panel system, because after some further investigation, there are some good reasons that I architected it the way that I did, in spite of the fact that it’s unwieldy at times. The main reason is that I’m relying on the Unity serialization system to save the panel layouts, and certain data formats just serialize more straightforwardly and quickly. One dimensional data type arrays are the most easily serializable format. So, even though it would be more useful to have a single tile structure which stores all the relevant data, including references to spawned GameObjects at runtime, it is a bit more challenging to implement than it would immediately seem.
Furthermore, serialization can get real nasty when dealing with null references, as stated on this page in the Unity docs:
Consider how many allocations are made when deserializing a MonoBehaviour that uses the following script.
class Test : MonoBehaviour { public Trouble t; } [Serializable] class Trouble { public Trouble t1; public Trouble t2; public Trouble t3; }
It wouldn’t be strange to expect one allocation: That of the Test object. It also wouldn’t be strange to expect two allocations: One for the Test object and one for a Trouble object.
However, Unity actually makes more than a thousand allocations. The serializer does not support null. If it serializes an object, and a field is null, Unity instantiates a new object of that type, and serializes that. Obviously this could lead to infinite cycles, so there is a depth limit of seven levels. At that point Unity stops serializing fields that have types of custom classes, structs, lists, or arrays.
Since so many of Unity’s subsystems build on top of the serialization system, this unexpectedly large serialization stream for the Test MonoBehaviour causes all these subsystems to perform more slowly than necessary.
I may yet return to make some more of those changes, but you can see that the water is fraught with peril. A good approach would probably be to have two formats, one for all of the runtime code, and one that is used for serialization, but this would also create a bunch of potentially slow startup code every time the game was run in order to translate between the data formats (and there’s already more initialization code for the panels than I really want there to be)
On a humorous note, before some of the changes I made, I had this monstrosity of a line of code:
symbol = litSquares[p.x+width*p.y].GetComponent().transform.GetChild(1).gameObject.GetComponent();
And now, with some data restructuring and a few helper aliases, it becomes the much more manageable:
symbol = tiles[p.x+width*p.y].symbols[0]