Skip to content

Move interact verb/action to HUD#2150

Merged
manuq merged 1 commit intomainfrom
wjt/move-interact-verb-action-to-hud
Apr 29, 2026
Merged

Move interact verb/action to HUD#2150
manuq merged 1 commit intomainfrom
wjt/move-interact-verb-action-to-hud

Conversation

@wjt
Copy link
Copy Markdown
Member

@wjt wjt commented Apr 28, 2026

Instead of showing text over the object in the world, show it on the HUD
at the bottom of the screen, in place of the "Interact" label.

Show a bouncing red arrow above the currently-targetted interactable
entity. Show nothing above interactable entities that are not currently
targetted, but define an "idle" animation for this case so that we could
in theory change this later just by adjusting the animation.

Define the arrow position with a Marker2D attached to the InteractArea.
Migrate all existing interact_label_position values to a new Marker2D
node on each - these will not all be correct, but some of them might be.
Manually adjust some, including the tricky case of the Stella tortoise.
(Doing this by moving a Marker2D around in the editor is so much easier
than when I did it by editing Vector2 values by hand!) For some cases
where the default position - 64px above the InteractArea - is good
enough, I removed the Marker2D for a simpler scene & smaller diff.

Make all InteractArea nodes visible. (In a number of cases we previously
hid them to reduce the visual clutter, but this would hide the arrow.) I
may have missed some but we can fix them as we find them.

At runtime, if no Marker2D is provided, place one 64px
above the InteractArea node - better than nothing.

Adjust the logic in character_sight.gd to only emit
interact_area_changed if it actually does change, rather than just
because the player moved so that a second object is in range without
changing the "best" object. (This has no visible effect but was
necessary to add a sound effect on change, which I tried then removed.)

Resolves #2114

@github-actions
Copy link
Copy Markdown

Play this branch at https://play.threadbare.game/branches/endlessm/wjt/move-interact-verb-action-to-hud/.

(This launches the game from the start, not directly at the change(s) in this pull request.)

@wjt
Copy link
Copy Markdown
Member Author

wjt commented Apr 28, 2026

This is a draft for a number of reasons, not least that interact_label_position is not a good property name for the arrow position, and because there is a bug where the arrows appear much higher for the rocks in situ than the arrow does when editing the rock scene itself and I don't know why.

But another thing I realised while testing this… I had really hoped that we could avoid needing the ScreenOverlay CanvasLayer, on the basis that we seem to get away OK with the grapple direction arrow on the player being smaller when the view zooms out, and because we could then just WONTFIX #725.

However while testing this I happened to look at res://scenes/quests/story_quests/stella/3_stella_sequence_puzzle/stella_sequence_puzzle.tscn and discovered that the Guardian NPC in that scene is scaled to 1.5×1.5, and so due to the way I have implemented this feature, so is the arrow above her head. It looks really bad.

When I first prototyped this I put a static, dimmed/semitransparent arrow above all interactable objects, with the currently-targetted one (if any) bouncing and fully opaque. This looked really busy in areas with many interactable objects, such as sequence puzzles. If we are happy to only show a single arrow at a time (not sure!) then we could instead have the PlayerInteraction node get the position to show the arrow from the InteractArea it is targeting, and have a single arrow that gets moved around the screen as a child of the player node. A bit weird but it would mean the arrow follows the player scale (i.e. hopefully 1×, but if someone does scale the player for some reason it would also scale the grapple arrow...), leaving only the camera zoom problem.

Otherwise... maybe we are stuck with the ScreenOverlay (but could we use the InputHud autoload which is guaranteed always to be present?) and then we'll have to solve the dizzying effect still.

@manuq
Copy link
Copy Markdown
Collaborator

manuq commented Apr 28, 2026

This is a draft for a number of reasons, not least that interact_label_position is not a good property name for the arrow position, and because there is a bug where the arrows appear much higher for the rocks in situ than the arrow does when editing the rock scene itself and I don't know why.

I see that in code you create a Marker2D and then instantiate the interact_indicator.tscn . Can we just remove the interact_label_position offset add a Marker2D directly as child of InteractArea in the scenes using it? Similar to @export var anchor_point: Marker2D in HookableArea. Or just instantiate the arrow directly in scenes, and make it an export of the InteractArea? Something like:

## Node2D used to display to indicate that this area can be interacted.
@export var indicator: Node2D

But another thing I realised while testing this… I had really hoped that we could avoid needing the ScreenOverlay CanvasLayer, on the basis that we seem to get away OK with the grapple direction arrow on the player being smaller when the view zooms out, and because we could then just WONTFIX #725.

However while testing this I happened to look at res://scenes/quests/story_quests/stella/3_stella_sequence_puzzle/stella_sequence_puzzle.tscn and discovered that the Guardian NPC in that scene is scaled to 1.5×1.5, and so due to the way I have implemented this feature, so is the arrow above her head. It looks really bad.

This is what I see:
image

In my opinion it doesn't look that bad. This Guardian is an instance of Talker. Maybe it can just be a StaticBody2D with the AnimatedSprite2D (scaled at 1.5), plus the TalkBehavior, plus the InteractArea as children. Like a one-off node for this particular scene.

I also would like to say goodbye to ScreenOverlay :)

When I first prototyped this I put a static, dimmed/semitransparent arrow above all interactable objects, with the currently-targetted one (if any) bouncing and fully opaque. This looked really busy in areas with many interactable objects, such as sequence puzzles. If we are happy to only show a single arrow at a time (not sure!) then we could instead have the PlayerInteraction node get the position to show the arrow from the InteractArea it is targeting, and have a single arrow that gets moved around the screen as a child of the player node. A bit weird but it would mean the arrow follows the player scale (i.e. hopefully 1×, but if someone does scale the player for some reason it would also scale the grapple arrow...), leaving only the camera zoom problem.

I remember watching a screenshare with the multiple semi-transparent arrows and yes, my first impression was that it was a bit busy. I like it one at the time, like in this draft.

When you say you would like to reuse a single arrow, is it for consistency? For optimization? I wouldn't mind having each InteractArea with their own arrow that gets disabled/enabled, at least as a first step.

Otherwise... maybe we are stuck with the ScreenOverlay (but could we use the InputHud autoload which is guaranteed always to be present?) and then we'll have to solve the dizzying effect still.

@manuq
Copy link
Copy Markdown
Collaborator

manuq commented Apr 28, 2026

After playing some more... I really like where this is going. Maybe the "[Space] Talk to..." in the HUD can have a visual/audio clue when it changes (for later). Also I wonder if the arrow is too tiny for the Eternal Loom or other big things to interact, but I guess it's fine!

@wjt
Copy link
Copy Markdown
Member Author

wjt commented Apr 28, 2026

I see that in code you create a Marker2D and then instantiate the interact_indicator.tscn . Can we just remove the interact_label_position offset add a Marker2D directly as child of InteractArea in the scenes using it? Similar to @export var anchor_point: Marker2D in HookableArea.

That's what I plan to do. (And in fact you're on the right track that this was the cause of the misalignment bug - in one code path I set the position on the marker, and in the other I set the position on the indicator.)

I was hoping to have a clever tool script migrate all the scenes that set interact_label_position to instead add a marker at that position. Even if we have to (gradually!) go through and fix all their positions later.

Or just instantiate the arrow directly in scenes, and make it an export of the InteractArea? Something like:

## Node2D used to display to indicate that this area can be interacted.
@export var indicator: Node2D

The reason I didn't do this is that we want (I think?) the indicator to be consistent between every interactable thing, so it would be manual work each time to put the indicator in place, and then a nightmare if we want to change how it works later!

In my opinion it doesn't look that bad.

OK!

This Guardian is an instance of Talker. Maybe it can just be a StaticBody2D with the AnimatedSprite2D (scaled at 1.5), plus the TalkBehavior, plus the InteractArea as children. Like a one-off node for this particular scene.

True!

I remember watching a screenshare with the multiple semi-transparent arrows and yes, my first impression was that it was a bit busy. I like it one at the time, like in this draft.

👍🏻

When you say you would like to reuse a single arrow, is it for consistency? For optimization? I wouldn't mind having each InteractArea with their own arrow that gets disabled/enabled, at least as a first step.

It was for showing a single arrow in ScreenOverlay/InputHud, moved around to the current target. I don't think there is likely to be a performance issue with one arrow per target but all bar one being invisible.

OK, I will continue down this path.

@wjt wjt force-pushed the wjt/move-interact-verb-action-to-hud branch from 478f259 to 07b7111 Compare April 29, 2026 11:56
@wjt wjt changed the title Move interact verb/action to HUD [1/1] Move interact verb/action to HUD Apr 29, 2026
@wjt wjt force-pushed the wjt/move-interact-verb-action-to-hud branch 2 times, most recently from 6fac8b9 to 54b0dc1 Compare April 29, 2026 12:09
@wjt
Copy link
Copy Markdown
Member Author

wjt commented Apr 29, 2026

I finished migrating all existing interact_label_position values to Marker2Ds. To do this I took advantage of the power of @tool scripts: in InteractArea._ready, if running in the editor, I would:

  1. Add a Marker2D child, assigning it the correct owner to be saved to the scene
  2. If interact_label_position was non-zero, I assigned its position to the Marker2D then cleared its value
  3. Now I just opened every scene that uses interact_area.gd and saved all scenes.

I tweaked the Stella tortoise by hand.

I also tried adding a little sound effect whenever the interact target changes. I think it's good? I wondered about making the "(A) Talk to Sigurd" label emit particles in this case too but decided that's too much, and visual design is not my forté.

@wjt
Copy link
Copy Markdown
Member Author

wjt commented Apr 29, 2026

One more problem I noticed: the arrow is in the world layer so is affected by weather.

@wjt wjt force-pushed the wjt/move-interact-verb-action-to-hud branch from 54b0dc1 to ea56a0e Compare April 29, 2026 12:18
@wjt wjt changed the title [1/1] Move interact verb/action to HUD Move interact verb/action to HUD Apr 29, 2026
Comment thread scenes/ui_elements/input_hints/components/input_hud.gd Outdated
@wjt wjt force-pushed the wjt/move-interact-verb-action-to-hud branch from ea56a0e to 8de64ce Compare April 29, 2026 12:59
@wjt wjt changed the title Move interact verb/action to HUD [1/1] Move interact verb/action to HUD Apr 29, 2026
@wjt wjt force-pushed the wjt/move-interact-verb-action-to-hud branch from 8de64ce to a997d89 Compare April 29, 2026 13:08
@wjt wjt changed the title [1/1] Move interact verb/action to HUD Move interact verb/action to HUD Apr 29, 2026
@wjt wjt force-pushed the wjt/move-interact-verb-action-to-hud branch 3 times, most recently from df42d84 to 07367b9 Compare April 29, 2026 14:46
@wjt wjt changed the title Move interact verb/action to HUD [1/1] Move interact verb/action to HUD Apr 29, 2026
@wjt wjt force-pushed the wjt/move-interact-verb-action-to-hud branch from 07367b9 to 00c6712 Compare April 29, 2026 17:20
Instead of showing text over the object in the world, show it on the HUD
at the bottom of the screen, in place of the "Interact" label.

Show a bouncing red arrow above the currently-targetted interactable
entity. Show nothing above interactable entities that are not currently
targetted, but define an "idle" animation for this case so that we could
in theory change this later just by adjusting the animation.

Define the arrow position with a Marker2D attached to the InteractArea.
Migrate all existing interact_label_position values to a new Marker2D
node on each - these will not all be correct, but some of them might be.
Manually adjust some, including the tricky case of the Stella tortoise.
(Doing this by moving a Marker2D around in the editor is so much easier
than when I did it by editing Vector2 values by hand!) For some cases
where the default position - 64px above the InteractArea - is good
enough, I removed the Marker2D for a simpler scene & smaller diff.

Make all InteractArea nodes visible. (In a number of cases we previously
hid them to reduce the visual clutter, but this would hide the arrow.) I
may have missed some but we can fix them as we find them.

At runtime, if no Marker2D is provided, place one 64px
above the InteractArea node - better than nothing.

Adjust the logic in character_sight.gd to only emit
interact_area_changed if it actually does change, rather than just
because the player moved so that a second object is in range without
changing the "best" object. (This has no visible effect but was
necessary to add a sound effect on change, which I tried then removed.)

Resolves #2114
@wjt wjt force-pushed the wjt/move-interact-verb-action-to-hud branch from 00c6712 to e9f0903 Compare April 29, 2026 19:48
@wjt wjt changed the title [1/1] Move interact verb/action to HUD Move interact verb/action to HUD Apr 29, 2026
@wjt wjt marked this pull request as ready for review April 29, 2026 19:49
@wjt wjt requested review from a team as code owners April 29, 2026 19:49

## Emitted when [method can_interact] will return a different value.
signal can_interact_changed
## Emitted when the entity the player is able to interact with changes (possibly
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first read I though that you were trying to say either "the entity" or "the player", not both. But after a second read, I got it!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, it's a tricky sentence...

Comment on lines +27 to +31
## Returns the human-readable text of [member character]'s current interact
## action (e.g. [code]"Talk"[/code]), or [code]""[/code] (empty string) if
## [member character] cannot currently interact with anything. Use [signal
## interact_action_changed] to monitor for changes.
func get_interact_action() -> String:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment on lines +30 to +34
if marker and _indicator:
marker.remove_child(_indicator)
marker = new_value
if marker and _indicator:
marker.add_child(_indicator)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment on lines +88 to +89
# Vaguely sensible default position
marker.position = Vector2(0, -64)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 as you say in the PR description, is better than nothing.

func set_bouncing(new_value: bool) -> void:
bouncing = new_value
if animation_player:
animation_player.play(&"bounce" if new_value else &"idle")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that the bounce is done through AnimationPlayer!

Comment on lines +81 to +86
if player_interaction:
var action := player_interaction.get_interact_action()
interact_input_hint.visible = not action.is_empty()
interact_input_hint.text = action
else:
interact_input_hint.visible = false
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@manuq manuq merged commit 9a949de into main Apr 29, 2026
11 checks passed
@manuq manuq deleted the wjt/move-interact-verb-action-to-hud branch April 29, 2026 22:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Move interact prompt label to HUD

2 participants