26 September 2020

ShipBasher Development Log 10: PewPewPEWpewPewPEWpewPEWpewPewPewPEWpewPEWpewPew

Something I hope will be a draw for ShipBasher is the ability to marshal completely impractical ships that fire entirely unreasonable amounts of ammunition at each other, as I mentioned briefly in the last post.

In pursuit of this idea, I dusted off the GPU bullet system I had prototyped a while back and integrated it into the game over the course of one very late night. In so doing, I discovered that I'd more or less made my computer work with proverbially one hand tied behind its back.

See, like some kind of pro l33t h4x0r code sorcerer, I decided to use a compute shader for processing bullets. A persistent buffer of bullets is formed on the GPU, so that unlike in a normal shader, the results of computations made in one frame can carry over to the next frame - an essential feature when trying to use a GPU to simulate things moving over time.

But like a dumb idiot n00b, I was taking the buffer back from the GPU and sending it off to be made into a mesh object, making a redundant copy of it along the way, and then when it was time to render the frame having the mesh object get sent over to the GPU so that the poor thing could hold it in memory alongside the persistent buffer, essentially causing both my computations and memory usage to be doubled for no reason and, perhaps even worse, bottlenecking my performance by schlepping data back and forth every single frame. Basically the only useful thing the GPU cores were doing was moving each bullet forward a bit, which is such a trivial operation that I probably would get more performance out of skipping the compute shader business altogether and just calculating everything with the CPU like a normal person.

Of course once I noticed and apprehended the scale of this problem, I had to fix it immediately. I tinkered and fussed with my code until I had eliminated all the redundant data processing and made everything way more efficient. Unfortunately it didn't change much as far as the frame rate went, because it turns out that with my setup and its relatively weak GPU, my performance was limited by rendering speed anyway. Nevertheless it sure felt good to know my code was way less stupid than before.

I also made some tweaks to my weapons firing code so that turrets with extremely fast firing rates could fire multiple rounds per frame rather than being limited to one per frame as they had been before, and behold!

Yes I think I know what you're thinking.

 

In practice I doubt I'll include single turrets able to expend ammunition quite this fast (the last one is 20,000 rounds per second or 1.2 million rounds per minute) in the finished product, but the ability for many bullets to be released every frame by all the various turrets that I expect to be active at once is important.

Something else I hope gets noticed here is a big improvement I made to how the bullets get rendered. Now they stretch out along their velocity vectors, much like Unity's built-in Line Renderer, but unlike the built-in Line Renderer, they smoothly transition to point sprites if they are very far from the camera or are viewed from a very shallow angle to this vector, causing them to maintain a round, 3D appearance rather than give away their true nature as flat rectangles. It's not perfectly polished just yet, but I'm pretty proud of what I accomplished with it and think it makes for some nice screenshots (as you may have guessed).

The next problem to tackle is making these bullets actually do something. While it's amusing to watch fountains of bullets pour forth from my gun barrel, they simply float through space until their numbers come up in the pooling system and they start the cycle over again, never having any effect on the world, much like the test bullets not too long ago. This is a harder fish to fry, though: Unity's physics engine, PhysX, and all of the alternate physics engines available, do their processing using the CPU, using data sitting in RAM for all of the collidable objects and their locations - but this new bullet system operates on the GPU, which has its own memory that's separate from the RAM and is alien to the physics engine. I could write a whole collision detection algorithm in a compute shader, or I can find some inexpensive way to roughly predict collisions in there, hopefully for only a relatively small subset of bullets, and then extract some data about those back into the regular RAM so I can use it to tell the physics engine what's going on. It's just as complicated as it sounds, and thus the subject of at least one future blog post.

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.

10 September 2020

ShipBasher Development Log 8: Vision

I figure it's about time I shared a detailed description of exactly what game I'm trying to make here. In short, ShipBasher is a real-time 3D sandbox, simulation, and strategy game about building custom spacecraft out of premade pieces (known as kitbashing, hence the name) and pitting them in battle against one another. I'll expound on each of these facets here in their own sections.

Main Menu 

The intended gameplay experience starts, as most games do, with the main menu. Originally this was a generic list of buttons to enter different environments in the game (i.e. settings page, credits, campaign mode, ship editor) but then I decided to take a bit of inspiration from Spore and have a big 3D galaxy occupying most of the screen, as I have mocked up here:

This is itself a menu in that each little circle represents a playable level. Part of each level's data file will be a position in the galaxy at which it appears. As I intend to allow players to create their own levels and place no limits on how many a player can have, it will be possible to fly the camera through this galaxy to explore different areas of it up close.

By selecting any of these circles, the player can open a preview of the level, which takes the form of a "wormhole" showing the level's background features and a window detailing properties such as the level's name and description.

Playing a Level 

Once a level is selected, or a fresh new one is created using the buttons on the side, it can then be played or edited. There will be separate UIs for playing a level and for editing it. This is a mockup of the play mode UI:

While playing a level, the player can select one or more ships to control. I plan to make it possible to restrict which ships are available for a player to control and which are "enemy" or "NPC" ships. For now clicking any module on a ship will select that module and the ship to which it is attached, and as seen here that module will be highlighted and the ship will have a ring drawn around it.

While a ship is being controlled, a menu will be visible (in this mockup it is at lower right) for issuing commands that affect that ship, such as initiating self destruct. Right-clicking a module on any other ship will open a menu (seen at upper left) for interactions between the selected ship and that other ship, such as attacking it.
The camera can be focused on any ship and rotated around it, but to keep track of objects not in the current field of view, there is a radar display at lower left with a slider to adjust its range.
Each ship may have a small readout next to it showing its current status.

Finally, a few large objects are visible in the background. The distant star and planet are visual effects only and won't affect gameplay, but the asteroid on the right is a physical obstacle players will need to accommodate. I may add levels in which asteroids need to be destroyed, or in which special environmental hazards from distant objects affect ships in the level - for example a pulsar that would damage ships with its radiation. These concepts have yet to be figured out in much detail for now.

Planned but not shown are options for pausing the level or returning to the main menu.

Editing a Level 

Instead of playing a level, the player can open a level for editing, or, in certain circumstances, the player can pause a level in progress and edit it. Editing a level involves a different UI:

As in play mode, any ship and any module can be selected. Different windows exist for editing these or for editing the level itself.

At lower left is a window for editing the properties of the level, such as its name, description, and location in the galaxy. Changing the level's location in the galaxy will alter the appearance of the background starfield, so that a level near the galactic core, for example, will be surrounded with a dense field of yellowish stars. Additional information may be shown such as how many total ships exist and a difficulty rating, which will likely be left to the players to determine but might be possible to calculate.
At upper left are buttons for adding objects to the level, e.g. ships, distant background objects such as stars or planets, or physical hazards such as asteroids. Clicking the button to add a ship will reveal options to either create a new ship (not shown in this image) or open a saved ship from a file and spawn it in front of the camera.
Once a ship exists in the level, it can be moved and rotated, and a window becomes visible for editing properties such as its name and description (seen here at lower center). Additional information such as its total mass and firepower is also intended to appear here. At the top of the ship editing window are buttons for tasks such as copying or removing a ship or for saving it to a file.

Any ship will need at least one module attached for it to function. Visible at lower right is a menu for adding modules to ships. When the player hovers over a module in this menu, a preview window appears, allowing the player to examine the properties of the module before loading it. Once a module has been attached, it can be moved and rotated using transform handles, as shown surrounding the module in space, and a window appears, shown here in the upper right, allowing properties such as its name and description to be edited. I may make it possible to restrict editing of certain properties in certain contexts, for example allowing the armor and damage power of a weapon to be changed but not the price (rather the price would be calculated based on how powerful the module is made via other edits). At the top of the module editing window is a set of buttons for tasks such as copying or removing modules or saving a customized module to a file.
At this time it is not planned to allow the scale of modules to be altered or for any custom 3D modeling or texturing to occur in the game.

Finally, at upper right are buttons for saving the current level, playing it, or returning to the main menu.

Other Features 

As seen here, ShipBasher uses a 3D environment with a third-person camera. Every object in the game is able to move in three dimensions, not restricted to a ground plane, grid, or global axes. The camera can be rotated omnidirectionally so that there is no universal "up" or "down" direction, as is the case in outer space in real life.

ShipBasher simulates in real-time, i.e. gameplay is not based on turns. It will be possible to pause the game, but time dilation, either to slow it down or speed it up, is not planned.

Being a sandbox and simulation game, ShipBasher has no central storyline, goals, or sequence of levels through which the player must progress. I intend to include a number of example ships and levels, and I may decide later to make it possible to restrict some levels until after other levels have been completed, but this is not planned at the moment.

A strategy element arises in how players will go about clearing each level that exists - which ships to include (if the option is available), what orders to give them, etc.

Players will be able, as described above, to create their own ships and levels, save these in files, and share them with other players. I have no plans to make this a multiplayer game, include any online functionality, or set up any hosting servers, so it will be up to individual players to send files to each other and import them into their own games.

Hopefully this clears up any mysteries surrounding what my goals for this game are. I'll be glad to address any questions I haven't answered so far.

ShipBasher Development Log 7: Moving

Updates have been scarce lately! The latest excuse I have is that I spent the last month preparing to move, moving, and then entertaining family members who came to visit. I have a lot of junk, so it took an embarrassing number of trips back and forth to load up a pair of storage units (the small ones were on sale so I saved money eschewing a single large unit) and then unload them.

On topic, I finally got back to work again and have spent most of my time focusing on adding gameplay functionality. The saving and loading system still has a few issues, but I can work around them easily enough that working on the ability to have ships, well, bash each other is feasible once again. As I mentioned in the last entry, long ago I had all this working on its own, but making ships load from files intact and then work is another layer of complexity. I started by tackling the task of making them able to move once again, which of course began with slapping on some engines:


Here we can see the return of the nice pretty modules I modeled way back when (still devoid of proper UV maps) and, more spectacularly, some big changes to the UI. I showed them a little in the previous post but didn't say anything about the topic, which in retrospect I really should have. ShipBasher has had a shift in intended user experience: rather than a separate ship editor and play mode (and possible level editor), I decided to roll them all up into one single "sandbox." The idea is that the player can open the sandbox with a fresh, empty volume of space, construct multiple ships in it, position them, and have the option to save individual ships or the entire level. At any time it will be possible to switch into "play mode" and let the ships fight, controlling one or more as desired, and to pause the simulation to re-enter editing mode. Saved levels could be shared with other players as challenges, in which editing may be restricted or unavailable. In light of these changes, the UI here features a number of windows for editing ships and their modules. I probably will make an entire post about the UI later.

Reintegrating all of these modules into the game meant extending my editable data system to work with custom-built module models and their corresponding behaviors. I decided to make use of Unity's Asset Bundle system, which provides the ability to package up arbitrary assets separately from the game's compiled data and load them during play. I engineered a system to open these asset bundle files, search them for module prefabs and text files dictating their properties, and pass them on to the editable data system so that they could be configured. After much work, I had that modules menu in the lower right working so that it was possible to spawn specific modules and attach them to the ship.

Once that was out of the way, I could get to doing the thing I mentioned back at the beginning of this post - making the modules I had spawned work. This was mostly a matter of trial and error in the form of putting things together similarly to how they were before and then figuring out which steps I had missed. Eventually I had exhaust coming out and the ships awkwardly pushing themselves around:

And once that was done, and I had debugged some issues with the ships completely failing to move in productive ways in favor of impotently drifting and spinning, I slapped together a simple gun turret and, once it was (guess what) debugged, could finally watch with joy as they flung white beads at each other:

Hooray! At last ShipBasher is fully operatio- except no, not only is this not even close but these "bullets" have no ability to do any damage nor even collide with other ships. They just drift through like ghosts until they despawn. But hey, it at least looks like a space battle is going on now, which I consider a milestone. Also visible at the top are "play" and "pause" buttons I threw in. They work in a superficial manner now, but properly suspending the operation of all the different gameplay systems I've made and properly resuming them is a much more complicated matter.

The last and most recent feature I've begun to address is restoring weapon damage, which due to the plans I have for later on I figured would be easier to implement with lasers than bullets. Thus I set about making a laser turret as well. I had already created most of the subsystems this includes, such as the turret and "line beam" components I made available a while back on the Unity Asset Store, so this turned out not to be too difficult, though I did encounter one amusing issue I'm anxious to address:

See the detached modules hovering in the center of the image? It turned out that by allowing my laser beam to damage the other ship's modules, I made it possible to destroy those modules, as the ability of modules to despawn when too damaged was already present, merely unused. This led to a problem because what I hadn't yet done was program what should happen when modules leave their assigned ship, so I started getting scolded in the debug console about ships having zero mass (due to having no modules attached) and modules being unable to figure out which ship was theirs because, due to their parent modules having been destroyed, they were no longer attached to any ship! More amusingly, because I designed ships to use a single Rigidbody component rather than assigning one to every module, these detached modules had none and thus immediately stopped still where they were and became immovable. I think I know what I need to address next.

So yeah, that's the story of me moving in real life and also making spaceships move in my video game.

Typing this up, it became clear to me that I really need to blog about what I'm doing more frequently. I tried dialing it back after like ten posts about the editable data system, but clearly I went too far and have thus had to do a lot of hurried review. I haven't even covered everything I've accomplished since the last entry and will have to make another one to avoid turning this one into a monstrosity.

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,...