20 September 2020

ShipBasher Development Log 9: Damage Works Now Except It Doesn't But Now It Does Except It Doesn't But Now It Does

Two Three days after my previous post, I hit a milestone! It became possible to build the game, set it up elsewhere (a different folder or a different PC), and complete a "full" play cycle of loading ships; editing ships; saving ships; and pitting ships in battle with movement, weapons fire, module damage, explosions, and module and ship destruction!

Modules that become detached when their parent modules are destroyed now assign themselves to new debris "ships" (at least when everything works... as with every other part of the game, bugs have been occurring) so that they can properly participate in the physics simulation and in the battle, although in most cases, lacking any AI, they merely drift until something shoots and destroys them.

With all this accomplished, I was about to compile another build when I noticed something wrong with my test bullets: despite my claims that they did damage, they didn't! Lasers had been working fine the whole time, and still do last I checked, but the bullets kept crashing against modules without affecting their HP. I found that I had misinterpreted how Unity handles collision callbacks and was having the bullets shout information about their damage into empty space.
So I fixed it and made bullets do damage again, except they still didn't, and those lasers that were working fine didn't either. Problem with the new code? Nope.
See, my laser code roughly took the DPS, divided it by the frame rate, rounded that number to the nearest integer, and applied that much damage every frame. So my laser with supposedly 1 DPS did, each frame, a damage of 1 divided by somewhere between 20 and 60 (so something like 0.02), rounded to the nearest integer, which was basically always zero. Somehow it had seemed like it was working before, as with all the modules having only 1 HP, occasionally I guess there was a hiccup long enough for the damage to round up to 1 and destroy them - but once I started writing in reasonable HP values like 100, the lasers stopped working so well. Luckily that was a simple fix of setting the DPS to 100 instead. Yes, I have decided that the above failure mode is an acceptable consequence of my code working as designed and that I (and players) will simply have to set comfortably high damage values for lasers. I consider it a small price to pay for avoiding the Kraken by minimizing floating point operations.

The next addition was the beginnings of a system for relative file paths so that the game needn't exist in the exact directory I specify prior to building it. It's going to need improvement later as it was a quick fix not designed for much expandability, but for the moment it's sufficient for allowing me to share builds for eventual beta (alpha?) testing. Of course this doesn't have much of any visual consequences.

What did have visual (and other) consequences was when I decided to no longer have my Editable Data System be responsible for converting editable data between linked lists and arrays based on whether the game is in an editing or play situation. Rather it would always stick with linked lists for ease of editing, and other systems interacting with it would instead be responsible for gathering up whatever data they would need to access rapidly or frequently and then submitting it when done. This, as I feared, messed up a lot of other things, as it turned out that despite my imagined efforts at maintaining encapsulation, the system had still ended up coupled to a bunch of other things in the project. It took most of the day to undo the damage and I kept kicking myself for not backing up the project just before doing this (I have several backups going up to a mere few days earlier, but even more recent would have been safer). Fortunately, eventually it worked despite the very un-helpful debug messages I had made it give me:

Those time stamps aren't even part of the messages - they're an optional feature of Unity's debug console. I had literally built a system to spam the log with empty messages. Hooray!

With that nerve-wracking process out of the way, I was about to recompile the game again when I noticed something wrong with my test bullets... uh, again. It turned out I had misinterpreted how Unity handles collision callbacks, again, and was having the bullets shout information about their damage to objects entirely unequipped to handle what was going on. Hey, at least it was better than empty space this time.

So I fixed that and damage was (supposedly) working again, except that lasers were still way more useful than bullets due to one simple thing: my ships' turrets have terrible aim. They are able to precisely point themselves at their targets and fire with no problem at all, but things move in this game, so by the time the bullets get there, often the target has moved far enough that they miss. Thus I decided that my next order of business was to start incorporating the awesome target tracking system I had rigged up a while back in my test project:

I was actually quite proud of myself when I made this. The turret in the foreground accounts for the velocity of the sphere in the background and fires at a point ahead of it, calculated just right so that the bullets hit it when it gets there. It also makes use of another cool (if I may say so myself) system I had made even earlier, which was a GPU-based point cloud sort of thing that can render absolutely obscene numbers of bullets (or star sprites, as in the background of this blog at the time of posting this entry):

The second image had its colors adjusted to make it easier to see just how many were being displayed - every single one of those little dots in the distance is a bullet the turret has fired, and the engine is chugging along happily at a very high frame rate. This thing can actually handle a larger number of bullets than Unity can handle of mesh vertices (more than 65535) and will thus be a serious boon in a game where I imagine people will be having ships spew entirely unreasonable quantities of ammunition at each other.

Of course, this system was not compatible with the turret system I was using for my weapons right out of the box. I was going to have to move away from the existing (temporary) strategy of having the turrets aim themselves and toward an architecture wherein weapons intelligently aim their turrets based on what the player is trying to accomplish - basically a system for letting the player issue orders to the ship regarding where to fire, so I figured I may as well get started on that. Thus I began building a UI for selecting ships in play mode, selecting targets for them, and ordering them to fire on those targets. Putting the buttons there was simple, but then I had to add functionality, meaning I had to draft a new ship control script and upgrade the way weapons function so that they can match their targets to what their parent ship has targeted (I do still want them to be able to pick their own targets in certain situations).

While testing that out, I came to realize that it would help to have a target practice dummy in the form of a durable ship with lots of inertia. So I built one and then edited its file manually to give its modules especially high masses and unreasonably durable armor. I loaded it up in the game, and then I noticed something wrong with my test bullets. This time they were doing way too much damage because the armor wasn't doing its job! Time to debug some more...

I had mixed myself up on what the design was for my own game. See, I had made it so that damage could come in one of eight types and that armor would resist each of those types differently (except for the first type, which is "magic" and bypasses armor - it's intended for testing (and has served me well thus far in that area) and cases where I would want a special overpowered weapon such as in boss fights). Armor shrinks the incoming damage toward zero based on how close its resistance to that type is to one - so yes, a resistance of one would make a module invulnerable against a given damage type, but I intend to disallow players from giving modules that high a resistance without enabling cheats.

Except I momentarily deluded myself into thinking it was the armor value itself that would shrink the incoming damage like this, not the resistances, so when I gave the test dummy's modules an armor value of 0.99999, expecting them to be nigh invincible, and they were instead popping within a few seconds, I got really confused. As seen above, my efforts to figure this all out involved a lot of calls to Debug.DrawLine and Debug.DrawRay and temporarily having my modules spam the console with messages about what sorts of damage they were receiving. Sadly I didn't think to show it here, but I took some time a few days ago to rig up an editor window for my Editable Data System that shows all of the editable values attached to a given object, and it proved very helpful here in reassuring me that at least the editable data was being handled properly.

Hopefully all this rambling didn't seem too pointless or boring. In short, it's the tale of my increasingly complex game having many possible points of failure and the confusion and frustration (and eventual joy) I experienced in tracking down, analyzing, and rectifying these failures. There are probably a lot of possible lessons to glean from all this, but I suppose one of them is that if you keep up your efforts, building systems for anything from cool visual effects to debugging assistants, chances are it'll pay off later when they all come together. I look forward to showing off more of the player ship control UI and my upgraded weapon system in the next installment.

No comments:

Post a Comment

Sorry this isn't a real post :C

I've entered graduate school and, as I expected, suddenly become far busier than I had been before, leaving no time to maintain my blog,...