(#100DaysOfCode) Day One – When the stars are(n’t) right!

As promised in the last blog post, here is the link to the bitbucket repo for the game project :- https://bitbucket.org/cybermancer1927/celestialaberration/src/master/

I’ll mainly be working off the master branch. So no complex GIT commands are needed here. For anyone who is new to GIT there is a tutorial here – https://www.tutorialspoint.com/git/index.htm

The above tutorial is aimed at Java programmers but the concept remains the same regardless of the language. I set up the account on bit bucket (www.bitbucket.org) and marked the repository as public. This means that anyone can view and download your source code, but only people to whom you give access can push code to the repository. It’s a good choice when working on open source projects but if you want your code hidden behind a more restrictive license then a private repository is a better choice.

I edited the site’s README.md directly from bitbucket to provide an introduction. The README.md text gets displayed on the repository ‘home’ page. It uses a very simple text processing language called markdown, a tutorial for which can be found here – https://www.markdowntutorial.com/

Ok. So now we have a repo its time to set up some code. The first thing I need to do is pull down my repo using the follwing git command in the directory under which I want my repo to reside:-

git clone https://cybermancer1927@bitbucket.org/cybermancer1927/celestialaberration.git

This text can also be obtained by acessing the repo in a browser and hitting the ‘Clone’ button next to the repo name. You will need to have GIT installed locally and can download it from https://git-scm.com/downloads.

Now over to Visual Studio. I’m using Visual Studio 2017 version 15.9.4. The community edition is free and good enough for this particular project.

I am created a new project. Under Visual C#, I chose Class Libaray (Net Standard). This creates a class library using .NET Standard 2.0. This limits some of the namespaces available but gives the assembly the greatest portability, making it platform agnostic. I intend to have the game engine logic here with interfaces to platform-specific operations and platform-specific stuff like user interfaces, etc will be coded for in separate assemblies.

The name I have given to the first project is Cybermancer.CelestialAberration.GameEngine. Since our solution will have several projects I ticked ‘Create directory for solution’ and changed the solution name to Cybermancer.CelestialAberration

Start The Engine

Ok so now to start coding. Every game needs a game introduction, right? So let’s start there. I’m storing the game introduction text as a JSON file, which I’ve embedded into the assembly for now. Details of how I did that will be explained later. The basic format of any piece of intro text will be a simple POCO (https://en.wikipedia.org/wiki/Plain_old_CLR_object) class called GameText. I’ll store this in a directory called GameObjects under the GameEngine project:-

GameText.cs :-

namespace cybermancer.CelestialAberration.GameEngine.GameObjects
{
    public class GameText
    {
        public string GameTextId { get; set; }
        public string Content { get; set; }
    }
}

Now as well as having a piece of game text, for the game introduction I wanted to add a little extra pizazz. I wanted the text to transition in and out as it’s displayed. I also wanted it to be able to add custom styling. So I created a derived class that extends GameText for this purpose, again the the GameObjects namespace.

TransitionText.cs:-

namespace Cybermancer.CelestialAberration.GameEngine.GameObjects
{
    public class TransitionText : GameText
    {
        public string Style { get; set; }
        public int SecondsToDisplay { get; set; }
    }
}

Now the game introduction itself is a collection of these transition texts so I added a final class in the GameObjects namespace to provide this collection.

GameIntro.cs

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
namespace Cybermancer.CelestialAberration.GameEngine.GameObjects
{
    public class GameIntro
    {
        public List<TransitionText> IntroTexts { get; set; }

        public override string ToString()
        {
            return JsonConvert.SerializeObject(this);
        }
    }
}

Noice that the ToString() method serializes the class into JSON. For this I am using the exellect Newtonsoft.Json assembly which I added as a project reference via nuget. To add a reference right click on the project and select ‘Manage nuget packages…’ This is provide you with a browser which you can use to search for nuget packages. Just search for ‘newtonsoft’, select the ‘Newtonsoft.Json’ package and hit the install button on the right. This will install the dependencies you need for this project.

Embedded in code

As I mentioned earlier the GameIntro object will be a JSON file embedded into the assembly. To do this I created a directory called “Resources” and under that another directory called “Default” (I had the idea of having different ‘resource types’, e.g. to support multiple languages).

I created a new file, this time selecting Web -> Scripts -> JavaScript JSON Configuration file. I then replaced the text like so:-

GameIntro.json :-

{
  "introTexts": [
    {
      "gameTextId": "INTRO_0",
      "content": "I have seen the dark universe yawning[br]Where the black planets roll without aim,[br]Where they roll in their horror unheeded,[br]Without knowledge, or lusture, or name.[br][br]- H.P. Lovecraft",
      "Style": "neon",
      "secondsToDisplay": 15
    },
    {
      "gameTextId": "INTRO_1",
      "content": "They hit us without warning. Indiscriminate and without provocation. Mankind was not equipped to deal with them. We were a fledgling race, just testing the waters of the space outside of our small home. Faster than light travel was still deemed a purely theoretical dream but a realistic impossibility. Advances in technology had allowed us to spread out into our own solar system but we had ventured no further. We had not truly considered the possibility of a first contact situation and would have been ill equipped to deal with the sort of alien invasion that popular culture and our imaginations could envisage. We never anticipated anything as hideous or unnatural as the harvesters.",
      "Style": "intro",
      "secondsToDisplay": 25
    },

-- *snip* --

   {
      "gameTextId": "INTRO_8",
      "content": "Destiny calls, captain. You have been chosen to lead a team of volunteers into the unknown. Your mission is to command the Voidwalker's maiden voyage across the stars. You must locate and destroy the source. This is our only chance. The fate of humanity rests on your shoulders. Good luck!",
      "Style": "intro",
      "secondsToDisplay": 15
    }
  ]
}

Then I right clicked on the file and selected Properties, then changed the build action to ‘Embedded Resource’. This will compile the file into the assembly itself, which is useful for static resources that will never change during the life cycle of the application.

So how do we access this embedded resource from code (I hear you ask)? This is where a repository comes in. In the context of this game, a repository is a class used to access either internal or external resources (from an embedded assembly, file store, database, web API, etc) and convert them to class objects that the application can use. I have created a Respositories directory and in this an interface class for accessing game resources called IGameResourceRepository. The idea here is that any assembly can create a class that implements this interface to provide a game resource from whichever internal or external storage mechanism it uses.

IGameResourceRepository.cs :-

using Cybermancer.CelestialAberration.GameEngine.GameObjects;

namespace Cybermancer.CelestialAberration.GameEngine.Repositories
{
    public interface IGameResourceRepository
    {
        GameIntro GetGameIntro(string resourceId);
    }
}

Now if we are using a windows application to retrieve resources from the filesystem we can create a .NET Core project and create a GameResourceFileRepository that implements the method GetGameIntro and retrieves the game intro from a JSON file stored on the file system. Indeed we will be doing this later on in the project when we start creating a user interface for the game. Right now, however, we will create a GameResourceAssemblyRepository in the current assembly which will retrieve our own GameIntro.JSON fro the embedded file and turn it intro a GameIntro object.

GameResourceAssemblyRepository.cs :-

using Cybermancer.CelestialAberration.GameEngine.GameObjects;
using Newtonsoft.Json;
using System.IO;
using System.Reflection;

namespace Cybermancer.CelestialAberration.GameEngine.Repositories
{
    public class GameResourceAssemblyRepository : IGameResourceRepository
    {
        public GameIntro GetGameIntro(string resourceId)
        {
            return JsonConvert.DeserializeObject<GameIntro>(GetResourceFileAsString(resourceId, "GameIntro.json"));
        }

        private string GetResourceFileAsString(string resourceId, string resourceName)
        {
            var assembly = Assembly.GetExecutingAssembly();
            var resourcePath = $"Cybermancer.CelestialAberration.GameEngine.GameResources.{resourceId}.{resourceName}";

            using (Stream stream = assembly.GetManifestResourceStream(resourcePath))
            using (StreamReader reader = new StreamReader(stream))
            {
                return reader.ReadToEnd();
            }
        }
    }
}

This class uses reflection to read in the JSON data from the assembly (It accesses it through it’s entire namespace (Cybermancer.CelestialAberration.GameEngine.GameResources.{Directory}.{File}) It then deserializes the result into a GameIntro Object.

In code we can call (IGameResourceRepository)repositoryInstance.GetGameInto(“Default”) and the code will look for a retrieve the file Cybermancer.CelestialAberration.GameEngine.GameResources.Default.GameIntro.json from the assembly and deserialize it.

Testing, Testing, 1.2.3

O.K. so we don’t have a user interface yet, just a collection of classes ready to be used. How do we know if our code works correctly? Time for a unit test.

Whilst I am a fan of NUnit, It can be a bit complex getting it to play nicely with visual studio so I will use the bog standard MSTest.TestFramework for this. I created a new project, selecting Test -> MSTest Test Project (.NET Core) from the list of projects. I called the project Cybermancer.CelestialAberration.GameEngineTests as this is where all unit tests for our basic game engine code will live. Deleting the default empty test class that is created I added a new test class in a directory called RepositoryTests called GameResourceRepositoryTests.cs

using System.Linq;
using Cybermancer.CelestialAberration.GameEngine.Repositories;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Cybermancer.CelestialAberration.GameEngineTests.RepositoryTests
{
    [TestClass]
    public class GameResourceRepositoryTests
    {
        [TestMethod]
        public void EnsureRepositoryRetrievesGameInfoCorrectly()
        {
            IGameResourceRepository repository = new GameResourceAssemblyRepository();

            var gameIntro = repository.GetGameIntro("Default");

            var actualIntroText = gameIntro.IntroTexts.FirstOrDefault();

            Assert.IsNotNull(actualIntroText);

            Assert.AreEqual("INTRO_0", actualIntroText.GameTextId);
            Assert.AreEqual(
                "I have seen the dark universe yawning[br]Where the black planets roll without aim,[br]Where they roll in their horror unheeded,[br]Without knowledge, or lusture, or name.[br][br]- H.P. Lovecraft",
                actualIntroText.Content);
            Assert.AreEqual("neon", actualIntroText.Style);
            Assert.AreEqual(15, actualIntroText.SecondsToDisplay);
        }
    }
}

This class has a single test method called EnsureRepositoryRetrievesGameInfoCorrectly. The test is very simple. It just creates a new instance of the GameResourceAssemblyRepository, retrieves the GameInfo object and asserts that it contains data and that the first TransitionText object in it’s collection is correct.

Here is a screenshot of the test running succesfully in Test Explorer:-

Pushing the code

Now we have some code and verified that it works it’s time to commit our work back into bitbucket. Note that the repository here only gives me access to push code up so don’t try this on my repository or I’ll send in the auditors to explain to you what not having the right clearance can actually mean.

We could use the command line for pushing code. We could also use visual studio itself. I personally like to use the excellent GitKraken – https://www.gitkraken.com/

This is a visual git client that allows you to view your repo graphically and stage and commit changes. Before we commit, however, I did some housekeeping. I moved the solution and projects into a ‘source’ directory under the main one. This allows me to commit things like design notes and diagrams that don’t exist in the code itself into separate directories to go alongside the source. I also added a .gitignore file to prevent me from committing unnecessary visual studio files such as build binaries and user settings:-

*.suo
*.DotSettings.*
.vs
bin
obj

Tip: If you try to create a file in windows that starts with a period (.) then windows will complain and not let you. However, if you give the file name ending in two periods (gitignore..) then windows will automagically create the correct file name for you (gitignore.. -> .gitignore).

Now in GitKracken I could select WIP above our master branch, then click ‘Stage Changes’. I add a comment in the box provided and then hit the ‘commit’ button.

So are we done? Not quite. We have only committed our code to the local git repository. We now have to push the code to the remote bit bucket repository. We do this by hitting the ‘push’ button in the toolbar and typing in our user name and password. After a little bit of whiring our client show that our code was pushed successfully.

That’s it for day one. On day two we will create a simple user interface for the game using ASP.NET Core MVC.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.