Skip to content

What a build needs from a project

The authoring decisions that reach a compiler: codes that become tags and names, what an unimplemented command does, option sets, and the start nobody guesses.

All documentation — Authoring for a build
On this page

Most of what an author does in the editor never reaches your build as anything but data. A short list of decisions does — and each of them has a consequence a writer cannot see from inside the editor.

Codes become tags, and names

Almost every code an author types becomes a gameplay tag, and — with generated types on — a C++ identifier: node types, their fields, entities, variables, commands, tags, boards, option sets.

So the guidance to a writer is short: codes that look like identifiers produce tags and names somebody can read. Nothing judges whether a code reads well, so nothing will tell them otherwise.

Three consequences worth knowing precisely:

Two codes that differ only in case are one codea gameplay tag is a name and a name compares without case; so does every keyed store. The first declared wins, and the sync reports the other. TaleShard refuses such a pair of any kind, comparing codes without case and without anything but letters and digits (gold, Gold and go_ld are one code), and renames the later one when the project is opened; a pair synced before that is reported as code-clash. Two variables differing only in case stay two variables, and the later one by id is numbered (Gold_2, GetGold2) — see The tags
Node type codesthe generated library comes from a sanitised, uniquified name — never from the raw code
Field codes are unique per namespace, not per typea type can carry the same code at its top level and inside a repeater. Both survive; the repeater’s are read through Get Blocks

Entity codes

Two entities sharing a code resolve first-wins, with one duplicate-entity-code diagnostic at load. Nothing crashes and nothing is dropped; one of them is simply unreachable by code.

An entity’s id survives a rename and its code does not, which is why the payload and every save key on the id and carry the code alongside.

Input action codes

A record of a definition whose mentions draw as a keycap — the Puzzles & world pack’s Input action — is an input action, and its code is the one your game maps to its own action: Crouch to IA_Crouch. Renaming the record changes only the label a player reads; changing its code breaks that mapping, silently, in every prompt that names it. Its key name, pad control and touch gesture fields are previews the editor draws — the binding a player sees is your game’s. See an input action’s glyph.

Commands: optional to implement, never silent

Declaring a command in the editor gives the programmer something to answer it with: a TaleShard Command event in Blueprint, and with generated C++ an event on the generated command class. The build never breaks over a command nobody has implemented yet, and an awaited command never leaves the story hanging:

Declared asIn BlueprintWith generated C++, left unimplemented
Ordinaryan event with the arguments as typed pinsRuns, and warns once — command-not-implemented, naming the event
Awaitedthe same event with a Done pin, which the story waits onFails the completion with an error naming the event, and the story carries on

In Blueprint, a command with no event of its own reaches the command handler’s On Command, and the story does not wait for it. With generated C++, an awaited command is failed rather than left waiting because an unanswered wait is not a no-op but an infinite hang — the story would suspend and nothing would ever settle the completion.

Marking a command as awaited is a request to a programmer, not a setting — worth saying out loud before a project has fifty of them. Receiving one is implementing commands.

Option sets versus inline options

A select field can draw its values from a reusable option set, or carry its own options inline. Only the first produces an enum.

Inline optionsA reusable option set
Integer valuesNoneYes
Generated C++a plain stringa real UENUM, plus a tag per option
A variable of that typetextthe enum type — ETaleShardMood::Calm

The payload carries the option set’s id and not its option list, which is why the mapping cannot be built for an inline select at all.

An option the project no longer declares fails Try Parse; the plain Parse answers the enum’s first entry. A set with more than 255 options gets no enum at all — the tags stay.

The start the engine will not guess

A project names a start board and, optionally, a start node.

Set the game start explicitly. It is one field, and it is the difference between a build that plays and a build that opens on whatever node happened to sort first.

Gates and requirements do not treat blankness the same way

Blank means
A gate on an exitFails. A gate that is present and empty is an unfinished edit on a path meant to be conditional.
A requirement row on a nodePasses. An author who has not written the test yet has not blocked anything.

Both are deliberate and neither is configurable.

Two more, worth telling a writer once:

  • A node’s unmet requirements are hoisted onto every choice that leads to it, so the requirement is written once on the destination and every route in shows locked.
  • A jump bypasses them, because a jump is already an authored override of the graph.

Media filenames are addresses

A synced media file’s code is what its tag carries and what the media index resolves. Renaming the file in the editor changes the code, and an entity’s portrait is looked up by the reference the payload holds, not by name.

Nothing breaks silently here — a missing address is reported — but it does mean the names an author chooses are a public surface. Loading media has the rest.

Rich text and plain text are different fields

A rich-text field and a plain-text field carry different things, and the conversion to something drawable is different for each. That is a programmer’s problem — the presenter widget decides, see drawing a line of text — but it is worth knowing that the choice is made in the editor, and that a stage direction typed into a plain field can contain angle brackets safely while the same characters in a rich field are formatting.

Languages

The master language’s values live in the fields themselves; every other language lives in a translation bag. There is no runtime fallback chain — the sync fills a missing translation with the master language’s string.

So a partly-translated project syncs and plays. It just plays with some lines in the master language, which is the right failure and is not reported as an error.

Written for
Unity developers · Advanced integrators
Last updated