Manual
Create a New Player Instruction
Add a custom action to the instruction sheet. Every instruction is one C# class plus one prefab.
Creating a Standard Instruction
-
Create the C# class in
Assets/Scripts/Instructions/
ExtendPlayerInstructionand add the[TypeName("YourName")]attribute. The TypeName is how the instruction is stored in save files — never change it after shipping. -
Override
displayName
Return the string shown in the instruction sheet UI (e.g."Teleport"). -
Override
Execute()
Apply instant, synchronous effects here — dealing damage, spawning a prefab, modifying a state flag. This runs before any animation. Leave it empty if your instruction is purely animated. -
Override
Tick(ExecutionContext ctx)
This is the coroutine. Useyield return new WaitForSeconds(t)to wait for animations. To cancel execution and signal a failure, setresolution = falsethenyield break. Always show a notification first so the player understands why it failed. -
Set up the Action component
Duplicate any prefab fromAssets/Prefabs/Player Instructions/. On the root GameObject, set the Action component's two fields: Instruction Type → your new C# class; Layout → theInstructionLayoutprefab on the child GameObject. TheActionbridges the palette item to the runtime instruction and its visual widget. See Create an Instruction Layout for the full prefab setup workflow. -
Configure the InstructionLayout
On the child GameObject, set the display name, icon, and which direction buttons appear in theInstructionLayoutcomponent. See Create an Instruction Layout for all configurable fields. -
Add to the palette
Place the prefab inside the palette panel in your scene. Make sure each palette item has aDragAndDropcomponent. See Create an Instruction Layout for placement details.
Example — Teleport
Moves the player three cells forward. Fails if the destination is not a valid path tile.
using System.Collections; using UnityEngine; namespace LoopAdventure { [TypeName("Teleport")] public class Instruction_Teleport : PlayerInstruction { public override string displayName => "Teleport"; public override void Execute() { } protected override IEnumerator Tick(ExecutionContext ctx) { Vector2 target = ctx.player.position + direction.AsVector2() * MapManager.cellSize * 3; if (!ctx.player.ValidatePosition(target)) { NotificationCenter.Notify( NotificationCenter.MessageType.Warning, "Can't teleport there!"); resolution = false; yield break; } ctx.player.position = target; ctx.player.PlayAnimation("Idle"); } } }
Creating a Modifier Instruction
A modifier wraps one child instruction and alters its behaviour every loop. Extend InstructionModifier and implement ModifyInstruction() — called automatically after the child executes each iteration.
- Extend
InstructionModifierinstead ofPlayerInstruction - Implement
ModifyInstruction()— changeinstruction.direction, state, or any property of the child - The prefab needs an
InstructionModifier_Layout(not a plainInstructionLayout) so the UI renders the child slot — see Create an Instruction Layout
namespace LoopAdventure { [TypeName("RotateRight")] public class Instruction_RotateRight : InstructionModifier { public override string displayName => "Rotate Right"; public override void Execute() { } // Called after the child instruction executes each loop. public override void ModifyInstruction() { Direction next = (Direction)(((int)instruction.direction + 1) % 4); instruction.SetDirection(next); } } }
Creating a Sequence Instruction
A sequence holds multiple child instructions and executes them in order within a single turn. Extend InstructionSequence and define maxCapacity.
- Extend
InstructionSequence - Override
maxCapacityto set the maximum number of children (e.g.4) - The prefab needs an
InstructionSequence_Layoutcomponent for the child slot UI — see Create an Instruction Layout Execute()andTick()can be left empty — the sequence parent calls each child automatically
namespace LoopAdventure { [TypeName("MyCombo")] public class Instruction_MyCombo : InstructionSequence { public override string displayName => "My Combo"; public override int maxCapacity => 3; public override void Execute() { } protected override IEnumerator Tick(ExecutionContext ctx) { yield break; } } }
Signalling Failure
| Pattern | Effect |
|---|---|
resolution = false; yield break; | Marks the instruction as failed. The runner stops execution and shows the failure animation. |
NotificationCenter.Notify(type, message) | Displays a pop-up to the player. Always call this before yield break so the player knows what went wrong. |
TypeName is permanent
The string in
[TypeName("...")] is written into save files. Changing it after release will break any save that contains the old instruction name.