Skip to content

How to setup a new Github project with Unity and tests

Matthias Hermsen edited this page Dec 4, 2020 · 6 revisions

Overview

This is a very small introduction on how to setup a new Github project with Unity and tests (for this I'm using xUnit but it shouldn't matter). I think it's important to mention that this is not about best practises here, it just explains the setup in its simplest form. The optional parts are mostly based on my personal preferences, except the setup for the Roslyn code generator.

Prerequisites

For this guide I'm using the following environment

  • Unity 2020.1.4f1
  • Unity Hub 2.4.2
  • Entitas 1.12.3 from the Asset Store

but it should work for all Unity versions above 2018.3.0.

Setting up the repository

First of all create a new repository on Github. I selected Github's Unity .gitignore template as a starting point.

Repository setup

Setting up the .gitignore file

Currently there is no golden rule for it. Github's Unity .gitignore template does not cover all the files that should be ignored. Below I will post my personal .gitignore file but please keep in mind to add files to the .gitignore file as you go. There might be more files which can be ignored later on. Edit the generated .gitignore file in the repository so that it meets your requirements.

# OS
Thumbs.db
.DS_Store

# Base
[Ll]ibrary/
[Tt]emp/
[Oo]bj/
[Bb]uild/
[Bb]uilds/
[Ll]ogs/
[Mm]emoryCaptures/

# IDEs
**/bin/Debug
**/bin/Release
**/packages
*.userprefs
*.user
test-results
DerivedData
xcuserdata

# Jetbrains
.idea
[Aa]ssets/Plugins/Editor/JetBrains*

# Visual Studio
.vs

# Visual Studio Code
.vscode

# Unity
*.pidb.meta
*.pdb.meta
*.mdb.meta
sysinfo.txt
*.unitypackage
Assembly-CSharp*.csproj
*.sln

# Entitas
Jenny.zip
Jenny.zip.meta
*.userproperties

# Gradle
.gradle

# Builds
*.apk

Setting up the Unity project

First of all clone the repository locally by running (don't forget to update the git clone url to your own)

git clone https://github.com/matthiashermsen/my-new-project.git

in the terminal. Open the Unity Hub and create a new Unity project inside the root of that local repository.

Create a new project with the Unity Hub

Importing Entitas

In the editor open the Package Manager (which can be found under the Window tab) and switch to your own assets. Entitas should show up in the list. For now I will simply import everything

Import Entitas

After that you should see multiple files in the root of your assets folder

Assets folder

(Optional) Organize external libraries

Create a new folder inside the assets folder and call it "Libraries" (you can name it anything you want) and move the imported files into it. There might be more libraries so you can create a subfolder "Entitas" and move everything into it. If everything was fine you should have this structure

Folder structure

Setting up Entitas

In the editor head over to the Tools section and select Jenny => Preferences. A window should pop up, simply hit the "Auto Import" button to setup the settings. A warning message appears, select "Continue and Overwrite".

The preferences should now looke like this

Jenny preferences

You can close the window after that for now.

(Optional) Organize Entitas code

The Jenny preferences have a section "Target Directory". The current value is "Assets". Jenny will create a "Generated" folder in the assets folder and put the generated code into it. If you want to keep Entitas related code in a subfolder, simply change the value to "Assets/Sources" (you can name it anything you want). Now generated code will be in "Assets/Sources/Generated".

Making sure the Assembly-Csharp.proj file exists

You will need a script acting as the glue between Unity and Entitas. Simply create a new script and call it "GameController" (you can name it anything you want). For now this script is empty but your IDE will create the needed script assemblies

using UnityEngine;

public class GameController : MonoBehaviour
{
}

You can close your IDE after that, Unity will refresh. Now click on Generate (which is found under the Tools section => Jenny). There should be no errors, in the console should be two logs

Console Logs

(Optional) Organize Unity code

In the assets folder create a new folder called "Scripts" (you can name it anything you want) and move the "GameController" into it. Unity related code will be here. The structure should look like

Script structure

(Optional) Setting up the Roslyn Code generator

-- TODO --

Creating your first system

Open up your project with Unity. For the sake of simplicty create a initialize system creating a new entity on initialization. Create a new script called "CreateEntitySystem".

using Entitas;

public sealed class CreateEntitySystem : IInitializeSystem
{
    private readonly Contexts contexts;

    public CreateEntitySystem(Contexts contexts)
    {
        this.contexts = contexts;
    }
    
    public void Initialize()
    {
        contexts.game.CreateEntity();
    }
}

(Optional) Organize systems

Move your system to "Assets/Sources/Systems/InitializeSystems".

Initialize systems

Initilialize systems go here.

Creating your first feature

Create a new script called "GameSystems".

public sealed class GameSystems : Feature
{
    public GameSystems(Contexts contexts)
    {
        Add(new CreateEntitySystem(contexts));
    }
}

(Optional) Organize features

Move your feature to "Assets/Sources/Features".

Features

Features go here.

Adding the features

Open up the script "GameController" and update it to

using UnityEngine;

public class GameController : MonoBehaviour
{
    private GameSystems gameSystems;
    
    private void Start()
    {
        Contexts contexts = Contexts.sharedInstance;
        gameSystems = new GameSystems(contexts);
        gameSystems.Initialize();
    }
    
    private void Update()
    {
        gameSystems.Execute();
    }
}

Now regenerate the code.

Setting up the scene

Create an empty GameObject and call it "GameController" (you can name it anything you want). Attach the script "GameController" to it.

Scene setup

When running the play mode the scene hierarchy should look like this

Scene hierarchy

You can close Unity now.

Setting up tests

Head over to the root of your repository. Create a new solution project with a unit test project

Unit test project

In your IDE add an existing project to the solution. Select the "Assembly-CSharp.csproj" file from your Unity project which should be at

/.../my-new-project/my-new-project/Assembly-CSharp.csproj

Your project should now look similiar to this

Solution overview

Referencing the Unity project

In your tests project add a new reference. Select the Unity project.

Reference the Unity project

Creating the test

Create a new .cs file or use the generated one (UnitTest1) and put in this code

public class UnitTest1
{
    [Fact]
    public void Test1()
    {
        Contexts contexts = new Contexts();
        Systems systems = new Systems();
        CreateEntitySystem createEntitySystem = new CreateEntitySystem(contexts);
        systems.Add(createEntitySystem);
        systems.Initialize();
          
        Assert.True(contexts.game.count == 1);
    }
}

Run your test, it should pass.

Test passed

(Optional) Organize tests

You can delete your test file, this will be a complete rewrite.

Base class for system tests

To take away boilerplate code you have to setup for every system test create a file "SystemTests" (you can name it anything you want) in Tests => SourceTests => SystemTests

SystemTests

and use this code

using System;
using Entitas;

namespace Tests.SourceTests.SystemTests
{
    public abstract class SystemTests : IDisposable
    {
        protected Contexts contexts;
        protected Systems systems;

        // ! This runs before every test !
        protected SystemTests()
        {
            contexts = new Contexts();
            systems = new Systems();
        }
        
        // ! This runs after every test !
        public void Dispose()
        {
        }
    }
}
Base class for initialize system tests

To take away boilerplate code you have to setup for every initialize system test create a file "InitializeSystemTests" (you can name it anything you want) in Tests => SourceTests => SystemTests => InitializeSystemTests

Initialize system tests

and use this code

using System;
using Entitas;

namespace Tests.SourceTests.SystemTests.InitializeSystemTests
{
    public abstract class InitializeSystemTests<TInitializeSystem> : SystemTests where TInitializeSystem : IInitializeSystem
    {
        // ! This creates a new instance and initializes it before every test !
        protected InitializeSystemTests()
        {
            TInitializeSystem initializeSystem = (TInitializeSystem)Activator.CreateInstance(typeof(TInitializeSystem), contexts);
            systems.Add(initializeSystem);
            initializeSystem.Initialize();
        }
    }
}
Rewritten test

Right next to the file "InitializeSystemTests" create a new file called "CreateEntitySystemTests" (you can name it anything you want)

Create entity system tests

and use this code

using Xunit;

namespace Tests.SourceTests.SystemTests.InitializeSystemTests
{
    public sealed class CreateEntitySystemTests : InitializeSystemTests<CreateEntitySystem>
    {
        [Fact]
        private void ItCreatesOneEntity()
        {
            Assert.True(contexts.game.count == 1);
        }
    }
}

When running the test it should pass.

Test still passed

All done!

gif

Clone this wiki locally