NPC Speech Bubble Component
Overview
NPC Speech Bubbles are world-space UI panels that float above characters and display short contextual text when the player is nearby. They provide ambient storytelling and gameplay signposting without requiring a full dialogue interaction — an NPC might say "Have you seen the blacksmith?" when you walk past, shift to "Please, find my missing cart!" once a relevant quest is active, and go silent after you've already spoken to them.
The system is built from two cooperating parts:
NPCSpeechBubble — a per-NPC MonoBehaviour that owns the bubble content, proximity detection, and conditions for what text to show.
SpeechBubbleManager — a scene singleton that coordinates all active bubbles, caps how many are visible at once, and handles object pooling so bubble UI instances are reused rather than instantiated and destroyed at runtime.
Text content is driven by Text Variants — a list of conditional text entries evaluated in priority order each time the bubble activates. Conditions can gate variants on quest state, dialogue flags, and objective progress, so the same NPC can say something different depending on where the player is in the story. If no variant condition matches, a configurable Default Text is shown instead.
Bubbles respond to proximity: each NPC defines its own activation and deactivation distances, with optional hysteresis to prevent flickering. Line-of-sight checking is also supported. The Manager enforces a global cap on how many bubbles can be visible simultaneously, prioritising the NPCs closest to the player when the limit is reached.

Properties
Dialogue Trigger
DialogueTrigger
Optional link to a DialogueTrigger on the same NPC. Auto-detected via GetComponent at Start if not assigned. Used by the quest indicator and auto-hide logic.
Player Transform
TransformReference
The player transform used for distance calculations. Falls back to GameObject.FindGameObjectWithTag("Player") at Start if left empty.
Bubble Anchor
Transform
The world-space transform the bubble is parented to. If unset, the NPC's own transform is used. The Bubble Offset is applied in local space relative to this anchor.
NPC Aligner
Aligner
Optional Malbers Aligner component. When assigned, the NPC rotates to face the player the moment they enter activation range.
Proximity Settings
Activation Distance
Float (1–50)
4
Player must be within this distance (metres) for the bubble to become eligible to show.
Deactivation Distance
Float (1–55)
5
Player must move beyond this distance for the bubble to become ineligible. Must be ≥ Activation Distance — auto-corrected with a warning if not. The gap between the two values provides hysteresis: the bubble won't flicker on and off as the player hovers right at the edge.
Require Line of Sight
Bool
false
When enabled, a Physics raycast is cast from the NPC to the player each frame. If an obstacle on Obstacle Layers blocks it, the NPC is treated as out of range even if within distance.
Obstacle Layers
LayerMask
All
The layers that count as obstructions for line-of-sight checks. Only relevant when Require Line of Sight is true.
Text Content
Text Variants
SpeechBubbleTextVariant[]
Empty
Conditional text entries evaluated in priority order. See Text Variants section below.
Default Text
String
"Hello there!"
Shown when no Text Variant's condition is met, or when the variants list is empty. At Start, if no variants are configured, a single Always variant is automatically created from this value.
Randomize Text
Bool
false
When true, selects randomly from all matching variants rather than taking the highest-priority one.
Cycle Text
Bool
false
When true, re-evaluates and updates the displayed text every Text Cycle Interval seconds while the bubble is visible.
Text Cycle Interval
Float
5
Seconds between text cycles. Only used when Cycle Text is enabled.
Quest Integration
Linked Quest
Quest
None
The quest this NPC is associated with. Drives the quest state indicator icon and the hide-on-complete behaviour.
Linked Objective
QuestObjective
None
A specific objective within the linked quest. Reserved for fine-grained per-objective indicator logic.
Hide When Quest Complete
Bool
true
When true, the bubble stops showing entirely once the linked quest reaches the completed state.
Show Quest Indicator
Bool
true
When true, an icon is overlaid on the bubble reflecting the quest's current state. See Quest State Indicator below.
Visual Settings
Custom Bubble Prefab
GameObject
None
Override the SpeechBubbleManager's default prefab for this specific NPC. The prefab must have a SpeechBubbleUI component. If left blank, the Manager's pooled prefab is used.
Bubble Offset
Vector3
(0, 2, 0)
Local-space offset from the anchor transform. Adjust Y to position the bubble above the NPC's head.
Billboard to Camera
Bool
true
Passes this flag to the SpeechBubbleUI component, which rotates the bubble to face the camera each frame.
Fade In Duration
Float
0.3
Duration in seconds for the bubble's appear fade.
Fade Out Duration
Float
0.2
Duration in seconds for the bubble's disappear fade. The bubble instance is not returned to the pool until this duration has elapsed.
Use Typing Effect
Bool
false
When true, text is revealed character by character at Typing Speed rather than appearing all at once.
Typing Speed
Float
0.05
Seconds per character for the typing effect. Only used when Use Typing Effect is true.
Audio
Audio Source
AudioSource
The AudioSource used for all bubble sounds. Assign for spatialised (3D) audio anchored to the NPC. If unassigned, no sounds will play.
Default Bubble Sound
AudioClipReference
Sound played when the bubble shows, unless the active Text Variant has its own variantSound assigned. Variant sounds always take priority over this default.
Interaction
Interaction Distance
Float (0.5–10)
2
When the player is within this distance, the Interaction Prompt is displayed on the bubble via SpeechBubbleUI.ShowInteractionPrompt().
Interaction Prompt
String
"Press E to talk"
The text shown in the interaction prompt area of the bubble UI when the player is close enough.
Auto Hide on Dialogue
Bool
true
Hides the bubble for all NPCs whenever any dialogue begins. The bubble re-appears after dialogue ends, subject to the cooldown below.
Cooldown After Dialogue
Float
2
Seconds after a dialogue ends before this bubble is allowed to show again. Prevents bubbles from immediately popping back up after a conversation.
Quest State Styling
Controls the colours and icons shown in the quest state indicator when Show Quest Indicator is enabled.
Quest Available Color
Color
Yellow
Tint applied to the indicator icon when the linked quest is available but not yet started.
Quest Active Color
Color
White
Tint applied when the quest is in progress but not yet completable.
Quest Complete Color
Color
Cyan
Tint applied when all objectives are done and the quest is ready to turn in.
Quest Available Icon
Sprite
None
Icon sprite for the available state. Conventionally an exclamation mark (!).
Quest Active Icon
Sprite
None
Icon sprite for the in-progress state. Conventionally a question mark (?).
Quest Complete Icon
Sprite
None
Icon sprite for the ready-to-turn-in state.
Events
OnBubbleShown
Immediately after ShowBubble() makes the bubble visible and fires the fade-in.
OnBubbleHidden
Immediately after HideBubble() is called, before the fade-out completes.
OnPlayerEnterRange
The first frame the player's distance drops to or below Activation Distance (and passes line-of-sight if required).
OnPlayerExitRange
The first frame the player's distance exceeds Deactivation Distance.
Text Variants
Text Variants (SpeechBubbleTextVariant) are the core of the contextual text system. Each variant is an independent entry in the list that carries its own display text, a condition, and a priority score.
At Start, all variants are sorted by Priority (highest first) and this order is retained for the lifetime of the component.
When the bubble is about to show, GetCurrentVariant() runs EvaluateConditions() on each variant in priority order and collects all that pass. The winner is:
The first match (highest priority) — normal mode.
A random pick from all matches — when Randomize Text is enabled.
If nothing matches, Default Text is shown instead.
Text Variant Fields
Text
String
"Hello!"
The speech bubble text displayed when this variant is selected.
Condition Type
Enum
Always
Which condition must pass. See condition types below.
Required Quest
Quest
None
The quest referenced by quest-based condition types.
Quest State Condition
Enum
Active
The required state. Used by some condition types for context (see below).
Required Objective
QuestObjective
None
A specific objective that must be completed. Only evaluated by QuestObjectiveComplete.
Required Dialogue Flag
String
""
The flag name to check in DialogueManager. Only evaluated by DialogueFlag.
Expected Flag Value
Bool
true
Whether the flag must be true or false. Only evaluated by DialogueFlag.
Variant Sound
AudioClipReference
None
Per-variant audio override. When assigned, this plays instead of the component-level Default Bubble Sound whenever this variant is selected or cycled to.
Priority
Int (0–100)
0
Higher numbers are evaluated first. When multiple variants match, the highest-priority one wins (unless Randomize Text is on).
Condition Types
Always
Always passes. Use for default ambient chatter that shows when no other condition matches.
QuestAvailable
The Required Quest is available (not yet started, not completed).
QuestActive
The Required Quest is currently active.
QuestCompleted
The Required Quest has been completed.
QuestObjectiveComplete
The Required Quest is active and the Required Objective has been completed within it.
DialogueFlag
The flag named Required Dialogue Flag in DialogueManager equals Expected Flag Value.
NoQuestActive
The Required Quest is not currently active (available or completed — anything but in-progress).
Tip: Stack multiple variants with increasing priority to create layered NPC awareness. A priority-0
Alwaysvariant provides ambient dialogue; a priority-10QuestActivevariant overrides it once the quest begins; a priority-20QuestObjectiveCompletevariant overrides that once a key task is done.
Quest State Indicator
When Show Quest Indicator is enabled and a Linked Quest is assigned, the indicator logic evaluates quest state each time ShowBubble() is called:
Quest available (not yet started)
Quest Available Icon
Quest Available Color
Quest active and objectives incomplete
Quest Active Icon
Quest Active Color
Quest active and all objectives complete (ready to turn in)
Quest Complete Icon
Quest Complete Color
Quest completed
Icon hidden
—
Quest not found / no quest assigned
Icon hidden
—
Visibility Gate Order
The Manager calls CanShowBubble before deciding to show any bubble. All of the following must pass:
Not Force Hidden (via
ForceHide()).Player is in range — distance ≤ activation threshold and line-of-sight passes if required.
No dialogue is active, or Auto Hide on Dialogue is
false.Cooldown after dialogue has elapsed.
Linked quest is not completed, or Hide When Quest Complete is
false.
If all five pass, the Manager then applies its own global cap (see Speech Bubble Manager below). A bubble that passes all local conditions but is pushed out by the cap is neither shown nor hidden — it waits for the next priority update cycle.
Public API
ShowBubble()
Called by the Manager. Acquires a pooled instance, resolves the current text variant, applies visual settings, and triggers the fade-in.
HideBubble(bool immediate = false)
Called by the Manager or internally. Triggers fade-out. The pool release is delayed by fadeOutDuration + 0.1s unless immediate is true.
ForceHide(bool immediate = false)
Sets the isForcedHidden flag and hides the bubble. The bubble will not show again until ReEnableAfterForceHide() is called.
ReEnableAfterForceHide()
Clears the forced-hide flag and requests a Manager visibility update.
ManualShow()
Clears the forced-hide flag and requests a Manager visibility update. Does not bypass proximity or condition checks — the Manager decides whether to show.
ManualHide()
Calls HideBubble() with the default fade.
RefreshText()
If the bubble is currently visible, re-evaluates the variant conditions and updates the displayed text immediately.
UpdateDistanceToPlayer()
Recalculates cachedDistanceToPlayer and updates isInRange. Called every frame in Update() and can also be called externally.
IsVisible()
Returns true if the bubble is currently shown.
Read-only properties:
IsBubbleVisible
Bool
Whether the bubble is currently in the shown state.
IsInRange
Bool
Whether the player is currently within the activation/deactivation hysteresis zone.
DistanceToPlayer
Float
Last cached world-space distance to the player. Updated every frame.
CanShowBubble
Bool
Evaluates all five local visibility gates. Does not account for Manager limits.
BubbleAnchorTransform
Transform
The resolved anchor — bubbleAnchor if set, otherwise the NPC's own transform.
Scene Gizmos
Gizmos are only drawn when Debug Mode is enabled on the component.
Wire sphere (always)
Green
Activation distance
Wire sphere (always)
Yellow
Deactivation distance
Wire sphere (always)
Blue
Interaction distance
Wire cube (selected only)
Cyan
World position of the bubble anchor + offset
Scene label (selected only)
Cyan
"Speech Bubble\nN variants"
Usage Tips
Hysteresis distance — always set Deactivation Distance 1–2 metres above Activation Distance. A player standing exactly at the activation boundary will constantly cross it if both values are equal, causing the bubble to flicker.
Layered variants — use
Alwaysat priority 0 as a fallback, then override it with quest-aware variants at higher priorities. The NPC appears aware of the player's progress without any extra scripting.Cycling ambient chatter — enable Cycle Text with several
Alwaysvariants at equal priority and Randomize Text enabled. The NPC will rotate through different lines of ambient speech while the player is nearby.Post-dialogue cooldown — set Cooldown After Dialogue to at least 2–3 seconds. This prevents the speech bubble from immediately reappearing right after the player closes a full dialogue conversation.
Per-NPC custom prefabs — if a particular NPC needs a stylistically different bubble (e.g. a monster growling instead of talking), assign a Custom Bubble Prefab directly on that NPC's component. All other NPCs continue using the Manager's pool.
Pool size — set Initial Pool Size to match or exceed Max Visible Bubbles. If the pool runs dry at runtime, a new instance is instantiated on the fly, which causes a brief hitch.
Global hide for cutscenes — call
SpeechBubbleManager.Instance.HideAllBubbles()at the start of a cutscene or menu, andShowAllBubbles()when returning to gameplay.Debug overlay — enable Debug Mode on the Manager during development to see a live stats overlay in Play Mode showing exactly how many bubbles are registered, eligible, and visible, alongside pool usage.
Last updated