<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Chris Boveda</title>
    <description>The latest articles on Forem by Chris Boveda (@cboveda).</description>
    <link>https://forem.com/cboveda</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1007214%2F7e6363e3-08d5-41bf-9c58-6678156fec9d.jpeg</url>
      <title>Forem: Chris Boveda</title>
      <link>https://forem.com/cboveda</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/cboveda"/>
    <language>en</language>
    <item>
      <title>Time Warp</title>
      <dc:creator>Chris Boveda</dc:creator>
      <pubDate>Fri, 07 Jul 2023 16:06:59 +0000</pubDate>
      <link>https://forem.com/cboveda/time-warp-34i9</link>
      <guid>https://forem.com/cboveda/time-warp-34i9</guid>
      <description>&lt;p&gt;It has been quite a while since I last worked on my game, Project YOMI. It's been an even longer while since I last wrote here. I ask you, dear reader, that you forgive me for my absence, as I have been a bit preoccupied with a very important life event--my wedding! This past weekend, after six years of dating and two years of engagement, my wife and I got married at sunset at an Oregon winery. It was a wonderful weekend of celebration, connecting with friends and family, and joy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IfU0kcRD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/caudtpkr45pl89m0yc6v.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IfU0kcRD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/caudtpkr45pl89m0yc6v.jpeg" alt="Image description" width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A wedding, like other major life events, has a bit of a time dilation effect. Each day leading up to it seemed to pass by quicker than the day before. So, despite my promise to myself to make at least one contribution to this project each day, it was more important to me to spend time helping with the planning and preparation of the wedding. Now though, with that incredible weekend behind us, and looking ahead to future plans and goals, I am ready to resume this work. &lt;/p&gt;

&lt;p&gt;The time away from the project has allowed me some time to reflect on my approach and my vision. Here are some notes and reminders to myself as I dive back in to this game:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finish the design.&lt;/li&gt;
&lt;li&gt;Done is better than perfect.&lt;/li&gt;
&lt;li&gt;Don't get caught up in pre-optimizing.&lt;/li&gt;
&lt;li&gt;Lastly, start thinking of a title... 'YOMI' is copyrighted.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devjournal</category>
    </item>
    <item>
      <title>Follow the Fun</title>
      <dc:creator>Chris Boveda</dc:creator>
      <pubDate>Fri, 26 May 2023 20:25:17 +0000</pubDate>
      <link>https://forem.com/cboveda/follow-the-fun-5dgd</link>
      <guid>https://forem.com/cboveda/follow-the-fun-5dgd</guid>
      <description>&lt;p&gt;There's been a longer gap than usual since my last post. Since refactoring the core game logic and implementing the first batch of unit tests, I've been hard at work on finalizing the gameplay design. This post will be a bit shorter than my others, but I wanted to give a quick update &lt;/p&gt;

&lt;p&gt;At a high level, my motivation for the past two weeks has been to get to a point where I can "show off" the game. Visually, the game has not changed much since the initial prototype. All work has been behind the scenes. That's primarilly because I've been hesitant to put work into the UI, character models, animations, and environment when the core gameplay design has not been finalized.&lt;/p&gt;

&lt;p&gt;What I mean by "not finalized" is that I've had a foundational understanding of what the game is, but the &lt;em&gt;flavor&lt;/em&gt; on top of the rock-paper-scissors-esque gameplay has still been up in the air. Combo system, special moves, round win/loss conditions--all needed to be fleshed out. So that's exactly what I did. I typed up an in-depth design document where I detailed and diagramed exactly what the &lt;em&gt;flavor&lt;/em&gt; would be.&lt;/p&gt;

&lt;p&gt;First, the combo system. The objectives behind the combo system I designed were: 1) to make the player feel like the combos were dynamic and could be shaped by their individual play style, and 2) to reward players for predicting counters to their combo. In the system I designed, any move could lead into a combo, however after following the standard combo path eventually the combo would lose its freshness. The combos paths eventually collapse and become repetetive (read: predictable by the opponent). There would be several ways to "refresh" the combo, including using a special move or selecting a mix-up option. If my next combo move was a light attack, and my opponent knew that and was going to choose a parry, and I knew that so I chose to grab--that would be a mix-up.&lt;/p&gt;

&lt;p&gt;Satisfied with the system I designed, I proceeded to map out each character's unique combo paths and what their "focus" moves would be. I set up the data struture to store the combo paths and the algorithm to evaluate them in combat. I updated the post-turn display to indicate to both players what the next combo move was going to be. I wrote unit tests to confirm that the combos were being evaluated correctly. Built the game, sent it to some friends, and...&lt;/p&gt;

&lt;p&gt;Eh. No wow factor. It is possible that the placeholder visuals aren't doing enough to convey the impact of the decisions the players are making--whether to continue their combo or to try to refresh it with a mix-up. But even then, the system just feels overly complicated. I thought that the complexity of the combo paths would give something for players to learn over time and raise the skill ceiling of the game. However, no one is going to want to invest the time to learn the combo paths if the system isn't engaging and fun to interact with from the get go.&lt;/p&gt;

&lt;p&gt;So even though I'd like to move on to visual updates and UI design, and as much as I want to get the game to a place where I feel proud to show it off on my portfolio and to friends, and even though I'll be undoing much of the work from the past two weeks... I'm going back to the drawing board. &lt;/p&gt;

&lt;p&gt;The core gameplay is there, but the flavor should enhance the game, not detract from it. Bottom line, it needs to be fun. I did get valuable feedback from this round of playtesting, so the work from the past two weeks was not nothing. Now I have some ideas of where the fun might be--I just need to find it and follow it. &lt;/p&gt;

</description>
      <category>devjournal</category>
    </item>
    <item>
      <title>Refactoring, Round 1 -- Fight!</title>
      <dc:creator>Chris Boveda</dc:creator>
      <pubDate>Fri, 05 May 2023 19:14:48 +0000</pubDate>
      <link>https://forem.com/cboveda/refactoring-for-testability-11ch</link>
      <guid>https://forem.com/cboveda/refactoring-for-testability-11ch</guid>
      <description>&lt;p&gt;One of my goals for this project was not just to make a game, but also--to the best of my ability--make a game well. I knew from the very beginning that I wanted to utilize continuous integration and automated build/unit testing, and that the architecture of the project and systems would be a large factor in the success of those efforts. As mentioned in my previous &lt;a href="https://dev.to/cboveda/prototype-v010-1ejg"&gt;post&lt;/a&gt;, code quality was sacrificed in the final push to complete my first gameplay prototype. Well, since then I've been working solely on refactoring. The three main issues I identified were: my over use of the singleton pattern, unmanaged references to database assets, and monolithic classes. &lt;/p&gt;

&lt;p&gt;Let's look at the monolith in the room: &lt;code&gt;GameData&lt;/code&gt;. At the time of the v0.1.0 gameplay prototype, this class handled &lt;em&gt;everything&lt;/em&gt;. Player values such as health and combo counter, UI state management, combat evaluation, and combat history--it did it all. Plus, it implemented a singleton pattern and a static accessor, which allowed all other objects in the scene to find it--great for prototyping, but not great for managing dependencies or unit test setup. &lt;/p&gt;

&lt;p&gt;To refactor this class, I first grouped all of the methods and fields into regions according to what specific responsibility they were fulfilling. Then, one by one, I began extracting those regions in to their own classes. The player values like health became &lt;code&gt;PlayerData&lt;/code&gt;. The state management for usable moves became the &lt;code&gt;UsableMoveSet&lt;/code&gt; (now attached as a component to the character prefab).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsableMoveSet&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NetworkBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;NetworkVariable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_moves&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;NetworkVariable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Moves&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_moves&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnNetworkSpawn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;moveSet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlayerCharacter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;Character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharacterMoveSet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;InitializeMoveSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;moveSet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;InitializeMoveSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CharacterMoveSet&lt;/span&gt; &lt;span class="n"&gt;moveSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;isUsable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;moveSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetMoveByType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;UsableByDefault&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;typeAsByte&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;_moves&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;|=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;typeAsByte&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;isUsable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DisableMoveByType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Byte&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MaxValue&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_moves&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;=&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;EnableMoveByType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_moves&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;|=&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;CheckEnabledByType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_moves&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I was concerned that as I extracted more and more responsibilities that I would have to untangle the dependencies within this class and within other classes, but the opposite happened. The depencies actually became clearer and less tangled. Plus, it exposed opportunities to improve my network communication. For example, by making the &lt;code&gt;PlayerData&lt;/code&gt; class a network serializable struct, now I could send all value updates after combat in a single packet, rather than having the individual &lt;code&gt;NetworkVariable&lt;/code&gt; values each send a message upon each change.&lt;/p&gt;

&lt;p&gt;Without the &lt;code&gt;GameData.Instance&lt;/code&gt; singleton, how can other systems, like the UI or the state machine, access the player data, then? Answer: dependency injection!&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;TurnHistory&lt;/code&gt; class that spawned from the dismantling of &lt;code&gt;GameData&lt;/code&gt; maintains a collection of &lt;code&gt;TurnData&lt;/code&gt; structs, each representing the state at the end of a turn of combat. The &lt;code&gt;GameUIManager&lt;/code&gt; class needs to subscribe to changes to the &lt;code&gt;TurnHistory&lt;/code&gt; class's &lt;code&gt;NetworkList&amp;lt;TurnData&amp;gt;&lt;/code&gt;, in order to update the state of UI elements, like player health bars, after each turn. The way it found this data before was by calling &lt;code&gt;GameData.Instance&lt;/code&gt;. Now, the &lt;code&gt;GameUIManager&lt;/code&gt; has a field for the &lt;code&gt;TurnHistory&lt;/code&gt; object, and this field gets injected by an installer upon initialization of the scene. The &lt;code&gt;GameUIManager&lt;/code&gt; no longer cares about how it gets access to the turn history, and it surrenders that responsibility to the DI framework, Zenject. Furthermore, now the dependencies of each class are clearly identified, and I no longer need to do a "Find all references" search for &lt;code&gt;GameData.Instance&lt;/code&gt; in Visual Studio when making a small change.&lt;/p&gt;

&lt;h4&gt;
  
  
  GameUIManager Class
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GameUIManager&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NetworkBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;TurnHistory&lt;/span&gt; &lt;span class="n"&gt;_turnHistory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TurnHistory&lt;/span&gt; &lt;span class="n"&gt;turnHistory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_turnHistory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;turnHistory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnNetworkSpawn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_turnHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TurnDataList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnListChanged&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;HandleTurnData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnNetworkDespawn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_turnHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TurnDataList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnListChanged&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;HandleTurnData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;HandleTurnData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NetworkListEvent&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TurnData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;changeEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;turnData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;changeEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// Update UI&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Installer that binds TurnHistory
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GameplayInstaller&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoInstaller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;InstallBindings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TurnHistory&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromComponentInHierarchy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsSingle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The installer of the binding for &lt;code&gt;TurnHistory&lt;/code&gt; is contained within the context of the gameplay scene. There are some dependencies, though, that need to be injected across multiple scenes or the entire project. like the &lt;code&gt;CharacterDatabase&lt;/code&gt; and &lt;code&gt;MoveDatabase&lt;/code&gt; assets. These &lt;code&gt;ScriptableObject&lt;/code&gt; databases expose a simple interface that allows for access to &lt;code&gt;Character&lt;/code&gt; and &lt;code&gt;Move&lt;/code&gt; assets using an integer ID value. This means that the size of communication of characters and moves over the network can be minimized to just that integer value. Granting objects access to these databases meant setting up a public or a serializable private field, and dragging in the reference in the Unity editor. &lt;/p&gt;

&lt;p&gt;If I wanted to create separate database files for production and development, maybe to experiment with new moves or a new character, that would mean I would need to drag-and-drop the new database files in every single object that referenced them--not great.&lt;/p&gt;

&lt;p&gt;A better solution is to inject them. So, I created a &lt;code&gt;Database&lt;/code&gt; provider class that just holds references to the two database assets. Then, I set up an installer in the project scope to install the bindings to the provider class wherever it is needed. Now if I want to swap out the databases, I just need to update them in one place: the provider class.&lt;/p&gt;

&lt;h4&gt;
  
  
  Database provider
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Database&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;CharacterDatabase&lt;/span&gt; &lt;span class="n"&gt;_characterDatabase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;MoveDatabase&lt;/span&gt; &lt;span class="n"&gt;_moveDatabase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;CharacterDatabase&lt;/span&gt; &lt;span class="n"&gt;Characters&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_characterDatabase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;MoveDatabase&lt;/span&gt; &lt;span class="n"&gt;Moves&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_moveDatabase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Installer that binds Database
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GlobalInstaller&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoInstaller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;InstallBindings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromComponentInNewPrefabResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Database"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsSingle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NonLazy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Using the move database in the CombatEvaluator
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CombatEvaluator&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt; &lt;span class="n"&gt;_database&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;PlayerDataCollection&lt;/span&gt; &lt;span class="n"&gt;_players&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CombatEvaluator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PlayerDataCollection&lt;/span&gt; &lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_database&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_players&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DoStuff&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;player1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_players&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetByPlayerNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;player1ActionId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;player1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PlayerData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;player1Move&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Moves&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetMoveById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player1ActionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// do stuff&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The changes made to facilitate dependency injection go hand in hand with improving testability. For example, the setter for the &lt;code&gt;Action&lt;/code&gt; property in &lt;code&gt;PlayerCharacter&lt;/code&gt; not only updates the &lt;code&gt;PlayerData&lt;/code&gt; state, but also invokes a ClientRpc to a specific client in order to update their UI to reflect their move selection. Previously, this ClientRpc was invoked by calling &lt;code&gt;GameUIManager.Instance.SomeMethodClientRpc()&lt;/code&gt;. While I could mock a &lt;code&gt;GameUIManager&lt;/code&gt; object, mock it's property &lt;code&gt;Instance&lt;/code&gt; to return itself, and then mock the method, it's much easier to inject the mock directly into the class being tested.&lt;/p&gt;

&lt;h4&gt;
  
  
  The method being tested
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;get&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_playerData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;set&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;previous&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_playerData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_playerData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;PlayerData&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Health&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_playerData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Health&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;SpecialMeter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_playerData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SpecialMeter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ComboCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_playerData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ComboCount&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;clientRpcParams&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ClientRpcParams&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Send&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ClientRpcSendParams&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;TargetClientIds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;_clientId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="n"&gt;_gameUIManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UpdateActiveSelectionButtonClientRpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;previous&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clientRpcParams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Unit test
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;  &lt;span class="c1"&gt;// 1&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SetActionCorrectlyAndSendsRpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IGameUIManager&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UpdateActiveSelectionButtonClientRpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;// 2&lt;/span&gt;
            &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ClientRpcParams&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;clientRpcParam&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; 
                &lt;span class="nf"&gt;HasCorrectTargetClientId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;clientRpcParam&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;_playerCharacter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientId&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt; &lt;span class="c1"&gt;// 3&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Verifiable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;_playerCharacter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GameUIManager&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;_playerCharacter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AreEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_playerCharacter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// 4&lt;/span&gt;
    &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UpdateActiveSelectionButtonClientRpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAny&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ClientRpcParams&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()),&lt;/span&gt;
        &lt;span class="n"&gt;Times&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Once&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;  &lt;span class="c1"&gt;// 5&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test looks like a lot, but it's actually quite simple. First, with the Unity test framework being built upon NUnit, we gain access to features such as paramaterized test cases (1). In this way, we can test multiple sets of inputs and expected values with a single test method.&lt;/p&gt;

&lt;p&gt;Using the Moq library, we can set up argument validation within the method setup. In this case, we are checking that the method &lt;code&gt;UpdateActiveSelectionButtonClientRpc()&lt;/code&gt; is being invoked with the correct action Id (2), and the correct target clientId (3). This setup is handled within the test, rather than in a &lt;code&gt;SetUp&lt;/code&gt; method because of the need to access the value being tested.&lt;/p&gt;

&lt;p&gt;Besides a typical assert that the input value and the expected value are equal (4), we can also verify that the mocked method was invoked a specific number of times--in this case, once (5).&lt;/p&gt;




&lt;p&gt;That's all for now! There's still room for improvement, of course, and this won't be my only round of refactoring. As I continue development, I'll be looking for opportunities to dive deeper into the Zenject framework and utilize its features to promote clean, quality code (...like using signals for audio/animation control? Could be good!). Once I finish writing test suites for the core systems, I'll be looking to iterate on gameplay design based on feedback from the prototype testing, and then it's on to the look-and-feel first pass.&lt;/p&gt;

&lt;p&gt;In the meantime, check out the &lt;a href="https://github.com/cboveda/ProjectYOMI"&gt;repo&lt;/a&gt; and &lt;a href="https://github.com/users/cboveda/projects/3"&gt;project&lt;/a&gt; to follow my progress.&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>unity3d</category>
      <category>unittest</category>
    </item>
    <item>
      <title>Gameplay Prototype v0.1.0</title>
      <dc:creator>Chris Boveda</dc:creator>
      <pubDate>Sat, 29 Apr 2023 19:23:01 +0000</pubDate>
      <link>https://forem.com/cboveda/prototype-v010-1ejg</link>
      <guid>https://forem.com/cboveda/prototype-v010-1ejg</guid>
      <description>&lt;p&gt;About 10 days ago, around the time of my last post in this series, I laid out my plan for how I would complete the basic gameplay prototype of my game. It would be as barebones as possible, only including the features absolutely necessary to test the core gameplay concept. &lt;/p&gt;

&lt;p&gt;To recap, the vision for the game is an online multiplayer, synchronous turn-based fighting game for iOS and Web with colorful visuals, uniquely stylized characters, and social features such as player profiles, leaderboards, and character customization. The gameplay prototype milestone just includes the "online" and the "turn-based" features. That means no animations, all default UI assets, placeholder character models, and no sound anywhere in the game.&lt;/p&gt;

&lt;p&gt;Last night, I marked that milestone complete and sent a small group of my gaming friends the first build. After so much testing in the Unity editor playing against myself, I got to watch the very first instance of my game being played by other people.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ErUu5xAc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ppn2988gx21wp4ooipsn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ErUu5xAc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ppn2988gx21wp4ooipsn.png" alt="Gameplay prototype screenshot" width="800" height="531"&gt;&lt;/a&gt;&lt;/p&gt;
Don't be misled by the action poses... the characters don't move yet.






&lt;p&gt;The rounds last too long. There were some instances where combat didn't properly resolve. There's no move history being displayed for either player so at times the players felt like they were just "mashing". Without the animations and visual/audio flavor the game feels slow.&lt;/p&gt;

&lt;p&gt;But, the reception was good! I was really nervous to share this first prototype, being as barebones as it is, but I am so glad that I did. I recorded all of the feedback and have distilled it into issues and tasks on my GitHub Project, which will help me to iterate over the design, mechanics and tuning of the game. Overall, the prototype did its job--it allowed me to see if the core gameplay loop is any fun, and based on the feedback, it is! When the play testers found themselves getting pulled into the "one more game" vortex, I knew that was a good sign.&lt;/p&gt;

&lt;p&gt;In the final push to get the protoype completed, I definitely sacrificed some code quality to get there. "Oh I need to track some piece of state? Throw it in the &lt;code&gt;GameData&lt;/code&gt; class," was said multiple times. I have set aside some time to refactor a large chunk of the code pushed out for the prototype in order to make it more maintainable, testable, and readable.&lt;/p&gt;

&lt;p&gt;With that said, there are elements of the code base that I am proud of. Huge shout outs to &lt;a href="https://www.youtube.com/@GameDevGuide"&gt;@GameDevGuide&lt;/a&gt; and their content on YouTube for inspiration for many of these.&lt;/p&gt;




&lt;h2&gt;
  
  
  Progress Bar
&lt;/h2&gt;

&lt;p&gt;I looked for opportunities to create reusable components for the UI, and one such example is the &lt;code&gt;ProgressBar&lt;/code&gt; component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ExecuteInEditMode&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProgressBar&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt; &lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Direction&lt;/span&gt; &lt;span class="n"&gt;fillDirection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt; &lt;span class="n"&gt;fillColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Direction&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OriginHorizontal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OriginHorizontal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Right&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;GetCurrentFill&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;GetCurrentFill&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;fillAmount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fillAmount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fillAmount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fillOrigin&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;fillDirection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fillColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SetCurrent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SetMaximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;maximum&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This component is being used to manage multiple elements of the gameplay UI...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CpNJtiQJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/89lyyoc1zsqhfcktunhi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CpNJtiQJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/89lyyoc1zsqhfcktunhi.png" alt="Highlight of which visual elements are driven by the ProgressBar component" width="800" height="595"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;...and its interface allows it to be controlled in both the Unity editor and script!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rzIYenT9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nurdthywpxfsvdywousv.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rzIYenT9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nurdthywpxfsvdywousv.gif" alt="Demonstration of the progress bar component in Unity Editor" width="800" height="286"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  State Machine
&lt;/h2&gt;

&lt;p&gt;I created a state machine that utilizes an abstract &lt;code&gt;GameBaseState&lt;/code&gt; class and a strategy pattern to manage the different stages of the game. Now I can execute functions of the game system at specific times by calling them in either the &lt;code&gt;EnterState()&lt;/code&gt;, &lt;code&gt;UpdateState()&lt;/code&gt;, or &lt;code&gt;ExitState()&lt;/code&gt; method of their corresponding concrete state object.&lt;/p&gt;

&lt;p&gt;Abstract class for a game state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GameBaseState&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="n"&gt;GameStateMachine&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="n"&gt;GameStateFactory&lt;/span&gt; &lt;span class="n"&gt;_factory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;GameBaseState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GameStateMachine&lt;/span&gt; &lt;span class="n"&gt;currentContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GameStateFactory&lt;/span&gt; &lt;span class="n"&gt;gameStateFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentContext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_factory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gameStateFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;EnterState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;UpdateState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ExitState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CheckSwitchStates&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SwitchState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GameBaseState&lt;/span&gt; &lt;span class="n"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;ExitState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EnterState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurrentState&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"New State: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetType&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a very simple example of a concrete implementation of the abstract &lt;code&gt;GameBaseState&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GameNotReadyState&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;GameBaseState&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;GameNotReadyState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GameStateMachine&lt;/span&gt; &lt;span class="n"&gt;currentContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GameStateFactory&lt;/span&gt; &lt;span class="n"&gt;gameStateFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gameStateFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CheckSwitchStates&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AllPlayersLoaded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;SwitchState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;EnterState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ExitState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;UpdateState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;CheckSwitchStates&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the machine that utilizes Unity &lt;code&gt;MonoBehavior&lt;/code&gt;/&lt;code&gt;NetworkBehaviour&lt;/code&gt; lifecycle methods and a strategy pattern to manage and execute the current state's methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GameStateMachine&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NetworkBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;GameBaseState&lt;/span&gt; &lt;span class="n"&gt;_currentState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;GameStateFactory&lt;/span&gt; &lt;span class="n"&gt;_states&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnNetworkSpawn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;IsServer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;_states&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;GameStateFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_currentState&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_states&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NotReady&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;_currentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EnterState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;IsServer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;_currentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UpdateState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Lightweight State Data
&lt;/h2&gt;

&lt;p&gt;I'm also proud of how I minimized storage of the usable moves for each player using a single byte and bit masking. Part of my refactoring efforts I mentioned above will be to encapsulate all of the bit arithmatic into its own class. For now, though, I'm able to store a single byte and communicate it between host and client with very lightweight packets, and update the state of a specific move just by flipping a bit.&lt;/p&gt;

&lt;p&gt;This is the enum that represents a move's type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CharacterMove&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ScriptableObject&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;LightAttack&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;HeavyAttack&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Parry&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Grab&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Special&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is an example of how the specific bit for a move can be modified to affect state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GameData&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NetworkBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;//These get initialized to 0b11110 when a player object is spawned.&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;NetworkVariable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_usableMoveListPlayer1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;NetworkVariable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_usableMoveListPlayer2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;//...&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CheckShouldSpecialBeEnabled&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_specialMeterPlayer1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="m"&gt;100f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="n"&gt;_usableMoveListPlayer1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;|=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;CharacterMove&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Special&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_specialMeterPlayer2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="m"&gt;100f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="n"&gt;_usableMoveListPlayer2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;|=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;CharacterMove&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Special&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;    

     &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ellipses in this code snippet are carrying a lot of weight.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;...and how the UIManager can subscribe to and update interactable buttons based on that state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GameUIManager&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NetworkBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;//...&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnNetworkSpawn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UsableMoveListPlayer1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnValueChanged&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;UpdateUsableMoveButtons&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnNetworkDespawn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UsableMoveListPlayer1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnValueChanged&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;UpdateUsableMoveButtons&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;UpdateUsableMoveButtons&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;previousValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CharacterMove&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CharacterMove&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;button&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_playerControls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetButtonByType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Button&lt;/span&gt;
            &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interactable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newValue&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;There's a lot more to do. More play testing, balancing damage/health values and round duration, character passive and special design tweaks, debugging, refactoring--not to mention my next major milestone: Look and Feel, First Pass. The initial positive feedback to the core gameplay has reassured me that I'm heading in a good direction with my design choices, and with v0.1.0 behind me, now I'm looking ahead to v0.2.0!&lt;/p&gt;

&lt;p&gt;If you are interested in seeing my progress, the project can be found &lt;a href="https://github.com/users/cboveda/projects/3"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>unity3d</category>
    </item>
    <item>
      <title>Projectizing the Work</title>
      <dc:creator>Chris Boveda</dc:creator>
      <pubDate>Mon, 24 Apr 2023 14:25:49 +0000</pubDate>
      <link>https://forem.com/cboveda/projectizing-the-work-3m6p</link>
      <guid>https://forem.com/cboveda/projectizing-the-work-3m6p</guid>
      <description>&lt;p&gt;A common pitfall in solo development projects, particularly with indie games, is a lack of planning. It can be difficult to define "done" and prioritize tasks without defining clear milestones and the steps needed to meet them. I felt myself losing some momentum on this project because when I would sit down to work on it, I either wasn't sure what I should do, or it felt like what I wanted to do wasn't what I should be doing. Over the past week, I committed some time to projectizing the work, and the impact it has had on my motivation and focus has been immediate and powerful.&lt;/p&gt;

&lt;p&gt;I am utilizing GitHub Projects to allow for easy linking between tasks, branches, and pull requests. My first step was to create three milestones--initial gameplay prototype, CI and testing setup, and a look-and-feel first pass. I have not applied due dates to these milestones, allowing me to work on the project as I am able to. However, just by categorizing the work based on high level objectives, I now have a better idea of task priority, I have the end-state in mind, and my velocity has improved.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b0FceBMB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jtf07yc1dyo8tv4k47ny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b0FceBMB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jtf07yc1dyo8tv4k47ny.png" alt="Project milestones" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For each milestone, I created issues to track the high-level steps necessary to achieve the milestone goal. In the case of the "Gameplay Prototype" milestone, these issues have direct correlation with the features I identified as necessary to begin gathering real world testing feedback from friends and family.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9l1n0xja--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o7zyemf5555h7uds3v79.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9l1n0xja--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o7zyemf5555h7uds3v79.png" alt="Issues for protoype milestone" width="800" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most issues are then broken down into the sub-tasks necessary to close the issue. I have elected not to create individual issues for each of these sub-tasks, but that may be an option for more complex issues in the future.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OEnBuxYP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bqkqeozy94v7yj63qwl9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OEnBuxYP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bqkqeozy94v7yj63qwl9.png" alt="Issue sub-tasks" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Taking the time to plan and projectize the work has greatly improved my motivation and velocity. Just in the past three days I've completed roughly half of the work necessary to create my first playable prototype, where before I was swirling trying to decide what to work on next. I'm very excited to continue working towards delivering a complete game in a controlled and planned way!&lt;/p&gt;

&lt;p&gt;If you are interested in seeing my progress, the project can be found &lt;a href="https://github.com/users/cboveda/projects/3"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>unity3d</category>
    </item>
    <item>
      <title>From Dream to Delivery, Part 2</title>
      <dc:creator>Chris Boveda</dc:creator>
      <pubDate>Sun, 09 Apr 2023 22:18:13 +0000</pubDate>
      <link>https://forem.com/cboveda/from-dream-to-delivery-part-2-166p</link>
      <guid>https://forem.com/cboveda/from-dream-to-delivery-part-2-166p</guid>
      <description>&lt;p&gt;The following is a continuation of my answers to the questions in Dan L. Daglow's "Indie Games: From Dream to Delivery" (2018).&lt;/p&gt;

&lt;h2&gt;
  
  
  Dreams change as we live our lives. What is a game you once really hoped to create, but that is no longer as important to you?
&lt;/h2&gt;

&lt;p&gt;The first game I dreamed about making was a co-operative, competitive hover craft racing game, inspired by the Matrix sequels and their tunnel chase/combat scenes. I also envisioned a dedicated peripheral, similar to steel battalion. &lt;/p&gt;

&lt;h2&gt;
  
  
  What game do you think you may want to build a few years from now that isn’t as important to you today? Why will you care more about it in a few years than you do now?
&lt;/h2&gt;

&lt;p&gt;I enjoy auto-battlers, cooperative pve, rogue-likes, deck builders, and a couple of custom games from the old SC2 days, and I feel like there’s a hidden game idea somewhere in the intersection of those genres that I’d be interested in finding. The reasons I’m not diving into that game first are: 1) it’s less defined than my Yomi game idea, and 2) the Yomi game is more straightforward in terms of technical requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do you have ideas for additional content or follow-up products that can be introduced if the first game is a success?
&lt;/h2&gt;

&lt;p&gt;I like cosmetic elements that are tied to achievement or prowess, as means to showcase one’s skill rather than one’s wallet. I think that the pool of available cosmetic customization could be greatly expanded upon if the base game is successful. &lt;/p&gt;

&lt;h2&gt;
  
  
  Is it the technology involved in the making of the game that interests you? Is it the genre or category of the game? The platform on which you’ll create the original version? The style of interface? Or do you take joy in the visuals of the game? The audio? Some combination of the above choices?
&lt;/h2&gt;

&lt;p&gt;I believe it is a combination of the game style, the technological challenge, and the visual/audio elements of the game that make it interesting for me. The tactile elements of a turn-based fighter like this could be very simple, or they could be very complex—it depends on how deep I want to dive into it and how much time I want to spend on them, and that type of open ended aspect where I can let interest and passion guide me sounds fun, albeit scary from a timeline planning perspective. &lt;/p&gt;

&lt;h2&gt;
  
  
  What makes you believe that this game will draw players’ attention in a crowded market? Is this a completely new idea, or are you setting out to improve on a game that already exists?
&lt;/h2&gt;

&lt;p&gt;My hope is that the game will have enough “flavor” added to the core rock paper scissors mechanics to be enjoyable to repeatedly play. From what I can tell, a turn based fighter is a new concept, but there are many, many rock paper scissors games. &lt;/p&gt;

&lt;h2&gt;
  
  
  If you could describe your indie game to a stranger in just one sentence that would interest him or her, what would that sentence be? What are the three most important features that will make someone want to play your game and spend money on the title?
&lt;/h2&gt;

&lt;p&gt;It’s a competitive turn based fighting game that captures the core mind-game elements of traditional fighters without the mechanical requirements. It will be online and competitive, allow for player customization, and have ranked matchmaking and possibly tournaments. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is the primary mood of your game? (e.g. happy, dark, frightening, goofy, stealthy, foreboding, curious, joyful etc.) What is the pace of your game? (e.g. frenetic, thoughtful, alternating action and strategy, etc.) How long will people play in a typical session, and where will they be playing? For two minutes waiting in line at the grocery store? Fifteen minutes on the bus? Three hours in the living room? One of the first people to play your game has lunch with a friend. What do they say to the friend about how they were feeling as they played?
&lt;/h2&gt;

&lt;p&gt;The primary mood is “playful”. I want the game to be something that can be taken seriously when pushing for ranked points, but also something that is just fun to play even for less serious players. &lt;/p&gt;

&lt;p&gt;The pace will be less frantic than traditional fighters, but also not a slog—even though it will be turn based the turns themselves need to be moderately quick to make players decide their move quickly. &lt;/p&gt;

&lt;p&gt;I imagine the typical play session to be 15+ minutes. I’ll have control over total match length through player hit points, turn timers, and the “best of X” format, and perhaps all of those are different in competitive settings and casual settings. &lt;/p&gt;

&lt;p&gt;I want the words someone uses to describe the game to a friend to be awesome or sick. &lt;/p&gt;

&lt;h2&gt;
  
  
  What will the first two minutes of the player experience be like in your game? Will you have a tutorial? How will users learn how to play?
&lt;/h2&gt;

&lt;p&gt;There will be a tutorial, but I do not know if I would make it mandatory for first time players. But either way, the tutorial will be quick and to the point, so as to not consume too much of the player’s time. &lt;/p&gt;

&lt;h2&gt;
  
  
  What will the first playable prototype of your game include?
&lt;/h2&gt;

&lt;p&gt;The first playable prototype will have full gameplay mechanics and lobby based multiplayer, that’s all. Visuals, matchmaking, cosmetics can all come after I’ve proven that the core mechanics are enjoyable. &lt;/p&gt;

&lt;h2&gt;
  
  
  What kinds of people will want to play your game? What other games do those same people play? What websites do they visit? Where would you find them on social media sites like Facebook, Twitter, Instagram etc.?
&lt;/h2&gt;

&lt;p&gt;I think the core audience will be “cerebral” gamers that enjoy mentally challenging games, but I would hope it finds more widespread appeal. Reddit would be the primary websites I would expect to find and engage with such players. &lt;/p&gt;

&lt;h2&gt;
  
  
  What games are the most similar to your indie title? How is yours different? What makes yours better?
&lt;/h2&gt;

&lt;p&gt;Rock paper scissors is what the game will be most similar to, but I will be aiming to add enough flavor to the mechanics to make it distinct and more replayable. &lt;/p&gt;

&lt;h2&gt;
  
  
  How would you feel if The New York Times, Wired magazine, Oprah Winfrey and your best friend’s mother all said your game sucks?
&lt;/h2&gt;

&lt;p&gt;I would take that pretty hard. If the game isn’t engaging enough to hook people and show them that there’s a skill ceiling to climb for, or if the flavor mechanics are deemed “gimmicky” or not really an improvement over rock paper scissors, then I would view that as a failure if my core design goal. &lt;/p&gt;

&lt;h2&gt;
  
  
  You just won a mega-million-dollar jackpot in the lottery! From this moment on you can do whatever you want to do each day. In this “perfect world” how would you spend the first month when you got back?
&lt;/h2&gt;

&lt;p&gt;How I would ideally spend that time is to establish a routine of enriching activities, like working out and eating well, but more realistically I would spend the time enjoying some of my favorite games—all of which are very time consuming. &lt;/p&gt;

</description>
      <category>devjournal</category>
    </item>
    <item>
      <title>From Dream to Delivery, Part 1</title>
      <dc:creator>Chris Boveda</dc:creator>
      <pubDate>Sun, 09 Apr 2023 22:09:21 +0000</pubDate>
      <link>https://forem.com/cboveda/from-dream-to-delivery-part-1-26dp</link>
      <guid>https://forem.com/cboveda/from-dream-to-delivery-part-1-26dp</guid>
      <description>&lt;p&gt;I've been passionate about video games for the better part of 20 years. I still remember my first time playing capture the flag on Blood Gulch after school with friends and getting bullied by the more experienced players piloting Scorpion tanks. Or the first time I was zergling rushed on a "No Rush 15" map in StarCraft. Or figuring out how to do Mephisto speed farming in Diablo II. More recently, I've been proud to fulfill goals like earning a raid title in Destiny 2, and achieving Cutting Edge in World of Warcraft. These experiences have shaped the fabric of my close friendships over the years, and I'm so thankful to have access to this social, creative, and competetive outlet and this way to bring people togethers even when physical distance or a global pandemic have kept us apart. &lt;/p&gt;

&lt;p&gt;Throughout my time playing games, I've always been fascinated with the development process. I would watch the behind-the-scenes DVD on repeat that came with my copy of Halo 2 (that I picked up at midnight at launch on a school night--thanks for driving me, Dad!). Growing up, my friends and I would dream about what types of games we would enjoy making and playing, and create elaborate storyboards for the major story beats and draw up concept art. Now days, I love watching GDC conference talks on the design process, technical demos for new engines, and early alpha footage for upcoming titles. &lt;/p&gt;

&lt;p&gt;As a recent software graduate exploring the available positions and fields that I could see myself working in, of course video game development has been a consideration. My capstone project for school was to create a browser-based educational game for the NASA Psyche Mission, and I loved every minute of learning the intricacies of the Unity engine to make that game. That experience of collaborating with others in a Scrum environment, producing playable demos to product owners and stakeholders, collecting feedback from playtesters, and iterating on the game to make it engaging, informative, and fun was incredibly enriching.&lt;/p&gt;

&lt;p&gt;Now, as I'm looking to add some projects to my portfolio to demonstrate my skill and aptitude to future employers, I've decided to try my hand at making my own game, solo. One of the first things I do when approaching a new venture is to seek wisdom from others, and learn tips and pitfalls to avoid that they can share from their experiences. Enter "Indie Games: From Dream to Delivery" by Don L. Daglow.&lt;/p&gt;

&lt;p&gt;Daglow starts his book by guiding the reader through a series of questions, with the intent of helping the reader give form to their dream. I've decided to record my answers to these questions here, for posterity and reflection. Part one of those questions follows below.&lt;/p&gt;

&lt;p&gt;Credit to Dan L. Daglow &lt;a href="https://www.amazon.com/Indie-Games-Delivery-Don-Daglow-ebook/dp/B079NSDVSF"&gt;"Indie Games: From Dream to Delivery"&lt;/a&gt; (2018) for all of the questions below.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the game that you have you been thinking about creating that inspired you to read this book?
&lt;/h2&gt;

&lt;p&gt;A multiplayer turn-based fighting game that plays into the concept of “yomi” from traditional fighting games, and taps into the satisfying feeling of “getting into their head” when pulling off strings of predictive moves and combos. The simple mechanics of the game can be spiced up with flashy visuals paired with frozen/dilated time to build excitement. &lt;/p&gt;

&lt;h2&gt;
  
  
  What objective would you like to meet by launching your indie game?
&lt;/h2&gt;

&lt;p&gt;The design of the game feels like a good introduction to some fundamentals of multiplayer gaming—profiles, stats, matchmaking, lobbies, chat, cosmetic customization, and networked gameplay itself. &lt;/p&gt;

&lt;h2&gt;
  
  
  What makes you passionate about this game? In what game genre or category will reviewers classify it?
&lt;/h2&gt;

&lt;p&gt;My favorite moments in multiplayer gaming are when I can utilize my skill to predict and capitalize on an opponents move. That feeling is immensely satisfying and joyous to me. Whether it’s a tech read in Super Smash Brothers: Melee, or a dunk in Rocket League, or a well-timed prediction for a shot in Overwatch, they make me feel like I’m a master of not just my own technical skill, but also my opponents mind. Now of course, being turn-based, this game idea would not tap into that mechanical side of that feeling, but instead it would be purely focused on the predictive “mind reading” side. I think this game would be classified as a tactical fighter. &lt;/p&gt;

&lt;h2&gt;
  
  
  When did you first become interested in the play style, genre, category or theme that lies at the core of the game you want to create? What was the first game you played in this genre that made you love the category?
&lt;/h2&gt;

&lt;p&gt;I’m not aware of a game that exists in this genre, or sub genre of fighting, but games that have inspired this idea are Melee and Rocket League. In both games, a player must demonstrate both mastery of game mechanics and the ability to “read” their opponent to achieve victory. Pulling off a “hard read” has always been an experience that makes me giddy. &lt;/p&gt;

&lt;p&gt;Edit: I have since learned of the existence of another indie game, Your Only Move Is Hustle (aka YOMI Hustle). I believe that my game idea is distinct enough to be considered completely separate and not derivative of this game, given that YOMI Hustle is based on frame data and is more of a "Tool Assisted"-esque style of game, while mine is more focused on simple mechanics and the rock-paper-scissors element at the core of the moveset.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the first platform on which you plan to release your game? What other platforms do you intend to support simultaneously, or soon after your initial launch?
&lt;/h2&gt;

&lt;p&gt;I believe the simplicity of the game lends itself to being a mobile game, however I have an unconfirmed suspicion that most people do not want to have to download an app to play a game. If I could serve the game through a mobile friendly website for greatest exposure, then release optimized iOS and Android versions via app stores later, that feels like it would be the best choice. However, I’m not sure how well a Unity-built WebGL game would work on a mobile internet browser. This may require some prototyping to test and flesh out. &lt;/p&gt;

&lt;h2&gt;
  
  
  Will your game require any additional hardware or peripherals for users to play, like VR headsets or alternate interface devices?
&lt;/h2&gt;

&lt;p&gt;No, the game at its base form will only require interacting with 3-5 buttons, besides social/menu buttons. &lt;/p&gt;

&lt;h2&gt;
  
  
  In what programming language (or languages) will your game be coded? Will you use a third party engine as the foundation for your game, or create it entirely from scratch? If you’re using an engine, will it be Unity 2D or 3D? The Unreal Engine? CryEngine? GameMaker? Something else?
&lt;/h2&gt;

&lt;p&gt;If I want the game to be mobile browser friendly, given that WebGL has limited support in mobile browser, I think I will be forced to write it in a JavaScript based web framework. &lt;/p&gt;

&lt;p&gt;If I accept that it will need to be an app, then I will build it in Unity with C#. Whether it is 2D or 3D is an interesting question. 3D may be more challenging, but would allow me some flexibility when it comes to visuals. Or, if I go with a more retro/pixelated aesthetic, 2D would work just fine. &lt;/p&gt;

&lt;h2&gt;
  
  
  Is this your first game of this scale? Have you pursued lots of indie games in your life? Have you worked on other initiatives or startups that were similar in size to this project?
&lt;/h2&gt;

&lt;p&gt;Yes, this would be the largest game I have worked on. Prior “games” were all for school assignments, and had very simple, if any, interfaces. The game would be simpler in scale than the capstone project I worked on, but still the largest solo endeavor. &lt;/p&gt;

</description>
      <category>devjournal</category>
    </item>
  </channel>
</rss>
