I need help to create a interaction system in my game

Godot Version

4.3

Question

Hello, anyone can help me? i’m making a interactions function, i’ve crated a interaction area and a interaction manager, but i can interact from anywhere on the map and if i put two objects to interact the game crashes every time

My codes is down bellow:
Interaction_area.gd:
extends Node2D

@onready var player = get_tree().get_first_node_in_group(“player”)
@export var max_interaction_distance: float = 100.0 # Distância máxima permitida para interação

var active_areas =
var can_interact = true

func register_area(area: InteractionArea):
active_areas.push_back(area)

func unregister_area(area: InteractionArea):
var index = active_areas.find(area)
if index != -1:
active_areas.remove_at(index)

func _process(delta: float) → void:
if active_areas.size() > 0 && can_interact:
active_areas.sort_custom(_sort_by_distance_to_player)
print(“pode interagir”)
else:
print(“não pode”)

func _sort_by_distance_to_player(area1, area2):
var area1_to_player = player.global_position.distance_to(area1.global_position)
var area2_to_player = player.global_position.distance_to(area2.global_position)
return area1_to_player < area2_to_player

func _input(event):
if event.is_action_pressed(“ui_interact”) && can_interact:
if active_areas.size() > 0:
can_interact = false
await active_areas[0].interact.call()
can_interact = true

interaction_manager.gd:
extends Area2D
class_name InteractionArea

@export var action_name: String = “interact”

var interact: Callable = func():
pass

func _on_body_entered(body):
InteractionManager.register_area(self)

func _on_body_exited(body):
InteractionManager.unregister_area(self)

So, if I understand. You want to be able to interact while the player is in an area. You dont want interactions to be able to happen from anywhere. But you want interactions to be possible on multiple object?

extend Node2D
#array of active areas
var interactables : Array[Area2D]
var can_interact : bool = true
#connecrs all child areas signals to this controller parent
func _ready():
     for area in get_children():
          area.is_entered.connect(is_entered)
#handles input for initiating interaction
func _input():
     if Input.is_action_pressed("interact") and can_interact:
          if interactables.is_empty()
               return
          can_interact = false
         #if needed add sort func
         else:
               can_interact = interactables[0].interact()
#controls registering and unregistering areas
func is_entered(area:Area2D, can_interact:bool):
     if can_interact:
          interactables.append(area)
     else:
          interactables.erase(area)
extend Area2D
#lets parent know if is an active area
signal is_entered(area:Area2D, entered:bool)
#if player enters activates area
func _body_entered(body):
     if body.is_in_group("player")
          emit_signal("is_entered", self, true)
#if player exits deactivates area
func _body_exited(body):
     if body.is_in_group("player")
          emit_signal("is_entered", self, false)
#do something here
func interact() -> bool:
     return true
     #the return is for after the interactions

If what I described isn’t what you wanted the I can tweak it if you give me more clarity.

I noticed you had a var max_interaction_distance but I don’t see that used in your code.

Also, I noticed you had a distance sort for your areas. Are you only wanting to interact with the closest area? If that is the case you could add something like this.

#in reality if this is the case you don't need an array, 
#unless you want to keep track of areas you are in for future interactions
func is_entered(area:Area2D, can_interact:bool):
     if can_interact:
          if interactables.is_empty:
               interactables.append(area)
          else:
               if interactables[0].distance_to("player") < #the new areas distance
                    return
               else: 
                     interactables[0] = area
     else:
          interactables.erase(area)

There are a lot of possibilities depending on what you want to do, if you need to always interact with the first object you can add the sorting func back under input, whenever you press input rather than iterating over the array it sorts it and just does the first.

Also, why did you use an await call in your code? If you can do the same thing with another method it is good to avoid await. At least in my opinion, it has also allowed me to adapt different methods of avoiding await, like using return.