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 → InteractableObjectNoteInteractableObjectTriggerableBase provides the standard interactable object framework:
objectAssetreference.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
checkUseable(): Returns false if the cursor is showing (e.g., in a menu or inventory). Prevents note reading from other UI states.use(): Opens the note text inPlayerBarricadeSignUI, 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:
- Direct text — Static text defined in the asset's .dat file.
- Rich text — Text with rich formatting tags (color, bold, italic, size) using Unity's rich text markup.
- 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
| Feature | InteractableObjectNote | InteractableSign |
|---|---|---|
| Type | World object pool | Barricade (player-placed) |
| Text source | Asset data (interactabilityText) | Player-written (state bytes) |
| Editable | No | Yes |
| Network state | None (read-only) | Owner, group, text in state bytes |
| Rendering | PlayerBarricadeSignUI only | 3D mesh text + UI |
| Quest trigger | Yes (useObjectQuest) | No |
| Plugin hook | N/A | onModifySignRequested |
| Placement | Editor-placed by map author | Player-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
ObjectAssetwithinteractabilityText. - 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
- Read-only text — Notes have no mutable state; text comes from the asset definition. No sync protocol needed.
- Shared UI system — Notes reuse
PlayerBarricadeSignUI, the same UI that signs use. - Quest trigger integration — Single
use()call both displays text and progresses quests. - World object vs. barricade — Two distinct note systems: world-placed (static, quest-aware) vs. player-placed (dynamic, editable).
- Cursor gate —
checkUseable()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
| Feature | InteractableObjectNote | InteractableSign | Quest Dialog (DialogueAsset) | NPC Chat Bubble |
|---|---|---|---|---|
| Placement | Editor-placed object | Player-placed barricade | NPC-triggered | NPC proximity |
| Text source | interactabilityText (asset) | Player-written (state bytes) | DialogueAsset pages | NPC asset config |
| Editable by player | No | Yes (with permissions) | No | No |
| Quest integration | useObjectQuest() check | No | Direct quest progression | Quest dialogue tree |
| Rich text | Yes (Unity markup) | Yes | Yes (limited) | No |
| Persistence | Permanent (level data) | Until destroyed | Permanent (asset) | Ephemeral (proximity) |
| Multi-language | Yes (localization files) | No (raw player text) | Yes | Yes |
| Audience | All players in range | All players in range | Interacting player | Proximity players |
| Max text length | Asset limit (~4KB) | Up to 255 bytes | Unlimited pages | ~128 chars |
Failure Modes and Common Mistakes
Cursor gate blocking legitimate reads —
checkUseable()returns false whenPlayerUI.window.showCursoris 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.Quest triggers firing on every interaction —
useObjectQuest()is called unconditionally in theuse()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.PlayerLifeUI.close() on dedicated servers —
PlayerLifeUI.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.Text truncation in PlayerBarricadeSignUI — The
PlayerBarricadeSignUIsystem has a maximum text length determined by its UI layout. Extremely longinteractabilityTextvalues are truncated silently. Map authors writing extensive lore or instructions into a single note may find their text clipped.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,
InteractableObjectNoteinherits fromInteractableObjectTriggerableBase, 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.InteractableObjectNoteuses 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
PlayerNoteUIexists. In the SDK,InteractableObjectNotereusesPlayerBarricadeSignUI, 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
- Interactable Sign System — Barricade signs for player-authored text. Covers the contrast between static notes and editable signs.
- Quest Asset System — How quest conditions and rewards reference note objects as triggers.
- Dialogue Asset — Structured NPC dialogue compared to free-form note text.
- Level Editor Toolbar System — Placing note objects in the level editor.
