<?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>DEV Community: Mike Evans</title>
    <description>The latest articles on DEV Community by Mike Evans (@jigsawpsycho).</description>
    <link>https://dev.to/jigsawpsycho</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%2F2044851%2Fa385af13-3bb0-4dc5-ac12-bc2ad3d95c25.png</url>
      <title>DEV Community: Mike Evans</title>
      <link>https://dev.to/jigsawpsycho</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jigsawpsycho"/>
    <language>en</language>
    <item>
      <title>Simple Unity to Nvim Bridge for Nvim IDEs</title>
      <dc:creator>Mike Evans</dc:creator>
      <pubDate>Tue, 09 Sep 2025 09:57:16 +0000</pubDate>
      <link>https://dev.to/jigsawpsycho/simple-unity-to-nvim-bridge-for-nvim-ides-23o7</link>
      <guid>https://dev.to/jigsawpsycho/simple-unity-to-nvim-bridge-for-nvim-ides-23o7</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/Byte-Blast-Ltd/nvim-unity-bridge" rel="noopener noreferrer"&gt;https://github.com/Byte-Blast-Ltd/nvim-unity-bridge&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A simple, scuffed interface between unity and nvim IDEs. It calls code from &lt;code&gt;com.unity.ide.vscode&lt;/code&gt; as that was the simplest, quickest way to get it working.&lt;/p&gt;

&lt;p&gt;The tool searches for nvim at &lt;code&gt;/opt/nvim/nvim&lt;/code&gt;. If your path is different, feel free to change it in &lt;code&gt;Editor/Resources/unity_nvim.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This currently only supports linux. I don't plan to look at windows anytime soon.&lt;/p&gt;

&lt;h1&gt;
  
  
  Features
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Opens nvim automatically when opening scripts as it would with other ides.&lt;/li&gt;
&lt;li&gt;Restarts your lsp whenever Unity recompiles its scripts&lt;/li&gt;
&lt;li&gt;Won't open a new nvim window for every script&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Dependencies (Tested against)
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.10.12&lt;/li&gt;
&lt;li&gt;pynvim (Install via &lt;code&gt;python3 -m pip install --user --upgrade pynvim&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;nvr 2.5.1&lt;/li&gt;
&lt;li&gt;xdotool 3.20160805.1&lt;/li&gt;
&lt;li&gt;GNOME Terminal 3.44.0 using VTE 0.68.0 +BIDI +GNUTLS +ICU +SYSTEMD&lt;/li&gt;
&lt;li&gt;nvm-lspconfig&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Simple Unity Package for Unity Support of Nvim IDEs</title>
      <dc:creator>Mike Evans</dc:creator>
      <pubDate>Tue, 09 Sep 2025 09:57:16 +0000</pubDate>
      <link>https://dev.to/jigsawpsycho/simple-unity-to-nvim-bridge-for-nvim-ides-2ke9</link>
      <guid>https://dev.to/jigsawpsycho/simple-unity-to-nvim-bridge-for-nvim-ides-2ke9</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/Byte-Blast-Ltd/nvim-unity-bridge" rel="noopener noreferrer"&gt;https://github.com/Byte-Blast-Ltd/nvim-unity-bridge&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A simple, scuffed interface between unity and nvim IDEs. It calls code from &lt;code&gt;com.unity.ide.vscode&lt;/code&gt; as that was the simplest, quickest way to get it working.&lt;/p&gt;

&lt;p&gt;The tool searches for nvim at &lt;code&gt;/opt/nvim/nvim&lt;/code&gt;. If your path is different, feel free to change it in &lt;code&gt;Editor/Resources/unity_nvim.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This currently only supports linux. I don't plan to look at windows anytime soon.&lt;/p&gt;

&lt;h1&gt;
  
  
  Features
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Opens nvim automatically when opening scripts as it would with other ides.&lt;/li&gt;
&lt;li&gt;Restarts your lsp whenever Unity recompiles its scripts&lt;/li&gt;
&lt;li&gt;Won't open a new nvim window for every script&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Dependencies (Tested against)
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.10.12&lt;/li&gt;
&lt;li&gt;pynvim (Install via &lt;code&gt;python3 -m pip install --user --upgrade pynvim&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;nvr 2.5.1&lt;/li&gt;
&lt;li&gt;xdotool 3.20160805.1&lt;/li&gt;
&lt;li&gt;GNOME Terminal 3.44.0 using VTE 0.68.0 +BIDI +GNUTLS +ICU +SYSTEMD&lt;/li&gt;
&lt;li&gt;nvm-lspconfig&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Porting a PHP Based Web Game to Mobile Part 4</title>
      <dc:creator>Mike Evans</dc:creator>
      <pubDate>Wed, 16 Apr 2025 09:40:07 +0000</pubDate>
      <link>https://dev.to/jigsawpsycho/porting-a-php-based-web-game-to-mobile-part-4-34ei</link>
      <guid>https://dev.to/jigsawpsycho/porting-a-php-based-web-game-to-mobile-part-4-34ei</guid>
      <description>&lt;p&gt;Alright, we've got our units in the game and they're looking rather flash (or again, you know, exactly the same as the ones in the web version). Now we need to make the rest of the map look that good. I've already taken the liberty of getting started on our next item, terrain. Terrain, thankfully, was for the most part a lot simpler than units. There are no gifs to route through Aseprite via bulk processing cli commands, no country names to apply before searching for their sprites and thankfully this time around, I was able to get in touch with a member of the web versions developer community, they were more than happy to provide the art assets for the game, which meant I had easy access to all the terrain assets. I'll just pretend I don't see those unit assets that definitely wouldn't have come in handy in part 3... In all fairness, it actually would have taken longer to get each of those setup as animations than what it would to just route the gifs through Aseprite and then Unity, so I'm not too fussed over that.&lt;/p&gt;

&lt;p&gt;Enough about units though, we're here for terrain. So let's get caught up, we started off with our map from last time, if you don't recall what it looked like, here you go:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpprzli8ztm4oej6b39t6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpprzli8ztm4oej6b39t6.gif" alt="Image description" width="400" height="400"&gt;&lt;/a&gt;&lt;br&gt;
Terrain, much like its unit counterpart, has a sprite who's name can be derived from the fields in the terrain object that we parse from the game.php response HTML (let's just call it, 'The One True HTML'). So, much like before, all we need to do is download all of the terrain files, place them into our new &lt;code&gt;Resources/Terrain&lt;/code&gt; directory and tell our code to look in that directory when it is dealing with a terrain tile, simple enough. We then run into our first hurdle, what about terrain tiles that have variants? For example, Road tiles. These tiles are for the same general type of terrain, but they are variants for things like, vertical roads, horizontal roads, turning roads, etc. When we downloaded all the art for the game, they came in a separate directory entirely and unlike the other terrain sprites, these ones don't match their server counterpart names one to one. Not a big deal as the difference isn't huge, the sprites I've received are only missing the "Road" part of VRoad, so just "V.png". This appears to be consistent across all the sprites, so I can just fill that part in via code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    private List&amp;lt;string&amp;gt; terrainSpecials = new List&amp;lt;string&amp;gt;()
    {
        "Pipe",
        "Road",
        "River",
        "Shoal",
        "Sea"
    };

    string terrainTypePath = "Terrain";
    string specialTerrainType = terrainSpecials.Find(x =&amp;gt; unitName.Contains(x));
    if(specialTerrainType != null)
    {
        terrainTypePath = $"Terrain/{specialTerrainType}";
        if(specialTerrainType != unitName)
            unitName = unitName.Replace($"{specialTerrainType} ", "").Replace($"{specialTerrainType}", "");
    }
    string finalPath = $"{terrainTypePath}/{unitName}";
    Sprite terrainSprite = Resources.Load&amp;lt;Sprite&amp;gt;(finalPath);
    spriteRenderer.sprite = terrainSprite;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I know what you're going to say, "Hey Mike, that's cheating, I can clearly see you've already gone and done this for all the other special terrain types too, that's not fair. Why not take us through the issues you had for each?" And I understand, we're moving fast, but I assure you, there's a reason for it. That is because, we are about to grind to a halt, because you see, that code above is not fully working. To understand why, let me show you where we are at now, first.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzot7ar9ert2ft68kvh0c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzot7ar9ert2ft68kvh0c.png" alt="Image description" width="657" height="656"&gt;&lt;/a&gt;&lt;br&gt;
It looks pretty good right! But I know what you're thinking (aside from the gaping void that seeks to consume all above each mountain, we'll get to that), the water looks weird, right? Yes, reader whom I do not know, the water does look weird, why would that be?&lt;/p&gt;

&lt;p&gt;Do you remember how I told you that everything we need to know about which sprite to use could be found in each tile's json representation, meaning we could have a super easy ride through this project and have to do little to no actual development? Okay, that last part was a bit of a fabrication, but I think you see what I'm getting at, the json data in the The One True HTML, does not contain which type of Sea or Shoal sprite needs to be used. That data can not be found in the tiles name, nor in any other field in string format that could simply be parsed into its sprite name. Sure, it could maybe map to the tile_id found in each tile's data, but mapping every tile_id to its matching sprite name would be a tedious headache, something I would never do, right?...&lt;/p&gt;

&lt;p&gt;Well, let's see just how much of a tedious headache it would be compared to its alternative. That alternative being, obtaining the data of tiles around Shoal/Sea tiles, and using that to decide which sprite to spawn on our own. Before we even start coding, let's think about it for a second. What outcome are we trying to achieve, i.e. how should this look when it's done? To know that, we luckily have an already functioning map to reference in the web version.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F80eq1x1n9a9nghcw6ss2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F80eq1x1n9a9nghcw6ss2.png" alt="Image description" width="731" height="729"&gt;&lt;/a&gt;&lt;br&gt;
We see a pretty standard pattern, everywhere water meets land, a different sprite is used depending on how many sides surround said water, are land. It does however look like we have two exceptions, at least that we can see here, namely, mountains and bridges, those don't seem to be recognized as "land" necessarily. So how many sprites would we need to take into account? According to the sprites I received, it's a lot. 85 sprites for shoal, and 49 for sea. Sheesh... Okay, maybe we just double check one last time if there's a way to do this with the data we already have (and that doesn't involve mapping each of these 134 sprites to a unique id). &lt;/p&gt;

&lt;p&gt;So I searched for, admittedly, not that long of a time, before arriving at the conclusion that, this would be simpler if we just chose the sprite to use ourselves. Taking a look at the sprite names we have downloaded:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyjddtznwpu6epsb3n762.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyjddtznwpu6epsb3n762.png" alt="Image description" width="274" height="405"&gt;&lt;/a&gt;&lt;br&gt;
We can see first see the letter A, which to be honest I have no idea the meaning behind, but looking a little further down we can start to see a pattern we do know the meaning behind.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9tldi5nfecef1lasqcur.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9tldi5nfecef1lasqcur.png" alt="Image description" width="529" height="390"&gt;&lt;/a&gt;&lt;br&gt;
That's right, North, South, East and West and various combinations of them, with an order that doesn't seem to change, this should be perfect to at least get us something on the board. So let's get to implementing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private string GetTerrainFilename(TileInfo tileInfo)
    {
        string terrainName = tileInfo.Name;
        string terrainTypePath = "Terrain";
        string specialTerrainType = terrainSpecials.Find(x =&amp;gt; terrainName.Contains(x));
        if(tileInfo.Name.Contains("Shoal") || tileInfo.Name.Contains("Sea"))
        {
            terrainName = GetWaterTileName(tileInfo);
        }
        if (specialTerrainType != null)
        {
            terrainTypePath = $"Terrain/{specialTerrainType}";
            if (specialTerrainType != terrainName)
                terrainName = terrainName.Replace($"{specialTerrainType} ", "").Replace($"{specialTerrainType}", "");
        }
        return $"{terrainTypePath}/{terrainName}";
    }

    private List&amp;lt;string&amp;gt; waterAdjacentNonInterruptors = new List&amp;lt;string&amp;gt;()
    {
        "Sea",
        "Shoal",
        "Mountain",
        "Bridge",
    };

    private string GetWaterTileName(TileInfo tileInfo)
    {
        string terrainName;
        string[] adjascentTileNames = GetCardinalAdjascentTileNames((int)tileInfo.X, (int)tileInfo.Y);
        terrainName = "";
        for (int i = 0; i &amp;lt; adjascentTileNames.Length; i++)
        {
            if (adjascentTileNames[i] == null) continue;
            bool isWaterCutoffTile = waterAdjacentNonInterruptors.Find(x =&amp;gt; adjascentTileNames[i].Contains(x)) == null;
            if (isWaterCutoffTile)
            {
                if (terrainName.Length != 0) terrainName += "-";
                terrainName += ((RelativeDirection)i).ToString();
            }
        }

        return terrainName;
    }

private string[] GetCardinalAdjascentTileNames(int x, int y)
    {
        string[] tileNames = new string[4];
        TileInfo[] tiles = new TileInfo[4];
        tiles[0] = terrainInfo.Tiles.Find(t =&amp;gt; t.X == x &amp;amp;&amp;amp; t.Y == y - 1); //north
        tiles[1] = terrainInfo.Tiles.Find(t =&amp;gt; t.X == x + 1 &amp;amp;&amp;amp; t.Y == y); //south
        tiles[2] = terrainInfo.Tiles.Find(t =&amp;gt; t.X == x &amp;amp;&amp;amp; t.Y == y + 1); //east
        tiles[3] = terrainInfo.Tiles.Find(t =&amp;gt; t.X == x - 1 &amp;amp;&amp;amp; t.Y == y); //west

        for(int i = 0; i &amp;lt; tiles.Length; i++)
        {
            if(tiles[i].IsValid())
            {
                tileNames[i] = tiles[i].Name;
            }
        }
        return tileNames;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alrighty, that should do the trick for now, let's take a look at what we got.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9el80kwtzh54jl7k6puv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9el80kwtzh54jl7k6puv.png" alt="Image description" width="644" height="643"&gt;&lt;/a&gt;&lt;br&gt;
Ah- woops, 2 problems here, firstly we forgot about buildings, so let's just tweak our code to also check for adjacent buildings if there is no adjacent terrain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private string[] GetCardinalAdjascentTileNames(int x, int y)
    {
        string[] tileNames = new string[4];
        TileInfo[] tiles = new TileInfo[4];
        tiles[0] = terrainInfo.Tiles.Find(t =&amp;gt; t.X == x &amp;amp;&amp;amp; t.Y == y - 1); //north
        tiles[1] = terrainInfo.Tiles.Find(t =&amp;gt; t.X == x + 1 &amp;amp;&amp;amp; t.Y == y); //south
        tiles[2] = terrainInfo.Tiles.Find(t =&amp;gt; t.X == x &amp;amp;&amp;amp; t.Y == y + 1); //east
        tiles[3] = terrainInfo.Tiles.Find(t =&amp;gt; t.X == x - 1 &amp;amp;&amp;amp; t.Y == y); //west

        for(int i = 0; i &amp;lt; tiles.Length; i++)
        {
            if(tiles[i].IsValid())
            {
                tileNames[i] = tiles[i].Name;
            }
        }

        if(string.IsNullOrEmpty(tileNames[0])) tileNames[0] = buildingsInfo.Tiles.Find(t =&amp;gt; t.X == x &amp;amp;&amp;amp; t.Y == y - 1).IsValid() ? "Building" : null; //north
        if(string.IsNullOrEmpty(tileNames[1])) tileNames[1] = buildingsInfo.Tiles.Find(t =&amp;gt; t.X == x + 1 &amp;amp;&amp;amp; t.Y == y).IsValid() ? "Building" : null; //south
        if(string.IsNullOrEmpty(tileNames[2])) tileNames[2] = buildingsInfo.Tiles.Find(t =&amp;gt; t.X == x &amp;amp;&amp;amp; t.Y == y + 1).IsValid() ? "Building" : null; //east
        if(string.IsNullOrEmpty(tileNames[3])) tileNames[3] = buildingsInfo.Tiles.Find(t =&amp;gt; t.X == x - 1 &amp;amp;&amp;amp; t.Y == y).IsValid() ? "Building" : null; //west

        return tileNames;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not the prettiest, but pretty can wait. The second problem we had was two missing sprites on either side of the map. Upon further inspection, we learn that these are the only 2 tiles, that are considered as being entirely without a bordering land tile. Our code reveals that &lt;code&gt;terrainName = "";&lt;/code&gt; here in our &lt;code&gt;GetWaterTileName&lt;/code&gt; method, we will return an empty string  if we have no neighboring land tiles. That's no good, let's return the original tile name instead so it can use the tile default.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private string GetWaterTileName(TileInfo tileInfo)
    {
        string terrainName;
        string[] adjascentTileNames = GetCardinalAdjascentTileNames((int)tileInfo.X, (int)tileInfo.Y);
        terrainName = "";
        for (int i = 0; i &amp;lt; adjascentTileNames.Length; i++)
        {
            if (adjascentTileNames[i] == null) continue;
            bool isWaterCutoffTile = waterAdjacentNonInterruptors.Find(x =&amp;gt; adjascentTileNames[i].Contains(x)) == null;
            if (isWaterCutoffTile)
            {
                if (terrainName.Length != 0) terrainName += "-";
                terrainName += ((RelativeDirection)i).ToString();
            }
        }

        return terrainName == "" ? tileInfo.Name : terrainName;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's take a look how things have turned out.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuwhd6himjot5gqjnn381.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuwhd6himjot5gqjnn381.png" alt="Image description" width="638" height="637"&gt;&lt;/a&gt;&lt;br&gt;
Much better, now all that is left are those pesky all consuming voids in each of the mountain tiles. From what I can tell, this is present in the actual sprite as well, so unsure how we'll fix this without just finding one that isn't broken. Let's see what we can do.&lt;/p&gt;

&lt;p&gt;Looking at the sprite info, mountains are 16 x 21 pixels, whereas other sprites are all 16 x 16, we may be able to resolve the issue by simply moving the graphic up a few pixels, or tweaking the pivot on the sprite. Let's go with the latter as there are only 3 mountain sprites in the game anyway.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzrub5a9hienofr1nvz9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzrub5a9hienofr1nvz9.png" alt="Image description" width="622" height="622"&gt;&lt;/a&gt;&lt;br&gt;
Awesome! It does seem like some of the mountains are now being cut off when below a building, but I suspect that this problem will go away once we add the actual buildings to the board. Buildings can wait though, we'll tackle those next time!&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>csharp</category>
      <category>php</category>
    </item>
    <item>
      <title>Porting a PHP Based Web Game to Mobile Part 3</title>
      <dc:creator>Mike Evans</dc:creator>
      <pubDate>Sat, 12 Apr 2025 07:38:59 +0000</pubDate>
      <link>https://dev.to/jigsawpsycho/porting-a-php-based-web-game-to-mobile-part-3-2nn8</link>
      <guid>https://dev.to/jigsawpsycho/porting-a-php-based-web-game-to-mobile-part-3-2nn8</guid>
      <description>&lt;p&gt;Now I know I said I would just use double digits for the edge cases, but after some internal deliberation, I decided, why not just use the unit sprites instead? We have to do it at some point anyway, and if my theory that the name maps straight to a sprite name is correct, there's a really quick and easy way we can get all the sprites recognized and rendered.&lt;/p&gt;

&lt;p&gt;The plan is simple; all we need to do is drop all of the game sprites into a folder that persists to the game's build. Resources or StreamingAssets will do, yes I see you addressables, but you give me headaches, so you can wait till the next project. Then we just point to the appropriate sprite directly, using the unit's unit_name value. For simplicity in loading objects directly into the appropriate type, we'll use Resources. If the picture still isn't quite forming in your head, this might help:&lt;br&gt;
The file would look like:&lt;br&gt;
&lt;code&gt;Resources/Units/greentank.png&lt;/code&gt;&lt;br&gt;
and the corresponding code to load it would look something like:&lt;br&gt;
&lt;code&gt;Resources.Load&amp;lt;Unit&amp;gt;($"Units/{unit_name}")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let's see if this is as easy as I'm hoping it will be...&lt;/p&gt;

&lt;p&gt;He said, 4 hours ago...&lt;/p&gt;

&lt;p&gt;As you may have guessed, it was, in fact, not that easy. &lt;/p&gt;

&lt;p&gt;Firstly, there is no easy way to get the sprites of every unit in the game, and certainly not of every unit for every army (there are many armies in the game, and the number only increases as the community makes more). I will likely have to write a script to pull each of the sprites from the site in the future, for now however, I will just focus on getting one color on the board. This still meant I needed to download all the sprites for each unit, only when downloading the sprites, they don't come as sprites, but rather as gifs (the website pretty much always displays a unit as a gif as opposed to a static image). This presents an issue; Unity does not like gifs, it has no support for importing them aside from just discarding everything but the first frame. This would be fine, if that file then worked when trying to load it as a Sprite via Resources.Load, since I'm writing this, you can imagine it did not work that way. It could load as a Texture2D, but not as a Sprite (despite using the Sprite's name when trying to load it). Now, I could convert the Texture to a Sprite, but since I needed to eventually get these units animating like on the website anyway, I decided to instead just tackle that now.&lt;/p&gt;

&lt;p&gt;Which brings us to our next issue, how do we get these converted relatively quickly into animated sprites, without taking each file, uploading it to a gif splitter, downloading the animation frames, re-importing back to Unity, and finally rebuilding the animation there. This would take a very very long time, and building a tool to do this for me, would likely take just as long. The answer? Aseprite. Aseprite is a pixel art tool, and the game we are porting just so happens to all be pixel art. Aseprite also supports gifs, it can automatically turn them into animations. And can you guess what Unity supports importing? That's right, Aseprite files. So, while still tedious, saving each of the pngs, importing them in bulk into Aseprite before saving each one as a .aseprite file, was the quickest way I could get these gifs into a usable format in Unity. And to be honest, I think it turned out quite nicely. With a little code to instead load an &lt;code&gt;AnimatorController&lt;/code&gt; from Resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Because we only have the green army sprites at the moment
                string unitGreenArmyName = "ge" + tileInfo.Name.ToLower();
                string finalPath = $"Units/{unitGreenArmyName}";
                AnimatorController unitAnimatorController = Resources.Load&amp;lt;AnimatorController&amp;gt;(finalPath);
                tileGameObject.GetComponentInChildren&amp;lt;Animator&amp;gt;().runtimeAnimatorController = unitAnimatorController;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is rather satisfying! &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flncdpw5rvjkfxuxez3r1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flncdpw5rvjkfxuxez3r1.gif" alt="Image description" width="321" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although, we still have an issue of not knowing who is on who's team, for now though we can just assign a color to each country (there can be a max of 16 countries in a game) and set the units and buildings accordingly. We can deal with the custom sprites for each country later. Let's take a look at how that looks&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4m6frw193j590fz0bvf7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4m6frw193j590fz0bvf7.png" alt="Image description" width="629" height="628"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yikes... I don't think I can stand to look at that much longer. Now, I know I said we wouldn't do this now, but we've already got all this momentum, we may as well take it all the way. Let's get every unit for every army rendering correctly, now we're getting serious. &lt;/p&gt;

&lt;p&gt;To do this, we need to, as I mentioned before, fetch every unit for every army (there are in actuality 19 total armies in the game). Each army has a color palette and each army's units can have completely different sprites and animations. Luckily, the unit gif names from the website all follow the same schema: &lt;code&gt;{countryCode}{unitName}.gif&lt;/code&gt;. And they can all be located under the same route &lt;code&gt;https://awbw.amarriner.com/terrain/ani/&lt;/code&gt;. We are also fortunate enough to have an absolute GIGA file containing pretty much all of the games metadata (if you still remember the response HTML from game.php). That file has a line that starts with &lt;code&gt;const genericUnits =&lt;/code&gt; who's value is a JSON that contains information about all units in the game. This is very useful. We can parse this, much the same as we parsed our in game unit information before, combine the &lt;code&gt;countries_code&lt;/code&gt; and &lt;code&gt;units_name&lt;/code&gt; variables, slap .gif on the end and amend our url above to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public const string DOMAIN = "https://awbw.amarriner.com";
    public const string UNIT_GIF_ENDPOINT = "/terrain/ani/";
    public void OnGUI()
    {
        if (GUILayout.Button("Download All Units"))
        {
            StartCoroutine(FetchUnitGifs());
        }
    }

    public IEnumerator FetchUnitGifs()
    {
        string gameInfo = File.ReadAllText(Path.Combine(Application.streamingAssetsPath, "gamephp.html"));
        GameInfoParser gameInfoParser = new GameInfoParser();
        UnitsInfo unitsInfo = gameInfoParser.ParseGenericUnitInfos(gameInfo);
        List&amp;lt;Country&amp;gt; countries = Resources.LoadAll&amp;lt;Country&amp;gt;("Countries").ToList();
        foreach(var country in countries)
        {
            foreach(var tile in unitsInfo.Tiles)
            {
                string fileName = country.code + tile.Name.ToLower() + ".gif";
                fileName = fileName.Replace(" ", "");
                UnityWebRequest uwr = new UnityWebRequest(DOMAIN + UNIT_GIF_ENDPOINT + fileName);
                uwr.downloadHandler = new DownloadHandlerFile("Resources/" + fileName);
                yield return uwr.SendWebRequest();
            }
        }
        Debug.Log("finished");
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And voila, we have downloaded every unit's .gif in the game. Now, to get them all into Aseprite. To my absolute delight, Aseprite are just built different and already have a way of bulk exporting files via CLI. Because I like to torture myself, I choose to do nye all my cli work with powershell. Meaning I need to make a function in powershell that will cycle through all my .gif files and tell Aseprite to convert each to a .aseprite file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Function AsepriteConvert([string] $fileType)
{
    Get-ChildItem -Filter "*.$fileType" | 
        Foreach-Object { 
            $fullName = $_.FullName
            $newName = $fullName.Replace(".$fileType", ".aseprite")
            Start-Process "C:\Program Files (x86)\Steam\steamapps\common\Aseprite\Aseprite.exe" -ArgumentList "--batch $fullName --save-as $newName"
        } 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, we can navigate to our directory where we downloaded all our gifs, and run our command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AsepriteConvert -fileType gif
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have a nice collection of over 450 .aseprite files. We add them to Unity, before filtering by &lt;code&gt;t:animatorcontroller&lt;/code&gt; and moving all the animator controllers into our Resources/Units folder. We're almost there, a little bit of tweaking now is all we need, once we have added country code to our &lt;code&gt;TileInfo&lt;/code&gt; object and connected it up to the corresponding &lt;code&gt;ServerUnitInfo&lt;/code&gt; during unit generation, we have our final product - visually speaking - for units. And I think it looks awesome, well, exactly the same as the web version, which is exactly what we want.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpprzli8ztm4oej6b39t6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpprzli8ztm4oej6b39t6.gif" alt="Image description" width="400" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now I know improvements can still be made, for starters, should there be another community event where yet another army is introduced to the game, I will need to add that army as well. A potential solution, might be to just swap out armies I don't have, for ones I do, after all there may be 19 armies, but there will only ever be 16 players in a game, so this is a viable workaround. That is something to think about when it becomes a problem, as for next time, we'll need to do what we did here, but for the terrain and buildings, hopefully with all the work we've done here, we can reuse what we've done to do those fairly easily.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>php</category>
      <category>unity3d</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Porting a PHP Based Web Game to Mobile Part 2</title>
      <dc:creator>Mike Evans</dc:creator>
      <pubDate>Thu, 10 Apr 2025 12:13:18 +0000</pubDate>
      <link>https://dev.to/jigsawpsycho/porting-a-php-based-web-game-to-mobile-part-2-30kk</link>
      <guid>https://dev.to/jigsawpsycho/porting-a-php-based-web-game-to-mobile-part-2-30kk</guid>
      <description>&lt;p&gt;First things first; we've got a case of inverted map syndrome. When parsing the game.php response for this map:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Firqwv6bp4avm2qxp3amp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Firqwv6bp4avm2qxp3amp.png" alt="Image description" width="585" height="585"&gt;&lt;/a&gt;&lt;br&gt;
We get the following output:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4169hifzrjeh0t8z238u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4169hifzrjeh0t8z238u.png" alt="Image description" width="549" height="549"&gt;&lt;/a&gt;&lt;br&gt;
Here, green refers to terrain, grey to buildings and red to units. You might notice that the map is inverted along the x axis. What should be at the top of the map is at the bottom, and vice versa. A simple enough fix, just tweaking our tile positions after instantiation. Unity (and to be honest, most all engines that deal with positions), deal with positive Y as upwards. AWBW however, takes positive Y in the downward direction, so to account for this in unity, we just need to invert the Y position of the tile when spawning it. Inverting like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;foreach (TileInfo buildingTile in buildingsInfo.Tiles)
        {
            GenerateMapTile(TileType.Building)
                .transform.localPosition = new Vector3((int)buildingTile.X, (int)-buildingTile.Y, 0);
        }

        foreach (TileInfo terrainTile in terrainInfo.Tiles)
        {
            GenerateMapTile(TileType.Terrain)
                .transform.localPosition = new Vector3((int)terrainTile.X, (int)-terrainTile.Y, 0);
        }

        foreach(TileInfo unitTile in unitsInfo.Tiles)
        {
            GenerateMapTile(TileType.Unit)
                .transform.localPosition = new Vector3((int)unitTile.X, (int)-unitTile.Y, 0);
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That seems to do the trick, as we now have a map that matches - at least in spirit - the map we are trying to reproduce.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6qd0ur8s972dse4obqqs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6qd0ur8s972dse4obqqs.png" alt="Image description" width="617" height="620"&gt;&lt;/a&gt;&lt;br&gt;
Now, we need to distinguish between the different units, buildings and terrain on the board. As we are just blocking out the game functionality at the moment, the first thing that comes to mind is just a letter to represent each unit. Since you will never have both a building and terrain on the same tile, we can do the same for buildings and terrain. For buildings and terrain we'll put the letter in one of the corners, and for the units we'll put it in the center. Let's see how that goes.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F597zqlp2l9tqaqp6q01o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F597zqlp2l9tqaqp6q01o.png" alt="Image description" width="590" height="590"&gt;&lt;/a&gt;&lt;br&gt;
That's pretty good! Now I won't lie, I did run into a few speed bumps, firstly, when a country controls a building, that country's name is included in the building name. I wasn't able to find another way to distinguish building types, so I had to just filter out building names like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static List&amp;lt;string&amp;gt; BUILDING_NAMES = new List&amp;lt;string&amp;gt;
    {
        "CITY",
        "BASE",
        "AIRPORT",
        "TOWER",
        "HQ",
        "LAB"
    };

string tileName = tileInfo.Name;
        string buildingName = GameInfoUtils.BUILDING_NAMES.Find(x =&amp;gt; tileName.ToUpper().Contains(x));
        if(buildingName != null) tileName = buildingName;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not the cleanest in the world, but it works. Secondly, and this one I haven't resolved yet, the tile name also includes the variant of the tile. For example, there are many types of "Road" tiles. Despite them all having the same effect on the game, their names are different, I imagine to help distinguish them when their sprites are being assigned. The last issue I encountered was unit's who's names start with the same letter. This was an issue I assumed would come up, however I may just hard code 2 letters for these edge cases and deal with it properly later once we start adding sprites.&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>programming</category>
      <category>gamedev</category>
      <category>php</category>
    </item>
    <item>
      <title>Porting a PHP Based Web Game to Mobile Part 1</title>
      <dc:creator>Mike Evans</dc:creator>
      <pubDate>Thu, 10 Apr 2025 06:57:52 +0000</pubDate>
      <link>https://dev.to/jigsawpsycho/porting-a-php-based-web-game-to-mobile-part-1-5e4a</link>
      <guid>https://dev.to/jigsawpsycho/porting-a-php-based-web-game-to-mobile-part-1-5e4a</guid>
      <description>&lt;p&gt;Advance Wars by Web (AWBW) is a web port of the Advance Wars Nintendo game. I was first introduced to the game by my best friend. I thought it was a fun game, however it was lacking in the mobile department. There is currently no mobile version of the game, at least not one that connects up to the same backend.&lt;/p&gt;

&lt;p&gt;I've decided to start building a mobile port for the game, my goal is to have an easy to navigate interface for mobile users, as well as notifications for when its the player's turn. The player should be able to play the game as they would on the web, only via their phone. Currently the web version does work on mobile, but it is clunky and difficult to navigate. I will build the app in Unity and make it compatible with all major current resolutions as at 2025.&lt;/p&gt;

&lt;p&gt;I started by running the web app with firefox's network logging enabled, in order to see what kind of game data I could get and from which endpoints. I uncovered a response from &lt;code&gt;https://awbw.amarriner.com/game.php?games_id={{game_id}}&lt;/code&gt; seems to hold the vast majority of game data, from terrain to building to unit data. All that I needed to do now was to parse it, so I started building a parser that would take in the response from game.php and extract the relevant data I would need and transform it into a usable form.&lt;/p&gt;

&lt;p&gt;I also found that you can login via the &lt;code&gt;https://awbw.amarriner.com/logincheck.php&lt;/code&gt; endpoint. This returns a 200 regardless of success, instead opting to return a 0 or 1 to indicate a failed or successful login respectfully. This will be helpful later when we actually start trying to send move data to the server.&lt;/p&gt;

&lt;p&gt;I continued working on the parser for the terrain info from game.php. I spent far too long trying to remember how to handle de-serializing JSONs with dynamic keys, eventually I settled on the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class GameInfoParser

{

    public TerrainInfo ParseTerrain(string gameInfoHtml)

    {

        TerrainInfo terrainInfo = new TerrainInfo();

        terrainInfo.Tiles = new List&amp;lt;TileInfo&amp;gt;();



        List&amp;lt;ServerTerrainInfo&amp;gt; serverTerrainInfos = ExtractTerrainInfoJson(gameInfoHtml);

        terrainInfo.Tiles = serverTerrainInfos.ConvertAll&amp;lt;TileInfo&amp;gt;(t =&amp;gt; new() { X = t.tiles_x, Y = t.tiles_y});



        return terrainInfo;

    }

    private List&amp;lt;ServerTerrainInfo&amp;gt; ExtractTerrainInfoJson(string gameInfoHtml)

    {

        string terrainInfoJson = gameInfoHtml.Split("const terrainInfo = ")[1].Split(";")[0];

        var json = JsonConvert.DeserializeObject&amp;lt;Dictionary&amp;lt;string, Dictionary&amp;lt;string, ServerTerrainInfo&amp;gt;&amp;gt;&amp;gt;(terrainInfoJson);

        List&amp;lt;ServerTerrainInfo&amp;gt; serverTerrainInfoList = new List&amp;lt;ServerTerrainInfo&amp;gt;();

        foreach(var xCoord in json.Keys)

        {

            foreach(var yCoord in json[xCoord].Keys)

            {

                serverTerrainInfoList.Add(json[xCoord][yCoord]);

            }

        }



        return serverTerrainInfoList;

    }



    private struct ServerTerrainInfo

    {

        public int tiles_x;

        public int tiles_y;

    }

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

&lt;/div&gt;



&lt;p&gt;After trying to run the code above against the actual game.php response HTML, I was - to be honest not awfully - surprised to find the code failing. After some debugging and a lot of confusion, I found the culprit. &lt;/p&gt;

&lt;p&gt;The game.php response returned an HTML file, in that HTML, you will find large JSON objects comprising of all the game state data. The terrain data as it turned out, had a rogue JSON array in amongst its JSON objects, likely caused by the JSON serializer mistaking the keys 0, 1, 2, 3... for indexes in an array as opposed to actual keys. You see, the JSON keys were y coordinates, while the value was a dictionary of x coordinates to objects, where the objects were the data I needed). If a y coordinate's corresponding collection of x coordinates happened to be a consecutive set of numbers starting from 0, the y coordinate's value would show as an array as opposed to just a JSON object. A bit strange, but we can easily make use of a library to instead parse the JSON into JSONNode objects, where we can deal with the data regardless of whether it is an array or an object.&lt;/p&gt;

&lt;p&gt;Eventually, we end on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private List&amp;lt;ServerTerrainInfo&amp;gt; ExtractTerrainInfoJson(string gameInfoHtml)
    {
        string terrainInfoJson = gameInfoHtml.Split("const terrainInfo = ")[1].Split(";")[0];
        List&amp;lt;ServerTerrainInfo&amp;gt; serverTerrainInfoList = new List&amp;lt;ServerTerrainInfo&amp;gt;();
        JSONNode jsonNode = SimpleJSON.JSON.Parse(terrainInfoJson);

        foreach(JSONNode yNode in jsonNode.Values)
        {
            foreach(JSONNode xNode in yNode.Values)
            {
                string jsonValue = xNode.ToString();
                ServerTerrainInfo serverTerrainInfo = JsonConvert.DeserializeObject&amp;lt;ServerTerrainInfo&amp;gt;(jsonValue);
                serverTerrainInfoList.Add(serverTerrainInfo);
            }
        }

        return serverTerrainInfoList;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, all green on my tests.&lt;/p&gt;

&lt;p&gt;It would be nice to at least start being able to view the map we have worked so hard to parse data for. So I got to work on building out a little map renderer. Nothing fancy, just taking the collections of data we extracted previously and instantiating some tiles in the scene according to their positions and tile type. That gives us this rather hideous looking image, but we know what it took to make, so it's beautiful to us.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh2wkatr2ipnq1pit8uze.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh2wkatr2ipnq1pit8uze.png" alt="Image description" width="568" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next time, we'll need to start parsing the units types and look into getting actions to the server. Until then!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>php</category>
    </item>
  </channel>
</rss>
