Skip to content
Jeff Campbell edited this page Aug 21, 2020 · 3 revisions

Overview

These are attributes that can be used to add additional functionality through code-generation. Some change how components are associated with an entity or Context while others generate helper or search functionality.

Unique

A unique component is a global component that exists on the Context rather than an Entity. A component is marked as unique when decorated with the [Unique] attribute. A unique component is useful when there is shared or global state to the entire Context.

Defining a Unique Component

using JCMG.EntitasRedux;

[Unique]
public class ExampleUniqueComponent : IComponent 
{
    public string value;
}

Example Unique Component API usage

// In this example, the name of the context is Game
Contexts.SharedInstance.Game.SetExampleUnique("Foo");
Contexts.SharedInstance.Game.RemoveExampleUnique();

Event

Adding an [Event] attribute to a component generates additional interfaces and systems for reactive logic based on the component being changed on an Entity. This is ideal for use-cases like reactive UX/UI where an player-facing element should be updated based on a component change.

This is different from reactive systems in that using the [Event] attribute and generated listener interfaces, systems would be better for external services, views, or traditional MonoBehaviour-based components that need to be updated based on a component change whereas reactive systems are better for organizing reactive systemic logic largely in the Context itself.

The [Event] attribute accepts several arguments that can alter what interfaces, systems are generated.

public EventAttribute(EventTarget eventTarget, EventType eventType = EventType.Added, int priority = 0)

EventTarget

The EventTarget enum denotes what event listeners should be invoked when a component is changed.

  • If EventTarget.Any is specified, whenever any Entity has that component changed, all event listeners on every entity that has that component will be invoked.
  • If EventTarget.Self is specified, whenever an Entity has that component changed, only the event listeners on that Entity will be invoked.
public enum EventTarget
{
	Any,
	Self
}

EventType

The EventType enum denotes what type of component change should trigger an event listener to be invoked.

  • If EventType.Added is specified or no EventType parameter is supplied (this is the default value), event listeners will be invoked whenever the component is added, updated, or replaced.
  • If EventType.Removed is specified, event listeners will be invoked whenever the component is removed from an entity.
public enum EventType
{
	Added,
	Removed
}

Example Event Component

[Game]
[Event(EventTarget.Self)]
public class PositionComponent : IComponent
{
  public float x;
  public float y;
}

Example Listener Interface Generated

public interface IPositionListener 
{
    void OnPosition(GameEntity entity, string value);
}

Example Listener Implementation

public class TestPositionListener : IPositionListener 
{
    public void OnPosition(GameEntity entity, string value)
    {
        // Do Nothing
    }
}

Example Event API Usage

IPositionListener listener = new TestPositionListener();
entity.AddPositionListener(listener);
entity.RemovePositionListener(listener);

Priority

The priority parameter is used as a way to define an execution order amongst all other event systems that are generated.

Cleanup

Adding an [Cleanup] attribute to a component generates an ICleanup system and the logic of that system changes based on what CleanupMode enum value is supplied.

  • Decorating a component with [Cleanup(CleanupMode.RemoveComponent)] will result in a new ICleanup system that removes components of that type from entities.
  • Decorating a component with [Cleanup(CleanupMode.DestroyEntity)] will result in a new ICleanup system that destroys all entities with that component.

Example Cleanup Component

using JCMG.EntitasRedux;

[VisualDebug]
[Cleanup(CleanupMode.DestroyEntity)]
[Cleanup(CleanupMode.RemoveComponent)]
public sealed class DestroyedComponent : IComponent
{
}

Generated Cleanup System to Destroy Entities RemoveDestroyedFromVisualDebugEntitiesSystem

using System.Collections.Generic;
using JCMG.EntitasRedux;

public sealed class DestroyVisualDebugEntitiesWithDestroyedSystem : ICleanupSystem
{
	private readonly IGroup<VisualDebugEntity> _group;
	private readonly List<VisualDebugEntity> _entities;

	public DestroyVisualDebugEntitiesWithDestroyedSystem(IContext<VisualDebugEntity> context)
	{
		_group = context.GetGroup(VisualDebugMatcher.Destroyed);
		_entities = new List<VisualDebugEntity>();
	}

	/// <summary>
	/// Performs cleanup logic after other systems have executed.
	/// </summary>
	public void Cleanup()
	{
		_group.GetEntities(_entities);
		for (var i = 0; i < _entities.Count; ++i)
		{
			_entities[i].Destroy();
		}
	}
}

Generated Cleanup System to Remove Components from Entities DestroyVisualDebugEntitiesWithDestroyedSystem

using System.Collections.Generic;
using JCMG.EntitasRedux;

public sealed class RemoveDestroyedFromVisualDebugEntitiesSystem : ICleanupSystem
{
	private readonly IGroup<VisualDebugEntity> _group;
	private readonly List<VisualDebugEntity> _entities;

	public RemoveDestroyedFromVisualDebugEntitiesSystem(IContext<VisualDebugEntity> context)
	{
		_group = context.GetGroup(VisualDebugMatcher.Destroyed);
		_entities = new List<VisualDebugEntity>();
	}

	/// <summary>
	/// Performs cleanup logic after other systems have executed.
	/// </summary>
	public void Cleanup()
	{
		_group.GetEntities(_entities);
		for (var i = 0; i < _entities.Count; ++i)
		{
			_entities[i].IsDestroyed = false;
		}
	}
}

ComponentName

Adding a [ComponentName] attribute on a non-component type enables the developer to generate new components with those names where the member of that component will be that type. This can be useful where multiple components can be generated from a single type implementation that would otherwise need to be defined as their own individual component by hand.

Example Attribute Usage of [ComponentName] on a Component

[Serializable]
[ComponentName("PositionComponent", "VelocityComponent")]
public struct IntVector2
{
	public int x;
	public int y;
}

Example Generated Components

//------------------------------------------------------------------------------
// <auto-generated>
//		This code was generated by a tool (Genesis v1.2.1, branch:develop).
//
//
//		Changes to this file may cause incorrect behavior and will be lost if
//		the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[JCMG.EntitasRedux.DontGenerate(false)]
public sealed class PositionComponent : JCMG.EntitasRedux.IComponent 
{
    public IntVector2 value;
}
//------------------------------------------------------------------------------
// <auto-generated>
//		This code was generated by a tool (Genesis v1.2.1, branch:develop).
//
//
//		Changes to this file may cause incorrect behavior and will be lost if
//		the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[JCMG.EntitasRedux.DontGenerate(false)]
public sealed class VelocityComponent : JCMG.EntitasRedux.IComponent 
{
    public ExampleContent.VisualDebugging.IntVector2 value;
}

DontGenerate

Using the [DontGenerate] attribute on a component will effectively prevent it from being used for the standard code-generation EntitasRedux offers. This is useful for cases where a code-generation itself might create a component and so decorating it with [DontGenerate] will prevent it from being discovered a second time.

Example Attribute Usage on a Code-Generated Component

//------------------------------------------------------------------------------
// <auto-generated>
//		This code was generated by a tool (Genesis v1.2.1, branch:develop).
//
//
//		Changes to this file may cause incorrect behavior and will be lost if
//		the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[JCMG.EntitasRedux.DontGenerate(false)]
public sealed class PositionComponent : JCMG.EntitasRedux.IComponent 
{
    public ExampleContent.VisualDebugging.IntVector2 value;
}

Primary Entity Index

The [PrimaryEntityIndex] attribute can be used on a public field of a component to indicate that it's value should be unique across all entities in that Context. In addition, a method will be generated for the Context that enables searching for an entity using that value. Internally, this creates a PrimaryEntityIndex using this component and field and adds it to the Context at runtime.

Example Attribute Usage

[Game]
public class UniqueIDComponent : IComponent 
{
	[PrimaryEntityIndex]
	public string value;
}

Example API Usage

var gameContext = Contexts.SharedInstance.Game;

// Attempts to retrieve an entity whose UniqueIDComponent value is FooID
var gameEntity = gameContext.GetEntityWithUniqueID("FooID");

Entity Index

The [EntityIndex] attribute can be used on a public field of a component to generate code for methods on the Context to enable retrieval of a collection of entities that have that component and whose field value matches the passed one. Internally this generates an EntityIndex for that component and value and adds it to the Context.

[Game]
public class PositionComponent : IComponent 
{
	[EntityIndex]
	public Vector2Int value;
}

Example API Usage

var gameContext = Contexts.SharedInstance.Game;

// Retrieves all entities at the passed position
var gameEntity = gameContext.GetEntitiesWithPosition(new Vector2Int(4,5));

PostConstructorAttribute

The [PostConstructor] attribute is used by the Contexts class to execute additive logic in the Contexts class after all various Context instances have been initialized. This is largely used by code-generators to be able to insert and execute post-constructor logic in the Contexts class.