Skip to content

Entitas CSharp Components

Maxim Zaks edited this page Dec 17, 2016 · 1 revision
comp Movable

Is a definition of a flag component which results in following code:

using Entitas;
public partial class MovableComponent : IComponent {
}

namespace Entitas {
	public partial class Entity {
		static readonly MovableComponent movableComponent = new MovableComponent();

		public bool isMovable {
			get { return HasComponent(ComponentIds.Movable); }
			set {
				if (value != isMovable) {
					if (value) {
						AddComponent(ComponentIds.Movable, movableComponent);
					} else {
						RemoveComponent(ComponentIds.Movable);
					}
				}
			}
		}
		
		public Entity IsMovable(bool value) {
			isMovable = value;
			return this;
		}
	}
}
public partial class Matcher {
	static IMatcher _matcherMovable;

	public static IMatcher Movable {
		get {
			if (_matcherMovable == null) {
				var matcher = (Matcher)Matcher.AllOf(ComponentIds.Movable);
				matcher.componentNames = ComponentIds.componentNames;
				_matcherMovable = matcher;
			}

			return _matcherMovable;
		}
	}
}

As you can see the get/set prefix for entity method is is we can change it by defining an alternativ prefix:

comp Movable / "isFlaggedAs"
...
namespace Entitas {
	public partial class Entity {
		static readonly MovableComponent movableComponent = new MovableComponent();

		public bool isFlaggedAsMovable {
			get { return HasComponent(ComponentIds.Movable); }
			set {
				if (value != isFlaggedAsMovable) {
					if (value) {
						AddComponent(ComponentIds.Movable, movableComponent);
					} else {
						RemoveComponent(ComponentIds.Movable);
					}
				}
			}
		}
		
		public Entity IsFlaggedAsMovable(bool value) {
			isFlaggedAsMovable = value;
			return this;
		}
	}
}
...

The component can also be defined as unique which means that it can be present only once in the pool.

comp unique Movable / "isFlaggedAs"

This implies that the Pool class is also extended.

...
public partial class Pool {
	public Entity movableEntity { get { return GetGroup(Matcher.Movable).GetSingleEntity(); } }
	public bool isFlaggedAsMovable {
		get { return movableEntity != null; }
		set {
			var entity = movableEntity;
			if (value != (entity != null)) {
				if (value) {
					CreateEntity().isFlaggedAsMovable = true;
				} else {
					DestroyEntity(entity);
				}
			}
		}
	}
}
...

If we want our component to hold values we have to first define a type alias.

alias int = "int"

This is needed because the DSL is programming language agnostic and have no awareness even from primitive types.

comp HealthPoints : int

If we want to define a component which holds only one value we can do it as written above. This produces following code

using Entitas;
public partial class HealthPointsComponent : IComponent {
	public int value;
}

namespace Entitas {
	public partial class Entity {
		public HealthPointsComponent healthPoints { get { return (HealthPointsComponent)GetComponent(ComponentIds.HealthPoints); } }
		public bool hasHealthPoints { get { return HasComponent(ComponentIds.HealthPoints); } }
		public Entity AddHealthPoints(int newValue) {
			var component = CreateComponent<HealthPointsComponent>(ComponentIds.HealthPoints);
			component.value = newValue;
			return AddComponent(ComponentIds.HealthPoints, component);
		}
		public Entity ReplaceHealthPoints(int newValue) {
			var component = CreateComponent<HealthPointsComponent>(ComponentIds.HealthPoints);
			component.value = newValue;
			ReplaceComponent(ComponentIds.HealthPoints, component);
			return this;
		}
		public Entity RemoveHealthPoints() {
			return RemoveComponent(ComponentIds.HealthPoints);
		}
	}
}
public partial class Matcher {
	static IMatcher _matcherHealthPoints;

	public static IMatcher HealthPoints {
		get {
			if (_matcherHealthPoints == null) {
				var matcher = (Matcher)Matcher.AllOf(ComponentIds.HealthPoints);
				matcher.componentNames = ComponentIds.componentNames;
				_matcherHealthPoints = matcher;
			}

			return _matcherHealthPoints;
		}
	}
}

As we can see, the generated component class has a value field of type int and Entity method / Matcher were generated accordingly.

comp unique HealthPoints : int

This is how we define the HealthPoints component to be unique.

comp HealthPoints {
 health : int
}

Is another way to describe the component. Here we actually tell what the property name is.

comp Position {
 x : int
 y : int
}

We use the same notation to describe a component with multiple properties.

comp Position {x: int, y: int}

And this is how we can write it on one line.

using Entitas;
public partial class PositionComponent : IComponent {
	public int x;
	public int y;
}

namespace Entitas {
	public partial class Entity {
		public PositionComponent position { get { return (PositionComponent)GetComponent(ComponentIds.Position); } }
		public bool hasPosition { get { return HasComponent(ComponentIds.Position); } }
		public Entity AddPosition(int newX, int newY) {
			var component = CreateComponent<PositionComponent>(ComponentIds.Position);
			component.x = newX;
			component.y = newY;
			return AddComponent(ComponentIds.Position, component);
		}
		public Entity ReplacePosition(int newX, int newY) {
			var component = CreateComponent<PositionComponent>(ComponentIds.Position);
			component.x = newX;
			component.y = newY;
			ReplaceComponent(ComponentIds.Position, component);
			return this;
		}
		public Entity RemovePosition() {
			return RemoveComponent(ComponentIds.Position);
		}
	}
}
public partial class Matcher {
	static IMatcher _matcherPosition;

	public static IMatcher Position {
		get {
			if (_matcherPosition == null) {
				var matcher = (Matcher)Matcher.AllOf(ComponentIds.Position);
				matcher.componentNames = ComponentIds.componentNames;
				_matcherPosition = matcher;
			}

			return _matcherPosition;
		}
	}
}

Entitas-CSharp has a concept of multiple pools. In order to use it we have to specify them first

ctx Core, Meta

In ECS-Lang we call pools context (ctx for short)

comp[Core] Position {x: int, y: int}
comp[Meta] Score : int

This is how we can annotate affiliation of a component.

comp[Core, Meta] PlayerId : string

As we can see in the last example, a component can be affiliated with multiple pools/context.

using Entitas;
public partial class PlayerIdComponent : IComponent {
	public string value;
}

namespace Entitas {
	public partial class Entity {
		public PlayerIdComponent playerId { get { return (PlayerIdComponent)GetComponent(MetaComponentIds.PlayerId); } }
		public bool hasPlayerId { get { return HasComponent(MetaComponentIds.PlayerId); } }
		public Entity AddPlayerId(string newValue) {
			var component = CreateComponent<PlayerIdComponent>(MetaComponentIds.PlayerId);
			component.value = newValue;
			return AddComponent(MetaComponentIds.PlayerId, component);
		}
		public Entity ReplacePlayerId(string newValue) {
			var component = CreateComponent<PlayerIdComponent>(MetaComponentIds.PlayerId);
			component.value = newValue;
			ReplaceComponent(MetaComponentIds.PlayerId, component);
			return this;
		}
		public Entity RemovePlayerId() {
			return RemoveComponent(MetaComponentIds.PlayerId);
		}
	}
}
public partial class MetaMatcher {
	static IMatcher _matcherPlayerId;

	public static IMatcher PlayerId {
		get {
			if (_matcherPlayerId == null) {
				var matcher = (Matcher)Matcher.AllOf(MetaComponentIds.PlayerId);
				matcher.componentNames = MetaComponentIds.componentNames;
				_matcherPlayerId = matcher;
			}

			return _matcherPlayerId;
		}
	}
}
public partial class CoreMatcher {
	static IMatcher _matcherPlayerId;

	public static IMatcher PlayerId {
		get {
			if (_matcherPlayerId == null) {
				var matcher = (Matcher)Matcher.AllOf(MetaComponentIds.PlayerId);
				matcher.componentNames = CoreComponentIds.componentNames;
				_matcherPlayerId = matcher;
			}

			return _matcherPlayerId;
		}
	}
}
Clone this wiki locally