Thursday, October 7, 2021

My development workflow

Today, I'm going to demonstrate my developer workflow and architecture for my game, with Unity3D, GitHub, and Visual Studio. I've created a workflow that allows me to verify any change with a rich set of automated tests, and automatically load those changes into my Unity project, reducing the time to resolve bugs, and giving me confidence that my code is robust.

Architecture

At the top level, I have my Unity project (in green), a private project stored in GitHub. This has an attached .NET project, (named the default "Assembly-CSharp"), that references a .NET standard dependency "Battle.Logic", a public project in GitHub. Attached to the Battle.Logic project, is a unit test project that currently has ~97% code coverage - and leverages over 200 tests that run in just a few seconds - giving me almost instant feedback that the project is in good shape.


The bug

Let's look at the bug. I've been iterating on AI for a few weeks, but when this code runs in Unity, it fails with the error below. You'll note the error is in the Battle.Logic. Uh oh. How can I replicate this? Luckily we can do it in just a few minutes. You'll notice in the top right of the UI, I have a "Save JSON" button. What does this do? Let's press it and see:



The button serializes our game into a JSON file. In the console below we can see where it was saved on our laptop, and copying the file into our Battle.Logic test project, I can create an automated test to load the file, replicate the game state, and test it.



The code to do this is quite simple, I open the JSON file, add the correct line to replicate the issue we saw in our Unity file (run the AI). Look at the error in the bottom left of the tests- we get exactly the same error we saw in Unity. Hurray! Now we can troubleshoot and resolve the issue. In this situation, an object, (character "CoverState"), wasn't being initialized correctly. I resolve the issue, my test passes, and I can continue building my game.



Let's look at how we get this fix back into Unity. In GitHub, we open a new pull request with the fix. We have a couple other checks in here to confirm quality - with code coverage (Coverall) and code analysis (SonarCloud). Everything looks good, so we merge the Pull Request.


This triggers a new build on the main branch in GitHub, which when successful, publishes a new dll to a "release", with a unique version - in this case 0.9.2. 



To get this into my Unity project, I wrote a simple script to download the files from the latest release, unblock them (thank you Windows, but I trust my files), and copy them to the right place in my Unity project



Opening up my Unity project, I can see in the files in the "Git Changes" box.



Running the same situation as before, I can see the issue is resolved - although I did find another bug - my character AI moved it to an unexpected position. Time to do this again, and write a more comprehensive test for this scenario!



Wrap up

Just a simple example, but I value my time, and developer productivity is everything. In just a few minutes, with some key enabling automation, I can troubleshoot and resolve an issue, create a test to prevent it from happening again, and get the change back into my Unity project in just a few minutes.

Sunday, August 1, 2021

Reorganizing, again.

Every couple of years I end up in the same spot, lost, overwhelmed, unmotivated. 6-12 months later I repeat the cycle, rebooting and starting again. I've done this for roughly ten years... 

This round is different. This one is powered by DevOps and GitHub.

Why is this one so different? 

First, I've separated the logic into a separate project, and it's open source and on GitHub for anyone to use. Using .NET Standard 2.0, and C#, I'm able to write unit tests that validate that this project is working as expected. As it's all just C#, no graphics are being rendered, the tests run extremely fast, nearly 200 tests in less than half a second. I calculate paths, chance to hit, field of view, etc, without a graphics engine. These automated tests are a crutch when I walk away for a day, week, or several months. I know that when I return, I have a series of tests to tell me when I break something else. 




This also allows me to measure code coverage - the percent of code that has a test. My code coverage is very high, 99.61%, when 70-80% is typical.


Additionally, I can visually see my maps and situations in 2D using ASCII. I can render these data structures in 3D with Unity3D relatively easily, but seeing it in 2D gives me an easy debug mechanism. 




I also have a detailed log to help me with debugging. Human readable, important as I am a human. 


More is coming, I've just started to integrate this in Unity3D, and I know there will be more breaks in development, but now I know I'm chipping away at a system I can always pick back up again. I start a turn by calling my battle logic, all of the movement calls return a list of moves and if their are any interrupts - and in general, it's making the 3D part of this, much, much easier to manage