Creating a Standard Instruction

  1. Create the C# class in Assets/Scripts/Instructions/
    Extend PlayerInstruction and add the [TypeName("YourName")] attribute. The TypeName is how the instruction is stored in save files — never change it after shipping.
  2. Override displayName
    Return the string shown in the instruction sheet UI (e.g. "Teleport").
  3. 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.
  4. Override Tick(ExecutionContext ctx)
    This is the coroutine. Use yield return new WaitForSeconds(t) to wait for animations. To cancel execution and signal a failure, set resolution = false then yield break. Always show a notification first so the player understands why it failed.
  5. Set up the Action component
    Duplicate any prefab from Assets/Prefabs/Player Instructions/. On the root GameObject, set the Action component's two fields: Instruction Type → your new C# class; Layout → the InstructionLayout prefab on the child GameObject. The Action bridges the palette item to the runtime instruction and its visual widget. See Create an Instruction Layout for the full prefab setup workflow.
  6. Configure the InstructionLayout
    On the child GameObject, set the display name, icon, and which direction buttons appear in the InstructionLayout component. See Create an Instruction Layout for all configurable fields.
  7. Add to the palette
    Place the prefab inside the palette panel in your scene. Make sure each palette item has a DragAndDrop component. 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.

  1. Extend InstructionModifier instead of PlayerInstruction
  2. Implement ModifyInstruction() — change instruction.direction, state, or any property of the child
  3. The prefab needs an InstructionModifier_Layout (not a plain InstructionLayout) 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.

  1. Extend InstructionSequence
  2. Override maxCapacity to set the maximum number of children (e.g. 4)
  3. The prefab needs an InstructionSequence_Layout component for the child slot UI — see Create an Instruction Layout
  4. Execute() and Tick() 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

PatternEffect
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.