Skip to content

Interactable Note — Text Display System

Placing readable notes, quest instructions, and lore in the Unturned world requires understanding how InteractableObjectNote bridges static asset text with the player UI. InteractableObjectNote is a minimal class that opens a text display for player-readable notes. It inherits from InteractableObjectTriggerableBase, making it a world-object note (not a barricade-placement sign). The note text comes from objectAsset.interactabilityText — read-only asset data that cannot be edited by players. Notes also serve as quest triggers via ObjectManager.useObjectQuest().

World-object notes are distinct from barricade-placement signs (InteractableSign), which support player-authored text and server-side validation through BarricadeManager.

Source code location: Unturned/Interactable/InteractableObjectNote.cs

Inheritance Chain

MonoBehaviour → Interactable → InteractableObjectTriggerableBase → InteractableObjectNote

InteractableObjectTriggerableBase provides the standard interactable object framework:

  • objectAsset reference.
  • checkUseable() / checkHint() / use() virtual methods.
  • Quest-trigger integration.

Core Implementation

csharp
public class InteractableObjectNote : InteractableObjectTriggerableBase
{
    public override void use()
    {
        PlayerBarricadeSignUI.open(objectAsset.interactabilityText);
        PlayerLifeUI.close();
        ObjectManager.useObjectQuest(transform);
    }

    public override bool checkUseable()
    {
        return !PlayerUI.window.showCursor;
    }
}

Interaction Flow

  1. checkUseable(): Returns false if the cursor is showing (e.g., in a menu or inventory). Prevents note reading from other UI states.
  2. use(): Opens the note text in PlayerBarricadeSignUI, closes the player life UI (health/food/water bars), and triggers quest integration.

Text Sources

The note's text comes from objectAsset.interactabilityText. This property provides:

  1. Direct text — Static text defined in the asset's .dat file.
  2. Rich text — Text with rich formatting tags (color, bold, italic, size) using Unity's rich text markup.
  3. Localized text — Text resolved from the asset's localization files for multi-language support.

The note has no state data — it cannot be edited by players. This distinguishes it from InteractableSign (which supports player-authored text) and makes it suitable for world-building and quest instructions.

Text Rendering

The note text is displayed through the same PlayerBarricadeSignUI system that signs use. The UI component handles:

  • Rich text formatting (color, bold, italic, size, alignment).
  • Scrolling for long text.
  • Background panel rendering.
  • Close button / escape key handling.

Quest Integration

csharp
ObjectManager.useObjectQuest(transform);

This call checks if the note is associated with any quest's conditions or rewards. If the note is a quest trigger, reading it may:

  • Complete a quest condition: e.g., "Read the note in the library."
  • Trigger a quest reward: e.g., receive an item after reading.
  • Progress a multi-stage quest narrative: advance to the next quest stage.

The InteractableObjectQuest component (if present on the same GameObject) handles the actual quest progression.

Comparison: Object Note vs. Barricade Sign

FeatureInteractableObjectNoteInteractableSign
TypeWorld object poolBarricade (player-placed)
Text sourceAsset data (interactabilityText)Player-written (state bytes)
EditableNoYes
Network stateNone (read-only)Owner, group, text in state bytes
RenderingPlayerBarricadeSignUI only3D mesh text + UI
Quest triggerYes (useObjectQuest)No
Plugin hookN/AonModifySignRequested
PlacementEditor-placed by map authorPlayer-placed as barricade

Notes are static world objects used by map authors for narrative and quest guidance. Signs are dynamic player-authored objects for communication and decoration.

InteractableObjectTriggerableBase

The base class provides:

csharp
public abstract class InteractableObjectTriggerableBase : Interactable
{
    public ObjectAsset objectAsset { get; protected set; }

    public virtual bool checkUseable() => true;
    public virtual string checkHint() => null;
    public virtual void use() { }
}

All world-object interactables (notes, dialogues, NPCs, quest objects, resource objects, rubble) inherit from this base. It bridges the gap between Interactable (the low-level component) and individual object-type behaviors.

Note in the Level Editor

Notes are placed in the level editor as object instances:

  • The editor assigns an ObjectAsset with interactabilityText.
  • Notes can be placed anywhere and at any rotation.
  • The note's collider (typically a trigger) determines the interaction range.
  • The note text is set once in the asset and cannot be changed per-instance (unless using different ObjectAsset GUIDs).

Key Design Insights

  1. Read-only text — Notes have no mutable state; text comes from the asset definition. No sync protocol needed.
  2. Shared UI system — Notes reuse PlayerBarricadeSignUI, the same UI that signs use.
  3. Quest trigger integration — Single use() call both displays text and progresses quests.
  4. World object vs. barricade — Two distinct note systems: world-placed (static, quest-aware) vs. player-placed (dynamic, editable).
  5. Cursor gatecheckUseable() prevents note reading while in other UI states.

Worked Code Example: Custom Note System

Multi-Page Note Display

csharp
using SDG.Unturned;
using System.Collections.Generic;
using UnityEngine;

public class MultiPageNotePlugin
{
    private Dictionary<Transform, int> _notePages = new Dictionary<Transform, int>();

    /// <summary>
    /// Opens a multi-page note display by cycling through pages on each use.
    /// Requires the note asset to use a page separator token (---PAGE---).
    /// </summary>
    public void OpenMultiPageNote(Player player, Transform noteTransform, string text)
    {
        string[] pages = text.Split(new[] { "---PAGE---" }, System.StringSplitOptions.None);

        if (!_notePages.ContainsKey(noteTransform))
            _notePages[noteTransform] = 0;

        int currentPage = _notePages[noteTransform];
        string pageText = pages[currentPage % pages.Length]
            + $"\n\n[Page {currentPage + 1}/{pages.Length}]";

        PlayerBarricadeSignUI.open(pageText);
        PlayerLifeUI.close();
        ObjectManager.useObjectQuest(noteTransform);

        _notePages[noteTransform] = (currentPage + 1) % pages.Length;
    }
}

Quest Trigger Chain

csharp
using SDG.Unturned;
using UnityEngine;

public class QuestChainNotePlugin
{
    /// <summary>
    /// Activates a chain of quests when a specific note is read.
    /// First quest is triggered immediately; subsequent quests
    /// activate only after the previous quest completes.
    /// </summary>
    public static void HandleQuestChainNote(Player player, ushort[] questIds, Vector3 notePosition)
    {
        if (questIds == null || questIds.Length == 0)
            return;

        foreach (ushort questId in questIds)
        {
            // Check if player has already completed this quest
            if (player.quests.getQuestStatus(questId) != ENPCQuestStatus.COMPLETED)
            {
                // Activate the first uncompleted quest in the chain
                player.quests.ServerAddQuest(questId);

                ChatManager.serverSendMessage(
                    $"Quest chain started: {questId}",
                    Color.yellow,
                    toPlayer: player,
                    iconURL: null,
                    useRichTextFormatting: true
                );

                break;
            }
        }
    }
}

Mermaid Diagram: Note Interaction Flow

Comparison: Object Notes vs. Other Text Delivery Systems

FeatureInteractableObjectNoteInteractableSignQuest Dialog (DialogueAsset)NPC Chat Bubble
PlacementEditor-placed objectPlayer-placed barricadeNPC-triggeredNPC proximity
Text sourceinteractabilityText (asset)Player-written (state bytes)DialogueAsset pagesNPC asset config
Editable by playerNoYes (with permissions)NoNo
Quest integrationuseObjectQuest() checkNoDirect quest progressionQuest dialogue tree
Rich textYes (Unity markup)YesYes (limited)No
PersistencePermanent (level data)Until destroyedPermanent (asset)Ephemeral (proximity)
Multi-languageYes (localization files)No (raw player text)YesYes
AudienceAll players in rangeAll players in rangeInteracting playerProximity players
Max text lengthAsset limit (~4KB)Up to 255 bytesUnlimited pages~128 chars

Failure Modes and Common Mistakes

  1. Cursor gate blocking legitimate readscheckUseable() returns false when PlayerUI.window.showCursor is true. This means a note cannot be read while the player is in any UI state (inventory open, crafting menu open, chat entry active). Players attempting to interact with a note while managing inventory will see no response, leading to confusion about whether the note exists at all.

  2. Quest triggers firing on every interactionuseObjectQuest() is called unconditionally in the use() method. If a quest uses the note as a "read this note" condition, reading the same note again completes the condition again (harmless but inefficient). If the note is associated with a reward trigger, reading it repeatedly can grant the reward multiple times if the quest state is not properly gated.

  3. PlayerLifeUI.close() on dedicated serversPlayerLifeUI.close() runs on the client only. On a dedicated server, this method is a no-op. This is harmless but means server-side plugins cannot rely on the life UI state being consistent for note-reading players.

  4. Text truncation in PlayerBarricadeSignUI — The PlayerBarricadeSignUI system has a maximum text length determined by its UI layout. Extremely long interactabilityText values are truncated silently. Map authors writing extensive lore or instructions into a single note may find their text clipped.

  5. Collider misplacement — If the note object's collider (typically a trigger) is too small or misaligned with the visual note model, players may stand at the visual note and be unable to interact because the trigger collider doesn't overlap the player's interaction radius. The editor must ensure the collider extends to at least a 2-meter interaction sphere.

How This Field Behaves Differently from the SDG Docs

  • SDG docs suggest notes are a type of barricade. The official developer overview sometimes groups notes under "interactable barricades." In the SDK, InteractableObjectNote inherits from InteractableObjectTriggerableBase, which is the object interactable hierarchy — these are level-editor-placed, not player-placed. Notes are in the object pool, not the barricade pool.

  • SDG docs describe notes as supporting player-authored text. The community wiki mentions notes as a general text display tool. In the SDK, only InteractableSign (barricade signs) supports player-authored text. InteractableObjectNote uses asset-defined, read-only text. This distinction is critical for map authors planning narrative content.

  • SDG docs mention a dedicated Note UI. The documentation implies a separate PlayerNoteUI exists. In the SDK, InteractableObjectNote reuses PlayerBarricadeSignUI, the same UI that signs use. There is no dedicated note UI component.

Performance Considerations

Notes have effectively zero runtime cost beyond the interaction check. They have:

  • No per-frame updates (no Update() method).
  • No network state to synchronize (text is asset-defined, not mutable).
  • No physics queries beyond the initial trigger collider overlap.
  • One call to PlayerBarricadeSignUI.open() per interaction.

The only performance concern is the interactabilityText localization lookup, which resolves once when the note is interacted with. For thousands of notes in a level, the memory cost is the ObjectAsset references (~8 bytes of GUID storage per note).

Deeper FAQ

Q: Can I use HTML or custom markup in note text?

Note text supports Unity's rich text markup (<color>, <b>, <i>, <size>) but not HTML. Custom markup beyond Unity's supported tags is ignored and displayed as plain text. The rendering uses Unity's Text component with rich text enabled, so the full Unity rich text specification applies.

Q: Can notes trigger multiple quests at once?

Yes. ObjectManager.useObjectQuest(transform) checks the note's transform against all quest conditions and rewards. If the same note is referenced by multiple quests, all applicable quests trigger simultaneously. A single note read can progress several quest chains at once.

Q: How do I prevent a note from triggering quests in the editor preview?

In the editor, quest progression is not active. useObjectQuest() runs during level editing but quest conditions are only evaluated on the game server. The editor's note interaction will display the text but will not advance quest states.

Q: Can plugins intercept and modify the displayed note text?

Yes. A Harmony patch on PlayerBarricadeSignUI.open(string) can intercept and modify the text before it reaches the UI. This is commonly used for dynamic note content (e.g., player-specific text, time-of-day-conditional text, or server announcement boards).

Q: What localization file format do notes use?

Note text localization uses the standard Unturned localization system: .dat translation keys are defined in the asset's localization bundle, and the system resolves interactabilityText through Localization.read(key). The key format follows Object_<GUID>_Note convention.

Cross-References

Document history