Tinkering With Entitias – Entitiasteroids

Tinkering With Entitias – Entitiasteroids

EDIT: Check the comments for an excellent reply from Simon, creator of Entitias.

Last week I wrote a couple of blog posts about my work on Unity Ash:

http://www.mikecann.co.uk/programming/unity-ash-a-different-way-of-thinking-about-making-games-in-unity/
http://www.mikecann.co.uk/myprojects/unityasteroids/unit-testing-with-unity-ash-and-unity-test-tools/

After publishing those I was contacted by a friend about another Unity Entity / System architecture called Entitas.

After giving it a read and watching the Unity Europe 2015 video I knew that I had to tinker with it.

Entitias is similar to Ash in that it is an Entity / System architecture that works in Unity but it differs in its approach.

Compared to Ash, Entitias favours a much more pure, immutability focused approach where components are added, removed and replaced instead of having their state changed. Systems react to changes by watching when matching groups of components are added or removed from a pool.

On paper it looks like a better approach to Entity / Systems in Unity than Ash as it means that Components are pure objects with no dependencies upon the Unity engine at all (Ash components extend MonoBehaviour). No dependencies on Unity means that the same code can be run on the client and server.

To dig a little deeper into Entitias I decided to port the Ashteroids example I did for Unity-Ash to Entitias, I called it Entitiasteroids:

https://github.com/mikecann/Entitasteroids

2015-11-09_09-22-56

So what did I learn in the process?

Code Generation

Is mostly awesome. I really love the easy to read fluent interface it lets you construct, I mean just take a look at how beautiful and expressive this is:

public static Entity CreatePlayer(this Pool pool, bool controllable)
{
	return pool.CreateEntity()
		.IsPlayer(true)
		.AddPosition(0, 0)
		.AddSpaceship(0.5f, 0.02f)
		.AddCollisionRadius(1)
		.IsControllable(controllable)
		.IsWrappedAroundGameBounds(true)
		.AddForce(new List<Vector2>(), 0)
		.AddResource("Prefabs/Spaceship");
}

My only complaint is that the code generation can sometime be annoying to work with because it requires that the app is in a compilable state to run. You can quite easily get into catch 22 situation where you need a component to make a System compile but to make the component you need to generate code, which requires that the app compiles.

I mentioned this issue to Simon Schmid, the author of the project, and he agreed. The reason it requires a compiling application is because the code generator currently works via reflection. There is a Roslyn compiler for Entitias which may solve the issue but its still quite experimental.

Prefabs

I miss prefabs. Look at the code example above, notice those hard-coded values there. After working with Unity’s method of inspector based configuration objects it just feels wrong to go and put magic numbers back into the code, forcing a recompile each time you want to tweak a value. Using this method with teams could be quite troublesome too as the coder will be required for each minor value tweak.

I think Unity-Ash excels here as you just create prefabs as normal with components as normal, the only difference is how those components are handled at runtime.

Interop With Unity Components

Retrieving data from Unity components such as Rigidbody2D and Transform can be quite painful. Perhaps I wasn’t doing it right and it may well be a totally necessary side effect of removing the Unity dependency from components but the copying of data from Unity components to Entitas components then “rendering” Entitas component values back to Unity components just felt cumbersome and confusing at times.

Cryptic Error Messages

During development you would continually run into errors such as:

2015-11-09_08-26-05

Its quite difficult to read and follow whats happening here. Named components would help instead of index’s but even with that it can be tough to understand whats going on. Unity-Ash doesnt have this problem really as you rarely ever interact with an Entity directly, you always go via a Node so you can be almost guaranteed that the components exist upon the entity.

Im not exactly sure why but sometimes adding IEnsureComponent to a system fixes some of the errors but I wasn’t really sure what it does.

Minor Annoyances

+ It would be nice if there was a utility to automatically add systems to the Systems object. Often you spend a lot of time wondering why your code isnt working, often its because you forgot to add the newly created system to the Systems object.

+ To keep things immutable, whenever you want to change a value in a component you must use ReplaceX() methods. This is a bit of a gotcha at first but once you get used to it its okay. The only problem is that the ReplaceX methods require that you supply the entire list of properties for a component. This can be quite annoying and verbose if you want to change a single property or simply increment or decrement the existing value;

+ Reactive systems are one of the best things about Entitias but 99% of the time you end up writing something like:

public void Execute(List<Entity> entities)
{
	foreach (var entity in entities)
	{
	}
}

It would be nice if that list was looped for you so Execute just supplied a single Entity.

+ Example projects listed on the Entitias github seems to use different versions of the library, so it can be quite confusing when looking for examples for how to do something when the examples all look different.

+ Some of the callbacks in Entitias start with a lowercased letter such “trigger” “ensureComponents”, this is strangely inconsistent with Unity and C# in general.

public TriggerOnEvent trigger
{
    get { return Matcher.AllOf(Matcher.Force, Matcher.Rigidbody).OnEntityAdded(); }
}

Conclusion

Despite the above bitching I think Entitas is really nice. I am really impressed at how clever it is and the refreshing take on Entity / Systems architecture. The Code Generation is really nice and the immutability model for the framework is really powerful.

I do however think that it would be more difficult for people coming from Unity to pick up than Unity-Ash due to the pure-code nature of the entities / components (instead of Prefabs), the hoops you have to jump through to use existing Unity components and the difficult to debug errors.

I think that there is much that could be applied to Unity-Ash to improve it and perhaps bring some of the cool features from Entitias, but thats a tinking for another day 🙂

Unit Testing with Unity Ash and Unity Test Tools

Unit Testing with Unity Ash and Unity Test Tools

So I promised in my last post to show how how Unity-Ash can make things easyier when it comes to Unit Testing.

I have added some tests to the Unity Asteroids example game. Lets take a look at one of the simpler systems as an example, the DeathThroesSystem:

public  class DeathThroesSystem : NodelessSystem<DeathThroes, Entity, Audio>
{
    public DeathThroesSystem()
    {
        _updateCallback = OnUpdate;
        _nodeAddedCallback = OnNodeAdded;
    }

    private void OnNodeAdded(DeathThroes death, Entity entity, Audio audio)
    {
        audio.Play(death.deathSound);
    }

    private void OnUpdate(float delta, DeathThroes death, Entity entity, Audio audio)
    {
        death.countdown -= delta;
        if (death.countdown <= 0)
            entity.Destroy();
    }
}

We can see its a really simple system that does a couple of things.

First we want to test that when a node is added to the system we play a sound effect. Using Unity-Test tools you can quickly whip up a test case:

[TestFixture]
public class DeathThroesSystemTests : UnityUnitTest
{
    private IEngine _engine;
    private DeathThroesSystem _system;

    [SetUp]
    public void Setup()
    {
        _system = new DeathThroesSystem();
        _engine = new Engine();
        _engine.AddSystem(_system, 0);
    }

    [Test]
    public void WhenANodeIsAdded_DeathSoundPlays()
    {
        var e = AddEntityToEngine();

        Assert.AreEqual(1, e.GetComponent<Audio>().toPlay.Count);
        Assert.AreEqual(e.GetComponent<DeathThroes>().deathSound, e.GetComponent<Audio>().toPlay[0]);
    }
        
    private GameObject AddEntityToEngine()
    {
        var obj = CreateGameObject();
        obj.AddComponent<DeathThroes>();
        obj.AddComponent<Audio>();
        obj.AddComponent<Entity>();
        _engine.AddEntity(obj.AddComponent<Entity>());
        return obj;
    }
}

I wrote a little helper that adds an entity to the engine with the necessary components. In the future we could use reflection to looup the types on the Nodeless system, but for now its enough to satisfy the test:

2015-11-02_17-28-19

Next we want to check that after update the countdown is decremented.

[Test]
public void AfterUpdate_CountdownDecremented()
{
    var e = AddEntityToEngine();

    var before = e.GetComponent<DeathThroes>().countdown;

    _engine.Update(1.5f);

    Assert.AreEqual(before - 1.5f, e.GetComponent<DeathThroes>().countdown, 0.01f);
}

Easy, now we want to check that when the countdown is less than or equal to zero, the entity is destroyed:

[Test]
public void AfterCountdownTimerEnds_EntityDestroyed()
{
    var e = AddEntityToEngine();

    var before = e.GetComponent<DeathThroes>().countdown;

    _engine.Update(before + 1);

    Assert.IsTrue(e.GetComponent<Entity>().IsDestroyed);
}

Sweet and thats it! Sure we could add a few more tests for some of the corner cases but as an example I think it serves its purpose.

Let me know in the comments or email me: mike.cann@gmail.com with what you think!

Unity Ash – A different way of thinking about making games in Unity

Unity Ash – A different way of thinking about making games in Unity

Over a year ago I decided to scratch an itch and see if I could get Richard Lord’s Ash framework to work in Unity. It actually turned out to be far easier than I had imagined. A few people contacted me as they wanted to use it for production games so I decided to do a little more work on it a few months later to fix some of the easily solved issues with my quick port.

Unfortunately I lacked any spare time to work on it until now. This week I spent 3 days rewriting the framework from scratch. I improved many things, making it much more Unity-friendly, and generally easier to use. Because of the differences from the AS3 version of Ash I now describe it as “heavily inspired” rather than a port.

Before I go into too much detail about the differences from Richard’s AS3 version I should briefly explain Ash and how it differs and extends upon Unity’s Enity / Component architecture.

TL; DR

Checkout the Unity-Ash src: https://github.com/mikecann/Unity-Ash and the example game: https://github.com/mikecann/UnityAshteroids

Entity / Components in Unity

Unity is an Entity / Component game engine. This means that instead of having classes for object types such as Spaceship, Asteroid or Player which may extend some other classes you have Entities (Unity calls these GameObject’s) which contain components that do the work.

2015-10-30_09-48-45

This is a much better structure for game design as it favours composition over inheritance which prevents the common pitfall of the “God Object” when developing a game. By separating the concerns into differnt components you can reuse functionality by attaching components to Entities / GameObjects rather than extending a base class and overriding required functionality.

I won’t go on any more about it, you can google the topic and find out plenty more.

Entity / Components in Ash

Although Ash is an Entity / Component architecture, it differs quite fundamentally from Unity in how data and logic should be structured.

In Unity components typically extend MonoBehaviour and contain the variables and logic for that specific functionality. For example a simple Unity component may look something like:

public class Bullet : MonoBehaviour
{	
	public float maxAge = 3;
	
	public float age;
	
	void Awake()
	{
		age = 0;
	}
	
	void Update()
	{
		age += Time.deltaTime;
		if (age >= maxAge)
			Destroy(gameObject);
	}
}

Imagine that Bullet is a projectile fired from the player spaceship. We can see that the bullet ages over time and when it is equal or greater than the max age then it is removed from the game.

The Ash way of doing things is to separate the component data from its logic. So it may now look something like:

public class Bullet : MonoBehaviour
{	
	public float maxAge = 3;
	public float age;
}

public class BulletNode
{
	public Entity Entity { get; set; }
	public Bullet Bullet { get; set; }
}

public class BulletAgingSystem : ISystem
{
	private INodeList<BulletNode> _nodes;

	public void AddedToEngine(Engine engine)
	{
		_nodes = engine.GetNodes<BulletNode>();
	}

	public void RemovedFromEngine(Engine engine)
	{
		engine.ReleaseNodes(_nodes);
	}

	public void Update(float delta)
	{
		foreach (var node in _nodes)
		{
			node.Bullet.age += delta;
			if (node.Bullet.age >= node.Bullet.maxAge)
				node.Entity.Destroy();
		}
	}
}

Here we can see that the Bullet’s data is now separate from the logic which modifies that data. In Ash components should simple data, they can contain some methods but they should be limited to operating on the state of that instance, they certainty aren’t allowed to have any dependencies on other components.

So instead of putting the logic with the data its been moved to a System. The system however may have dependencies on multiple components so it describes its dependencies by defining a structure, a “Node”, in this case BulletNode. This class lists all the components an Entity must have before it can be used by the System.

What this means is that whenever an Entity (GameObject) is added or removed from the game the Ash Engine will automatically update the INodeList with of all the Entities that match the required components specified in BulletNode.

If that all looks a little too verbose for you, one of the improvements I made in this rewrite of Unity-Ash was to provide a short-hand way of writing Systems in the form of the “NodelessSystem”, so now the above example could look like:

public class Bullet : MonoBehaviour
{	
	public float maxAge = 3;
	public float age;
}

public class BulletAgingSystem : NodelessSystem<Bullet, Entity>
{
	public BulletAgeSystem()
	{
		_updateCallback = OnUpdate;
	}
	
	private void OnUpdate(float delta, Bullet bullet, Entity entity)
	{
		bullet.age += delta;
		if (bullet.age >= bullet.maxAge)
			entity.Destroy();
	}
}

Now you may be thinking that’s all well and good but it doesn’t really improve things, you are just splitting the Monobehaviour up and adding more classes to the the game, which is true, but there are several benefits to working this way. One of those benefits becomes evident when you think about dependencies between components.

Suppose that we want to handle what happens when the player spaceship collides with an asteroid. We want to respawn the player somewhere but it would be a bit unfair if we respawned the player on top of an asteroid so we need to check that the new spawn location isn’t too close to an Asteroid.

How would we go about this in Unity? Well one solution may look something like the following:

public class Player : MonoBehaviour
{	
	private PlayerManager manager;
	
	void Awake()
	{
		manager = FindObjectOfType<PlayerManager>();
	}
	
	 void OnCollisionEnter2D(Collision2D coll)
	 {
		 var asteroid = hit.gameObject.GetComponent<Asteroid>();
         if (asteroid != null)
		 	manager.PlayerHitByAsteroid(this);
	 }	
}

public class PlayerManager : MonoBehaviour
{	
	public void PlayerHitByAsteroid(Player player)
	{
		var asteroids = FindObjectsOfType<Asteroid>();
		
		// Find a place to respawn the Player
		var postion = UnityEngine.Random.insideUnitCircle*1000;
		while (!IsClearToAddShip(position, asteroids))
			postion = UnityEngine.Random.insideUnitCircle*1000;
		
		player.transform.position.x = postion.x;
		player.transform.position.y = postion.y;
	}
	
	private bool IsClearToAddShip(Vector2 newSpaceshipPosition, IEnumberable<Asteroid> asteroids)
	{
		foreach (var asteroid in asteroids)
			if (Vector2.Distance(asteroid.transform.position, newSpaceshipPosition) <= 1f)
				return false;

		return true;
	}
}

Sure there are a few ways this could potentially be done but the point im trying to highlight is the way dependencies are handled in a typical Unity game. Usually we have managers which look after some part of the game and then we use FindObjectsOfType or Tags to get access to the entities in the system.

The ash way of doing it may looks something like:

public class Player : MonoBehaviour
{
}
	
public class Collisions : MonoBehaviour
{
	public List<Collision2D> hits = new List<Collision2D>();

	void OnCollisionEnter2D(Collision2D coll)
	{
		hits.Add(coll);
	}
}

public class PlayerCollisionNode
{
	public Player Player { get; set; }
	public Collision Collision { get; set; }
	public Transform Transform { get; set; }
}

public class AsteroidNode
{
	public Asteroid Asteroid { get; set; }
	public Transform Transform { get; set; }
}

public class PlayerCollisionSystem : ISystem
{
	private INodeList<PlayerCollisionNode> _players;
	private INodeList<AsteroidNode> _asteroids;
	
	public void AddedToEngine(Engine engine)
	{
		_players = engine.GetNodes<PlayerCollisionNode>();
		_asteroids = engine.GetNodes<AsteroidNode>();
	}

	public void RemovedFromEngine(Engine engine)
	{
		engine.ReleaseNodes(_players);
		engine.ReleaseNodes(_asteroids);
	}

	public void Update(float delta)
	{
		foreach (var playerNode in _players)
		{
			foreach (var hit in playerNode.Collision.hits)
            {
				var asteroid = hit.gameObject.GetComponent<Asteroid>();
                if (asteroid == null)
                    continue;
					
				RespawnPlayer(playerNode);
			}
			_players.Collision.hits.Clear();
		}
	}
	
	private void RespawnPlayer(PlayerCollisionNode node)
	{
		var postion = UnityEngine.Random.insideUnitCircle*1000;
		while (!IsClearToAddShip(position, asteroids))
			postion = UnityEngine.Random.insideUnitCircle*1000;
		
		node.Transform.position.x = postion.x;
		node.Transform.position.y = postion.y;
	}
	
	private bool IsClearToAddShip(Vector2 newSpaceshipPosition)
	{
		foreach (var asteroidNode in _asteroids)
			if (Vector2.Distance(asteroidNode.Transform.position, newSpaceshipPosition) <= 1f)
				return false;

		return true;
	}
}

Here we can see we have made the Collisions component generic so that it can be used by Asteroids, Bullets or whatever else requires collisions. Then we have a system that is specific to Player Collisions.

The PlayerCollisionSystem then gets references to the players and the asteroids and does the respawning logic.

Its important to realize that in Ash, Systems are not allowed to reference each other. They can only communicate by adding or removing entities / components or changing properties on those components. This may seem like a bit of a restriction at first but its really important to keep things separated and free from complex dependencies, as a side benefit it makes Unit Testing easier (more on that later).

Single Point of Entry

So the final piece of the puzzle is how do these System’s come together? What calls the AddedToEngine, RemovedFromEngine, and Update methods on the Systems?

Well this is another way that Ash differs from the typical Unity Game. Usually in a Unity MonoBehaviour you may add Awake() or Start() handlers which get triggered by Unity when the game starts up. The problem is that it makes it quite hard to know what order things are going to get initiated in. It also makes it really hard for a new coder to come to the project and understand how the project works.

In Ash you have a single entry point. So for example you may have something like:

public class GameBoostrapper : MonoBehaviour
{
	private EntityCreator creator;
	private Engine engine;

	void Awake()
	{
		creator = new EntityCreator();
		engine = new Engine();

		engine.AddSystem(new MenusSystem(), SystemPriorities.PreUpdate);
		engine.AddSystem(new PlayerRespawningSystem(creator), SystemPriorities.PreUpdate);
		engine.AddSystem(new LevelingSystem(creator), SystemPriorities.Update);
		engine.AddSystem(new MotionControlSystem(), SystemPriorities.Update);
		engine.AddSystem(new SpaceshipCollisionSystem(creator), SystemPriorities.ResolveCollisions);
		engine.AddSystem(new HudSystem(), SystemPriorities.Animate);
		engine.AddSystem(new AudioSystem(), SystemPriorities.Render);

		creator.CreateGame();
	}

	void Update()
	{
		engine.Update(Time.deltaTime);
	}
}    

The Engine is the main control object for Ash. It holds a list of Systems which are updated each tick of the game.

The EntityCreator isn’t an Ash specific class and is rather a helper class which is used to abstract out the creation of the entities from the business logic of the various systems (also important for the Unit Testing as we will see later).

There is one more piece I haven’t mentioned yet. How is the Engine informed when Entities are added or removed from Unity? Well this is one of those places where Unity-Ash differs from AS3 Ash.

All GameObjects that should be picked up by the Ash Engine require that an “Entity” component be added to the GameObject:

2015-10-30_11-57-09

The “Entity” represents an Entity in Ash but in Unity is actually a MonoBehaviour. It tells the Engine when the GameObject it is created and destroyed. One difference from my previous work on Ash is that if you want to add or remove components from a GameObject at runtime you should do it via the Entity directly so that it can inform the Engine:

public interface IEntity
{
	...
	T Add<T>() where T : Component;
	void Remove(Component component);
	...
}

This change was required to get around all the performance problems related to checking for component changes each frame.

Unit Testing

Well this is another massive benefit to using Ash but this post is already way too long so ill save Unit Testing for the next part, stay tuned!

For now you can checkout Unity-Ash here: https://github.com/mikecann/Unity-Ash

If you want to see an example of its usage you can see here: https://github.com/mikecann/UnityAshteroids

If you want some more info on Ash then you should check out the official Ash Framework site: http://www.ashframework.org/

Note its all still really new so things are probably going to be in flux quite a bit. I am really interested to get peoples opinions on this however. Let me know what you think!