Repository navigation
Components
[Component] // name = "Position"
public struct Position { public int X { get; set; } public int Y { get; set; } }
[Component(Name = "hp")] // template key: hp
public class Health { public int Max { get; set; } public int Current { get; set; } }Rules:
- Members are populated through settable properties (any visibility). Fields are not populated and are reported through
EntityDiagnostics. - Names match case-insensitively.
- Unknown component types throw with template + member context.
[Component(IsGlobal = true)] shares one instance per World (stored as DefaultEcs single-component). It is first write wins: the first spawn sets it, later spawns link to it and a warning is raised if their values differ.
Implement IValueComponent<T> when a component is just a primitive or string.
Scalars in templates convert to the member type in this order:
- already assignable
- a registered parser for the member type
- enums (case-insensitive name or number)
- primitives,
string,decimal(invariant culture) -
Guid,TimeSpan,DateTime,DateTimeOffset,Uri,Versionfrom strings
Out-of-range numbers throw OverflowException; 2.5 into an int throws FormatException (2.0 is fine).
TemplateValueParsers.Register(static s => Color.FromHex(s)); // thread-safeParsers run on every spawn, so cache inside the parser when the result is immutable.
Dice members are parsed from strings ("2d6+3", "4d6k3", "d%").
var d = Dice.Parse("2d6+3");
d.Roll(); // thread-local random
d.Roll(new Random(seed)); // reproducible
Dice.TryParse(text, out d); // no exceptionsSee the README for the full syntax table.
On older IL2CPP and NativeAOT, register each component type once at startup: AotComponentRegistry.Register<Health>();. See the Unity Guide.
RoguelikeToolkit.Entities