Skip to content

Attributes

Jesse Talavera-Greenberg edited this page Mar 1, 2019 · 23 revisions

Overview

The code generator currently supports the following attributes to be used with classes, interfaces and structs :

  • [Context]: You can use this attribute to make a component be available only in the specified context(s); e.g., [MyContextName], [Enemies], [UI], etc. Improves memory footprint. It can also create components.
  • [Unique]: The code generator will provide additional methods to ensure that up to a maximum of one entity with this component exists.
  • [FlagPrefix]: Can be used to support custom prefixes for flag components only.
  • [PrimaryEntityIndex]: Can be used to limit entities to a unique component value.
  • [EntityIndex]: Can be used to search for entities with a component value.
  • [CustomComponentName]: Generates multiple components with different names for one class or interface.
  • [DontGenerate]: The code generator will not process components with this attribute.
  • [Event]: The code generator will generate components, systems and interface to support reactive UI.
  • [Cleanup]: The code generator will generate systems to remove components or destroy entities.

Context

This attribute can be used for the following purposes:

  • Add a component to the default Context.
  • Add a component to a custom Context.
  • Generate a component from a class, interface or struct.

When used simply as [Context] on a class, interface or struct the code generator will create a new class for you with the suffix Component and add this new class to the default context; e.g., if your class is called Position the generated component will be called PositionComponent.

If you would like to use a custom context, first you must define it in the Entitas preference window. After creating a custom context such as 'Enemies' the code generator will subclass the default context attribute so that you can simply use [Enemies] as your custom context attribute in your class, interface or struct. The custom context classes can be found as individual files in your Generated Folder with the format {contextName}Attribute.cs.

Example

Add some custom contexts in the Entitas preference window and click on "Generate":

Entitas.Unity.CodeGeneration

You can now use the newly generated contexts attributes in your components:

using Entitas;
using Entitas.CodeGeneration;

[Game, Ui]
public class SceneComponent : IComponent
{
    public Scene Value;
}

[Game]
public class Bullet
{
    // Since it doesn't derive from 'IComponent'
    // it will be generated as 'BulletComponent'
}

[Meta]
public struct EditorOnlyVisual
{
    public bool ShowInMode;

    public EditorOnlyVisual(bool show) {
        this.ShowInMode = show;
    }
}

All of the above performs the same two operations: create a component and add it to one or more contexts.

Unique Entities

Add this attribute to components that should not have more than one instance in your application. This is an alternative for cases where you would want to use a singleton. It has the potential to throw an exception if circumvented in such a way that more than one entity with the component is found. It will generate the following additional properties and methods for the component:

  • Context.{ComponentName}Entity
  • Context.{ComponentName}.{ComponentProperty} (non-flag components)
  • Context.Set{ComponentName}({ComponentProperty}) (non-flag components)
  • Context.Replace{ComponentName}({ComponentProperty}) (non-flag components)
  • Context.Remove{ComponentName}() (non-flag components)
  • Context.has{ComponentName}() (non-flag components)

Example

Define your component:

using Entitas;
using Entitas.CodeGeneration.Attributes;

[Core] // Context name
[Unique]
public class UserComponent : IComponent {
    public string name;
    public int age;
}

You now have the following properties and methods available:

var context = Contexts.core;
var e = context.userEntity;
var name = context.user.name;
var has = context.hasUser;

context.SetUser("John", 42);
context.ReplaceUser("Max", 24);
context.RemoveUser();

FlagPrefix

Works only with components that act as flags. Flag components are empty classes, therefore have no fields and act as a form of boolean flag for entities. If this attribute is used in a component with public fields it will silently fail and no custom prefix will be generated.

Example

Create a flag component:

using Entitas;
using Entitas.CodeGeneration;

[FlagPrefix("flag")]
public class DestroyComponent : IComponent {
}

What the code generator would normally provide without the attribute:

entity.isDestroy = true;

With the [FlagPrefix] attribute:

entity.flagDestroy = true;

Possible Use Cases

  • When "is" as a flag prefix would read awkwardly.

PrimaryEntityIndex

Using this attribute on a component value will prevent duplicates of that value being used when adding that component to an entity. In addition, an accessor function will be added to the appropriate context to allow you to quickly find an entity with a value you are looking for.

Example

using Entitas;
using Entitas.CodeGeneration.Attributes;

// Core component to provide a name.  Will only allow a single entity with that name.
[Game]
public class NameComponent : IComponent {
    [PrimaryEntityIndex]
    public string value;
}

// Can use Game.GetEntityWithName("Foo");

Possible Use Cases

  • For components that are almost unique, but not quite.

EntityIndex

Using this attribute on a component value will allow you to search the context for all entities with the component attached and specific values.

Example

[Game]
public class FactionComponent : IComponent {
    [EntityIndex]
    public string name;
}
// Can now use Game.GetEntitiesWithFaction("Player");

CustomComponentName

With this attribute you can generate multiple components that differ only in name and share the same public fields. All from a single definition. This can be used to to enforce uniformity across multiple components and avoid the tedious task of writing all the components individually.

Example

The following will automatically generate PositionComponent, VelocityComponent and add them to the default context for you:

[Context, CustomComponentName("Position", "Velocity")]
public struct IntVector2 {
  public int x;
  public int y;
}

DontGenerate

This attribute is self-explanatory, any component with this attribute will be skipped by the code generator.

Example

using Entitas;
using Entitas.CodeGeneration;

[DontGenerate]
public class FutureFeatureComponent : IComponent {
}

Possible Use Cases

  • Mostly for internal use, to ensure that components generated by Entitas (e.g. event listeners) aren't themselves processed.
  • Also useful for similar reasons if generating components with your own code generator.

Event (aka Reactive-UI)

[Event({EventTarget}, {EventType}, {priority})]

Will generate additional listener interfaces and components that react to component change. Eliminate the need to write RenderXSystems.

Example

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

public class GameView: Monobehaviour, IPositionListener
{
  // Function to call after adding this View to a GameEntity
  public void RegisterListeners(Contexts contexts, GameEntity entity)
  {
    entity.AddGamePositionListener(this);
  }
  
  public void OnPosition(GameEntity entity, float x, float y)
  {
    transform.position = new Vector2(x,y);
  }
}

// using the same GameController from HelloWorld tutorial
public class GameController : MonoBehaviour
{
  ...
  private static Systems CreateSystems(Contexts contexts)
    {
      return new Feature("Systems")
        // Your systems here
        .Add(new GameEventSystems(contexts));
    }
  }
}

In the example above, when a GameEntity's position is changed, the generated GameEventSystems will call OnPosition(entity, x, y); for all subscribed listeners.

Parameters

Using the PositionComponent example for ease of understanding:

EventTarget

  • .Self: View's OnPosition will be called only when the listened GameEntity's position is changed.
  • .Any: View's OnPosition will be call when any GameEntity's position is changed.

First parameter of OnPosition is the entity whose Position has changed.

EventType

  • .Added (default): Will generate IPositionListener.
  • .Removed: Will generate IPositionRemovedListener.

priority

Decide generated systems' execution order.

Possible Use Cases

  • Playing animations
  • Playing sound effects
  • Updating UI (e.g. for score)
  • Any other interaction with APIs that exist outside of your game logic

Note: While generated Events can technically be used for game logic, we recommend using reactive systems for that instead.

Cleanup (Asset Store only)

There are currently two options:

  • CleanupMode.DestroyEntity
  • CleanupMode.RemoveComponent

CleanupMode.DestroyEntity will generate a system that destroys all entities which have this component.

CleanupMode.RemoveComponent will generate a system that will remove this component from all entities which have this component.

Example

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

Possible Use Cases

  • Processing components or entities that represent short-lived messages rather than game objects.
Clone this wiki locally