-
-
Notifications
You must be signed in to change notification settings - Fork 13
Attributes
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.
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();
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)
The EventTarget
enum denotes what event listeners should be invoked when a component is changed.
- If
EventTarget.Any
is specified, whenever anyEntity
has that component changed, all event listeners on every entity that has that component will be invoked. - If
EventTarget.Self
is specified, whenever anEntity
has that component changed, only the event listeners on thatEntity
will be invoked.
public enum EventTarget
{
Any,
Self
}
The EventType
enum denotes what type of component change should trigger an event listener to be invoked.
- If
EventType.Added
is specified or noEventType
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);
The priority
parameter is used as a way to define an execution order amongst all other event systems that are generated.
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 newICleanup
system that removes components of that type from entities. - Decorating a component with
[Cleanup(CleanupMode.DestroyEntity)]
will result in a newICleanup
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;
}
}
}
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;
}
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;
}
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");
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));
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.