0 votes

Hello, everyone.

Faced with an issue that I cannot figure out for quite a long time.

I try to show a menu with some choices after the player hits the interact action near some specific object. Technically, the player model has a raycast2d and a function that detects a situation when the player hits an interact button near object, that has method called "on_interact".

I just completely don't understand how can I call an a UI and "deliver" it a control. It seems that I should create a condition in UI's unhandledinput that checks interact action and that the player stands near interactable object but I don't know how it can be implemented. Moreover, I am not sure the this way is a correct at all.

Unfortunately, most of what I've found either tells about interactions inside UI only or tells too superficially.

Here is some code for context. Here is a Player code of an interaction

func _process(delta):
if Input.is_action_just_pressed("interact"):
    try_interact()

func try_interact ():
    rayCast.cast_to = facingDir * interactDist
    if rayCast.is_colliding():
        if rayCast.get_collider().has_method("on_interact"):
            rayCast.get_collider().on_interact(self)

On the other hand, a PopUp menu has a code like so

func _unhandled_input(event):
if not visible:
    #if Input.is_action_just_pressed("interact"):
    # Pause game
    already_paused = get_tree().paused
    get_tree().paused = true
    # Reset the popup
    selected_menu = 0
    change_menu_color()
    # Show popup
    #player.set_process_input(false)
    popup()
else:
    if Input.is_action_just_pressed("ui_down"):
        selected_menu = (selected_menu + 1) % 3;
        change_menu_color()
    elif Input.is_action_just_pressed("ui_up"):
        if selected_menu > 0:
            selected_menu = selected_menu - 1
        else:
            selected_menu = 2
        change_menu_color()
    elif Input.is_action_just_pressed("attack"):
        match selected_menu:
            # the code of processing the input
Godot version 3.4.2
in Engine by (15 points)

1 Answer

+2 votes
Best answer

I was doing similar system very recently. You already got most of this. I created custom ControlNode scene and instanced it anew every time interaction was triggered, and it was queued free on cancelling interaction. The object who player interacted with was the one to instance this controlscene. This object also had various data, that determined what details Control node is going to have when it is instanced ( for example what items in shop ). GUI input also sent back and updated some of owners object data ( like already bought items ). There is no point in having menu instanced from the start and hidden all the time, when it is only used in relatively rare situations.

by (7,445 points)
selected by

Amazing, so simple and at the same time so hard to realize. Thank you very much! You save me a lot of time.

For those who will stack into same issue.

Leaving the code I showed in the OP post conteptually unchanged, I modified the "on interact" method code in the next way

func on_interact (player):
    var scene = load("res://Context_menu.tscn")
    var menu = scene.instance()
    add_child(menu)

It does what Inces told about.

Next, you need to modifiy unhandledinput in such a way that it can interact with, say, player object to give either a gold or an exp (like in Heroes 3). All you need is to add a pointer on player object like so onready var player = get_node("/root/MainScene/Player") somewhere outside the unhandledinput and write a processing of a player choice like so (you can match it with the code in the op post)

match selected_menu:
                0:
                    # Resume game
                    if not already_paused:
                        get_tree().paused = false
                    hide()
                    queue_free()
                1:
                    player.give_gold(5)
                    if not already_paused:
                        get_tree().paused = false
                    hide()
                    queue_free()

                2:
                    player.give_xp(5)
                    if not already_paused:
                        get_tree().paused = false
                    hide()
                    queue_free()

I'm not sure that I've done it in a completely proper way because I'm still poor familiar with an engine, but it could be a start point though.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to [email protected] with your username.