Quirks of Low Level Coding

As seems to be becoming a habit, I just tackled and beat a significant bug (which I’d actually noticed much earlier and had put off fixing until now) so I decided to write about it. The “moral” from this one seems to be that that low level coding will invariably have interesting little quirks and anti-features which will always be lurking in the wings to knock experienced and novice coders alike off their feet. This bug (like the last one) was in the grid cell code. As I have mentioned previously, this project involves a 3D grid map with various things moving in freeform around it but still interacting with the grid for some things.

In my mind map of future progress, the interaction between freeform objects and grid cells will mostly come about through environmental effects like airflow, gravity and toxic gasses. Without going into too much detail on gameplay (that’s for later), I’m still playing around with how best to handle it. My current implementation is a holdover of my old habit of premature optimisation, where each object that needs to check the grid will periodically (or after moving) recheck with the MapSuite to see if their latest position has resulted in a cell change – if it has, inform their old cell that they’re leaving and inform the new cell that they’re entering.

This is to replace a system where the objects simply grab and reapply the environmental settings every update regardless of cell changes or the like. The advantages of the new system is that (obviously) there’s less lookups, but also that there’s a more clear chain of communication for events such as gravity/atmospheric changes to be passed down from a cell to it’s contents. The only appreciable disadvantage that I see with it is that if I don’t account for all circumstances where something could move, it won’t update it’s cell location which will have weird effects with the aforementioned events system. I’ve got the periodical recheck as a failsafe in case the delayed cell changes miss out on something, but that’s still a symptom of the old system where there is a redundant system which may or may not be necessary. Tradeoffs, I guess.

Just quickly while I’m on the subject of the cell map, I thought I’d lay out how it works under the hood. It’s relatively simple, with all the cells being stored in an STL unordered_map and indexed by a hash of the cell’s co-ordinates (which are normalised to integers, but I’m still a little wary about possible floating point error). I’ve also got a plan in the back of my mind to have additional collections of cells where they’re sorted by relevant attributes, eg cells that temporarily need regular environmental updates.

But the bug! What was the bug? Well I mentioned it was a somewhat low level quirk of C++, to be specific in how it handles numbers. As I mentioned above, freeform objects determine their current cells by taking their exact position as a float vector and normalising it to an integer vector with an offset of (0.5, 0.5, 0.5) to account for rounding. For simplicity’s sake, the way I was doing the rounding was just casting the float to an integer. This set of warning bells even before I had written the system the first time, but I decided to try it out and see how it went.

Turns out that when casting from a float to an integer, values above 0 round down but values below 0 round up – which results in a whole extra anomalous tile on each axis when trying to find the current cell that objects are in! The way to fix it is just to add a correction value of -1 when casting from float to integer for values below 0, but I was getting some weird bugs with gravity before I found it almost indirectly while working with the player code.

Here’s to hoping that the bug where rays of artificial gravity being projected from a tile adjacent to where they were supposed to originate from will disappear! Speaking of improbable bugs, maybe my next entry will cover how I found and fixed the null pointer error while assigning bullet quaternions… better not. I might never write an entry here again 😉

Advertisements

Overly engineered redundancy

I just had a fairly textbook bug pop up, so I thought I’d write about it while it was still fresh on my mind. Unlike most “interesting” bugs, this one was both easy to locate and easy to fix – but it came about as a direct result of the way I had setup a system.

In this project, I have a 3D world divided up into cells and each cell can have a single structural frame or be empty and in order to place a frame in a cell, there has to be a frame in a cardinally adjacent cell. I have a global manager which handles creation and deletion of these frames, and I was investigating why creating or deleting frames from cells wasn’t properly triggering the creatability/deletability of frames in adjacent cells. It turned out that a reference to the cell itself wasn’t being passed around, and in fixing that issue I realised there was something I was forgetting – I hadn’t actually setup the system to ensure that there could only ever be a single frame in a cell.

So what I did was added a check to the manager’s creation function for any other frames, and if there were it would delete them. Unfortunately, I placed it after the new frame was created but before the new frame was initialized (I’d setup delayed initialization as an option to make larger or frequently resizing worlds lighter on the processor). Obviously that was completely the wrong place to put the check, as it not only attempted to delete the frame I’d just created, but it hadn’t been fully created so it was crashing every time.

Easy find, easy fix, simple mistake, but I see it as a direct symptom of a system that I engineering to not only be capable of everything I wanted it to do now, but also everything I thought I would want years down the track while still making it simple and modular enough to be expandable and adhere semi-strictly to OOP (or my garden-hedge understanding of it).

After about 12 hours over the past few days spent restructuring and cleaning up how frames and cells are handled, I’ve been reconsidering my policy of rewriting systems to be better under the hood. As a self-analysis process, this has been ongoing ever since I discovered Unity and Winforms coding over a year ago (quote “You don’t need to make the code good, because this is scrub coding land”) but I’m still on the fence with my conclusion – that a combination of the end product and the goals for making it are what justifies the approaches, procedures and technology used.

In case of this project, I wanted to make a well designed and robust system to account for complex gameplay and in doing so improve the quality and speed of designing/implementing systems that I work on… while working on it in my spare time. In other projects, if I wanted to create a game with specific gameplay and timeframe to work in, then I clear deliverables which override my desire to learn and work at my own pace in doing so. Where this has been an issue for me in the past is with university projects – it’s a learning environment where I want to maximise the convenience of being able to make frequent mistakes and effectively analyse them, but still succeed (the latter was essentially my approach for final project).

In a commercial environment it strikes me that the deliverables would be the goal, but for viability of long term usage/expansion the system under the hood needs be clean and maintainable. Anyway, I guess it’s just a line which everyone needs to work out before they start.