Documentation menu

Entity types and property sets

Description: The schema layer — how a character, an item, or a quest objective knows which fields it has.

Example Entity Type

A character has a name, a role, a bio and a portrait. An item has a stack size. A kill objective has a target. All three are the same idea wearing different clothes: a thing with a set of fields, and a definition somewhere that says which fields it gets.

In Narratyr that definition is split in two. An entity type is the template — "this is what a Character is." A property set is a reusable bundle of fields that types are assembled from. And an entity is one actual thing built to that template, holding the values.

Layer What it is Example
Property set A named group of fields CharacterProperties: FullName, Gender, Role, Bio
Entity type A template: one or more property sets, plus how it's used and shown Character includes CharacterProperties and RelatedEntities
Entity One thing of that type, with values filled in Mira Thorne — FullName "Mira Thorne", Role NPC

Each layer has its own view in the left sidebar: Property Sets, Types, and Characters & Items. Most days you'll work in the last of those; the first two are where you go when the shape of something needs to change.

Why fields live in property sets

The split exists so that fields can be defined once and used by several types. A project with Player, NPC and Merchant types doesn't want three separate definitions of "first name, last name, role" that drift apart — it wants one CharacterProperties set attached to all three. Adding a field to that set gives it to all three at once.

That sharing is worth being deliberate about, because it cuts both ways. When you edit a property set from inside a type that isn't its only user, the editor tells you which other types are affected and offers Make unique — that copies the set, attaches the copy to the type you're editing, and detaches the original, so you can change fields without touching anything else.

A set used by exactly one type is perfectly normal too. KillObjective holding a single TargetId field will never be shared with anything, and that's fine — the grouping still shows up in the editor and in your exported code.

Property sets

A property set has a name, an optional description, and a list of fields. It also has a folder path, so the Property Sets view is a tree you can organise the way you organise everything else.

The name is an identifier — letters, digits and underscores, no spaces — because exporters use it directly as the name of a generated class or struct. Pick it the way you'd name a type in code.

Fields

A field looks a lot like a variable: a name, a type, and a default value. The difference is where the value lives. A variable holds one value for the whole game; a field holds a different value on every entity that has it.

Fields cover the same types variables do — text, number, whole number, true/false, an option set, a reference to another entity, and lists or sets of those — plus one variables don't have: an asset, so a type can carry an image, a sound, or any other file in your project.

A few per-field switches change how the field behaves in the editor and in export:

  • Required — the validator reports an error for any entity that leaves it empty. Nothing stops you saving in the meantime; it's a check, not a lock.
  • Multiline and Rich Text — how a text field is edited. Both apply to text fields only.
  • Translatable — marks a text field as player-facing content that needs localising, as opposed to an internal note.

An entity-reference field can be narrowed to a specific entity type, so a Merchant field that should point at a shop location offers locations rather than every entity in the project. A list or set of entity references models "many of these" — a character's known associates, an objective's several targets.

Fields are for what belongs to a specific thing; variables are for what belongs to the world. A character's role is a field. Whether the player has met that character is a variable.

Entity types

A type is mostly a list of the property sets it includes. Its fields are the union of every attached set, in the order the sets are listed — drag them to reorder, and the entity editor follows, showing one labelled group per set.

Alongside that, a type carries:

Setting What it does
Name An identifier, used in the editor and as the generated class name on export
Description A note to yourself; shown as a tooltip in the type list
Usage What kind of thing this type describes — see below
Color and Icon How instances of the type are shown in the editor. The icon is an image asset from your project
Associated Option Set/Entry Pairs the type with one entry of an option set, for the dispatch pattern below

Types aren't organised in folders. The Types view lists them grouped by usage and sorted by name, which is short enough to scan even in a large project.

Usage: not everything is a character

The Usage setting is the part that surprises people. Entity types don't only describe things in your game world — the same schema machinery is what lets graph nodes carry custom data. A type's usage says which of those jobs it's doing, and the editor only offers it where it fits.

Usage Describes
entity An ordinary thing — a character, a location, a piece of lore. Created in Characters & Items
item The same, marked as an inventory item
dialogue_node A variant of a dialogue node — a narrator line that carries an intonation, say. Chosen per node in its properties
quest_node Extra data on a quest graph's Quest Node
quest_objective_data The fields one kind of objective needs — a target to kill, an item and a count to fetch
custom_node The payload of a custom node: the data your game receives with the event
graph_metadata Per-graph metadata on a quest or dialogue, picked as that graph's Metadata Type
performance Reserved for the built-in Performance type, which supplies the delivery fields on dialogue nodes

Two consequences worth knowing:

  • Only entity and item types are referenceable. An entity-reference variable or field offers characters and items, never node-shaped types — those describe the shape of a node, not something addressable in your world. In the Characters & Items Add menu, the same split is why the supporting types appear greyed out below the ones you actually create.
  • The Performance type is created for you and can't be deleted. Its property sets are yours to edit, though: whatever fields you put on it are the ones every dialogue node's performance offers.

Pairing a type with an option set

quest_objective_data types use the associated option set to work out which fields go with which kind of objective. An Objective node asks you to pick an Objective Type from an option set — Kill, Fetch, TalkTo — and the editor then looks for the type whose associated entry matches the one you picked, and shows that type's fields. Pick Kill and you get KillObjective's target field; pick Fetch and you get an item and a count.

So adding a new kind of objective to a project is two steps rather than a code change: add an entry to the option set, then add an entity type that points at it and carries the fields that kind of objective needs.

Changing a schema later

Schemas move as a project grows. What's safe and what isn't:

  • Renaming is safe. Values are stored against a field's internal id, not its name, so renaming a field — or a property set, or a type — never disturbs the values already filled in. See Documents and the file format for how that's stored.
  • Adding a field is safe. Every entity of every type that includes the set gains it. Entities created afterwards start from the field's default value; ones that already existed show it empty until you fill it in.
  • Detaching a property set from a type is reversible. The fields stop appearing, but the values stay on the entities — re-attach the set and they're back.
  • Deleting a field or a property set removes data. The stored values go with it, on every entity and every graph node that used it. Undo covers you until you save; after that, source control does.
  • An entity's type is fixed once it exists. Narratyr stores entities in a folder per type, so a character can't be turned into an item. Create the new one and copy the values across.

What reaches your game

Both layers survive export. Each property set becomes a class or struct named after it, and each entity type becomes a class holding one member per attached set — so the grouping you author is the grouping your game code sees. In Unity, a Character type built from a CharacterProperties set comes out as:

[System.Serializable]
public class CharacterProperties
{
    public string FullName;
    public CharacterRoleEnum Role;
    public string Bio;
}

[System.Serializable]
public class CharacterEntity : Narratyr.NarratyrEntity
{
    public CharacterProperties CharacterProperties;
}

That's why type and property-set names have to be identifiers, and why they're worth choosing carefully: they're the names your programmers type. An individual entity's handle is separate — its External ID, which you set per entity and which your game uses to find that specific character. See Exporting.

Next

Add a character walks through creating an entity against a type that already exists. Variables covers the other half of the data model — the global state that isn't attached to any one thing — and Documents and the file format covers how types, property sets, and entities are stored on disk.