POI Marker

Overview

POI Marker is the scene component that makes any GameObject a trackable Point of Interest. Attach it to an NPC, chest, dungeon entrance, or quest target and it will automatically appear as an icon on the minimap, compass bar, and world map.

The component acts as the bridge between the data layer (the PointOfInterest ScriptableObject) and the scene. It self-registers with POIManager on Start, drives its own 3D world-space billboard, and reacts autonomously to changes in quest state, objective progression, and dialogue completion — all without requiring external scripting.

POI Markers support two configuration modes: POI Data mode, where all settings are read from a shared PointOfInterest asset, and Custom Settings mode, where icon, name, colour, and category are set inline on the component itself.


Inspector Sections

POI Configuration

The core identity and lifecycle settings for this marker.

Property
Type
Default
Description

Use Custom Settings

bool

false

When enabled, replaces the POI Data asset slot with inline custom fields. Use for one-off markers that don't need a shared asset.

POI Data

PointOfInterest

The ScriptableObject asset supplying this marker's name, icons, colours, and display rules. Shown only when Use Custom Settings is off. A live preview box beneath the slot confirms the asset's name, category, and visibility flags.

Is Active

bool

true

Whether this POI is currently tracked and visible. Can be toggled at runtime via the IsActive property.

Auto Register

bool

true

Calls POIManager.RegisterPOI() automatically in Start. Disable only if you need to control registration timing manually.

Persistent

bool

false

Calls DontDestroyOnLoad in Awake so this POI survives scene transitions.


Custom Settings (only when Use Custom Settings is enabled)

Inline overrides that replace the POI Data asset entirely.

Property
Type
Description

POI Name

string

Display name shown on maps and tooltips.

Map Icon

Sprite

Icon used on the minimap and world map.

Compass Icon

Sprite

Icon used on the compass bar.

Icon Color

Color

Tint applied to both icons.

Category

POICategory

Category used for map filtering. Defaults to Custom.


Location Data (only for Location-category POIs)

When the assigned PointOfInterest asset has its category set to Location, a Location Data section appears showing a live comparison between the position stored in the asset and the marker's current scene transform.

Inspector Element
Description

Stored Position (Asset)

Read-only display of the WorldPosition field saved in the POI asset.

Current Position (Transform)

Read-only display of this marker's transform.position.

Sync World Position to Asset

Copies transform.position to the asset's WorldPosition field, marks the asset dirty, and calls AssetDatabase.SaveAssets(). This is the canonical way to set the asset's world position.

Move Marker to Asset Position

Moves this marker's transform to the position stored in the asset. Only works if the stored position is non-zero.

Location Info Preview

Read-only summary of Location ID, type, trigger radius, and parent location pulled directly from the asset.

The inspector warns when the stored position is still at (0, 0, 0), and also warns if the positions have drifted more than 1 metre apart since the last sync.


Location Discovery (only for Location-category POIs)

Controls the physics trigger that fires when the player enters this location's area.

Property
Type
Default
Description

Enable Location Trigger

bool

false

Activates a SphereCollider trigger on this GameObject at runtime. When the player enters, the LocationManager is notified and discovery logic runs.

Use Custom Trigger Radius

bool

false

Overrides the LocationRadius from the POI asset with a per-marker radius.

Custom Trigger Radius

float

50 m

The override radius, shown only when Use Custom Trigger Radius is enabled. The active radius from the asset is shown as a read-only field otherwise.

Auto-Complete Quest Objective

bool

true

Automatically completes any GoToLocationObjective in an active quest whose location ID matches this POI when the player enters.

Trigger Tag

string

"Player"

Only colliders with this tag trigger discovery.

Collider Management — The editor actively validates the collider state:

  • If no collider is present, the inspector explains one will be created at runtime and offers an Add SphereCollider Now button to add it immediately in edit mode.

  • If a collider exists but Is Trigger is off, a warning and an Enable Is Trigger button appear.

  • If a SphereCollider exists but its radius doesn't match the target radius, a Sync Collider Radius button appears.

Location Events

Event
Fires When

On Location Entered

The player's collider enters the trigger zone.

On Location Exited

The player's collider leaves the trigger zone.

On Location Discovered

The player enters and this is the first time (the location was not previously discovered).

In Play mode, the Location Discovery section also shows whether the player is currently inside the location, the current discovery status, and a Force Discover (Debug) button that calls TriggerLocationDiscovery() manually.


Quest Integration

Links this POI to a specific quest objective so its visibility is driven automatically by quest progression. The editor uses a two-step workflow:

Step 1 — Select Quest: Drag a Quest ScriptableObject asset into the Linked Quest field. Changing the quest clears any previously selected objective.

Step 2 — Select Objective: A dropdown appears listing every objective in the quest, formatted as index: ObjectiveType — description. Select the objective this POI represents. The Linked Objective ID field is then filled automatically (read-only) and a confirmation box displays the quest name, objective description, type, and whether it is optional.

Additional Settings (shown once a quest and objective are linked):

Property
Type
Default
Description

Hide When Complete

bool

true

Hides this POI when the linked objective is completed. Exception: if this is a discovered Location-category POI, it stays visible even after the objective completes.

Only Show When Active

bool

true

Hides the POI until the linked objective is the currently active step in the quest's sequential progression. Future and past objectives are hidden.

Auto-Link Location

bool

true

On Start, if no objective ID is set, scans active quests for a GoToLocationObjective whose targetPosition is within 5 metres of this marker and links to it automatically.

Show Gizmo

bool

true

Draws the objective link indicator in the Scene view (see Gizmos below).

Gizmo Color

Color

Cyan

Colour of the objective link gizmo. Only shown when Show Gizmo is enabled.


Dialogue Trigger Integration

When a DialogueTrigger on the same (or another) GameObject marks a conversation as completed, this POI can hide itself automatically — useful for quest givers who should no longer show a marker after their opening dialogue has played.

Property
Type
Default
Description

Auto-Detect

bool

true

At runtime, searches this GameObject with GetComponent<DialogueTrigger>() if no manual reference is assigned.

Linked Dialogue Trigger

DialogueTrigger

Manual reference to any DialogueTrigger in the scene. Takes precedence over auto-detection.

Hide When Completed

bool

true

Hides this POI when the linked trigger's HasTriggered becomes true, or if it was already completed in the save system when the scene loads.

When a manual reference is set, an info box shows the linked trigger's GameObject name, dialogue asset name, triggerOnce flag, and saveTriggerState setting.

In Play mode the section shows live status: whether the dialogue has been completed and whether the trigger can currently fire.

Save-system awareness — On Start, the POI Marker queries DialogueSaveSystem directly using the same ID generation logic as DialogueTrigger to check completion state. This avoids problems caused by same-frame execution order, where the DialogueTrigger's own Start() may not yet have run.


World Marker (3D)

An optional 3D prefab spawned in world space above this POI's position.

Property
Type
Default
Description

Marker Prefab

GameObject

Prefab instantiated as a child of this GameObject at Start (if the POI is active). If left empty, the POI appears only on 2D navigation surfaces.

Position Offset

Vector3

(0, 2, 0)

Local-space offset applied to the spawned instance. Also defines WorldPosition, which is what the map and elevation systems use for icon placement.

Face Camera

bool

true

Every Update, the world marker rotates to face Camera.main (billboard behaviour).

The world marker is created lazily: if the POI starts inactive, the prefab is not instantiated until the POI is activated. If the POI is a Location with HideUntilDiscovered, the prefab is not instantiated until the location is first discovered.


Events

State Events

Event
Signature
Fires When

On POI Activated

UnityEvent

IsActive changes to true.

On POI Deactivated

UnityEvent

IsActive changes to false.

Distance Events

Event
Signature
Fires When

On Distance Changed

UnityEvent<float>

The cached distance changes by more than 0.1 world units since last check. Passes the new distance.

On Player Enter Range

MEvent

The player crosses into the visible range window (MinDistanceMaxDistance).

On Player Exit Range

MEvent

The player exits the visible range window.


Debug Options

Property
Description

Debug Mode

Enables detailed Debug.Log output for all visibility decisions, registration events, objective evaluations, and location triggers.


Status & Debug Tools

In edit mode, the section validates the configuration:

  • Error if no POI Data is assigned and Custom Settings is off

  • Warning if Custom Name or Custom Map Icon is missing when using inline settings

  • Warning if a quest is selected but no objective has been chosen

  • Confirmation that auto-register is enabled, or a warning that manual registration is required

In Play mode, the section shows:

  • IsActive, current distance, name, and category

  • Live ShowOnMinimap, ShowOnCompass, ShowWorldMarker values (including discovery-gate results)

  • Quest link status: quest name, objective description, whether the quest is active/completed, whether the objective is in progress or done

Testing buttons (Play mode only):

Button
Effect

Activate / Deactivate

Toggles IsActive immediately. Label updates to reflect the current state.

Register

Calls RegisterPOI() manually.

Unregister

Calls UnregisterPOI() manually, which also destroys the world marker instance.


Scene Gizmos

Visible when the GameObject is selected in the Scene view.

Gizmo
Description

Inner wire sphere

Drawn at MinDistance radius, tinted with the POI's icon colour at 30% opacity. Represents the near-clip distance.

Outer wire sphere

Drawn at MaxDistance radius at 10% opacity. Represents the visibility range limit.

Wire cube

Drawn at transform.position + worldMarkerOffset. Shows exactly where the world marker prefab will spawn.

Objective link sphere

Cyan (configurable) 2 m wire sphere and a bold label above the object showing "Objective: [id]". Only shown when Show Gizmo is enabled and an objective ID is set.

Location trigger sphere

Green-cyan wire sphere at TriggerRadius, with a light filled sphere for area context. A bold label above shows the location name and radius. Only shown when Enable Location Trigger is enabled.

The OnSceneGUI handles also draw two stacked horizontal discs and a tall vertical line (10 units high) at the marker's position as a scene-view beacon, coloured with the POI's icon colour.


Visibility Resolution

POI Marker uses a priority chain to determine whether to show each navigation surface. All three (ShowOnMinimap, ShowOnCompass, ShowWorldMarker) apply the same gate order:

  1. POI Data flagpoiData.ShowOnMinimap (or equivalent) must be true.

  2. Discovery gate — If this is a Location POI and both IsDiscoverable and HideUntilDiscovered are true, the marker is hidden until LocationManager.IsDiscovered(locationId) returns true.

Objective Visibility Logic

When a quest and objective are linked and onlyShowWhenObjectiveActive is true, EvaluateObjectiveVisibility() determines IsActive following this precedence:

Condition
Result

Inspector isActive was false at Start

Always hidden — designer intent overrides everything.

Dialogue trigger has been completed

Hidden.

Quest not yet started or already completed

Hidden.

Objective completed + hideWhenObjectiveComplete true

Hidden — unless this is a discovered Location POI, in which case it remains visible.

Objective is the current active step in the quest sequence

Shown.

Objective is a future step not yet reached

Hidden.

This evaluation re-runs whenever a relevant QuestEventBus event fires (QuestStarted, QuestCompleted, QuestFailed, QuestProgressUpdated).


Public API

Registration

Method
Description

RegisterPOI()

Manually registers this marker with POIManager. Called automatically in Start when autoRegister is true.

UnregisterPOI()

Removes this marker from POIManager and destroys the world marker instance.

State

Member
Type
Description

IsActive

bool (get/set)

Setting to false hides the world marker and fires OnPOIDeactivated. Setting to true shows it and fires OnPOIActivated.

UpdateDistance()

void

Recalculates CurrentDistance and fires range events. Called by POIManager each update interval for registered markers.

Quest & Objective

Method
Returns
Description

EvaluateObjectiveVisibility()

void

Re-evaluates and updates IsActive based on the current quest and objective state. Called automatically when quest events fire; call manually after quest state changes outside the event bus.

SetLinkedObjectiveId(string)

void

Sets the linked objective ID at runtime.

GetLinkedObjectiveData()

QuestObjectiveData

Returns the runtime data for the linked objective from QuestManager, or null if not found.

IsObjectiveTracked()

bool

Returns true if the linked objective's isTracked flag is set in QuestManager.

Location

Method
Returns
Description

TriggerLocationDiscovery()

void

Manually marks this location as discovered via LocationManager.DiscoverLocation(). Use for scripted discovery events (cutscenes, loading screens).

IsLocationDiscovered()

bool

Returns LocationManager.IsDiscovered(poiData.LocationId), or false if this is not a Location POI.

IsDiscovered()

bool

Same as IsLocationDiscovered(). Returns true for non-Location POIs (they are always considered "discovered").

Waypoint Conversion

Method
Description

SetAsWaypoint(string name, Sprite icon, Color color)

Switches to Custom Settings mode and configures this marker as a Waypoint-category POI. Used by CustomWaypoint and waypoint creation systems.


Key Properties

Property
Returns
Description

POIName

string

Custom name if using custom settings, otherwise poiData.POIName.

MapIcon

Sprite

Custom icon or asset icon.

CompassIcon

Sprite

Custom icon or asset icon.

IconColor

Color

Custom colour or asset colour.

Category

POICategory

Custom category or asset category.

WorldPosition

Vector3

transform.position + worldMarkerOffset. This is what the minimap, world map, compass, and elevation system all use.

CurrentDistance

float

Cached distance to the player, updated at POIManager.updateInterval.

ShowOnMinimap

bool

Final resolved visibility after applying data flags and discovery gates.

ShowOnCompass

bool

Final resolved visibility.

ShowWorldMarker

bool

Final resolved visibility.

IsLocationPOI

bool

True when poiData != null && poiData.IsLocation.

TriggerRadius

float

Active location trigger radius: custom value if overriding, otherwise poiData.LocationRadius.

PlayerInsideLocation

bool

True while the player's collider is inside the location trigger zone.

LinkedDialogueTrigger

DialogueTrigger

The resolved dialogue trigger reference (auto-detected or manually set).


Workflow: Setting Up a New POI Marker

  1. Add the POI Marker component to the scene GameObject you want to track.

  2. Drag a PointOfInterest asset into the POI Data slot, or enable Use Custom Settings and fill in inline values.

  3. If this is a Location POI, click Sync World Position to Asset to write the scene position into the asset.

  4. To link with quest progression, select the Linked Quest asset, then pick the objective from the dropdown.

  5. If the location should trigger discovery when the player walks into it, enable Enable Location Trigger. Use the Add SphereCollider Now editor button if you prefer to see the collider at design time.

  6. Assign a Marker Prefab if you want a 3D billboard above the object in the game world.

  7. Enter Play mode — the icon should appear on the minimap and compass immediately.


Other Tips

  • WorldPosition includes the offset — The map icon is placed at transform.position + worldMarkerOffset, not at the raw transform. If icons appear 2 metres above where you expect on the map, the offset is the reason. Set it to Vector3.zero for flat-plane markers.

  • Objective visibility vs. IsActive — Setting IsActive = false in the inspector permanently suppresses the marker (the inspectorIsActive flag captures this at Start). The objective system will never re-enable it. If you want quest logic to control a marker that starts hidden, set isActive = true in the inspector and let the objective visibility logic hide it on first evaluation.

  • Discovery gate is per-surfaceHideUntilDiscovered hides the minimap and compass icons but the Location Discovery trigger is still active. The player can physically enter the area and discover the location even while the icon is invisible.

  • Auto-Link Location runs once at Start — If no objective ID is set and a matching GoToLocationObjective exists within 5 metres, it links automatically. This saves setup time for densely placed markers but can produce false matches if two objectives are close together. Prefer the Quest Integration dropdown for explicit control.

  • Same-frame execution with DialogueTrigger — The save-system check in InitializeDialogueTrigger() works around Unity's non-deterministic Start() order. No special script execution order settings are required.

  • Persistent markers across scenes — Mark isPersistent = true only for markers that genuinely need to survive the full game session (e.g. a hub town). Overusing persistence adds overhead and can cause duplicate entries if the same scene is reloaded.

Last updated