-
Notifications
You must be signed in to change notification settings - Fork 1.1k
How to setup a new Github project with Unity and tests
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.
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.
First of all create a new repository on Github. I selected Github's Unity .gitignore template as a starting point.
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
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.
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
After that you should see multiple files in the root of your assets folder
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
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
You can close the window after that for now.
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".
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
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
-- TODO --
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();
}
}
Move your system to "Assets/Sources/Systems/InitializeSystems".
Initilialize systems go here.
Create a new script called "GameSystems".
public sealed class GameSystems : Feature
{
public GameSystems(Contexts contexts)
{
Add(new CreateEntitySystem(contexts));
}
}
Move your feature to "Assets/Sources/Features".
Features go here.
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.
Create an empty GameObject and call it "GameController" (you can name it anything you want). Attach the script "GameController" to it.
When running the play mode the scene hierarchy should look like this
You can close Unity now.
Head over to the root of your repository. Create a new solution project with a 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
In your tests project add a new reference. Select the Unity project.
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.
You can delete your test file, this will be a complete rewrite.
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
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()
{
}
}
}
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
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();
}
}
}
Right next to the file "InitializeSystemTests" create a new file called "CreateEntitySystemTests" (you can name it anything you want)
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.
Guides: Introduction - Installation - Upgrading - FAQ - Cookbook - Contributing
Need Help? Ask a question on Discord or create an issue.
- The Basics
- Concepts
- Architecture / Patterns