I need a bit of helping with this bit of coding, super newbie here

Godot Version

Godot 4.6 Stable

Question

I'm working on a survival horror game, right now mostly doing the movement system for the playable character.

I have the basic stuff already in a state machine, iddle, walk, aim, and basic interactions.

What I have right now with the interaction system is a set of Area3D nodes with a collision area that triggers the player ability to interact with an object, this is so you get the classic thing of inspecting the world (Think SH3 It’s Bread meme I guess).

It’s in this way, a generic Interactable Area3D node that is instanced into a 3D object and has a collision Shape, with this code:

extends Area3D

@export var interact_name: String = “”
@export var is_interactable: bool = true

var interact: Callable = func():
   pass

The interact_name being what I write the text in.

That Area3D is then instanced into an object in the world with this code:

extends CSGBox3D

@onready var interactable: Area3D = $Interactable

func _ready() → void:
   interactable.interact = _on_interact

func _on_interact():

   print("this is a box")

All of it then works within a code inside a node in the player controller, said controller formatted this way:

CharacterBody3D -Lilith Player Controller
-CollisionShape3D
-MeshRoot (Just the model and animation tree)
-Camera (Camera System and Code)
-StateMachine (A normal node with the state machine script with a bunch of nodes with script for each movement state like fall, idle, walk, run, etc).
-Interaction Component (It has an Area3D for the interaction range and the script to trigger it and label node to work the inspecting action).

The Interaction component script for inspecting things is:

extends Node3D

@onready var interact_label: Label = $“Interact Label”

var current_interactions := 

var can_interact := true

func _input(event: InputEvent) → void:
   if event.is_action_pressed(“interact”) and can_interact:
      if current_interactions:
         can_interact = false
         Interact_label_pause()
         can_interact = true
      else:
         interact_label.hide()
		
else:
	pass


func Interact_label_pause():
   if not interact_label.visible:
      interact_label.text = current_interactions[0].interact_name
      interact_label.show()
      await current_interactions[0].interact.call()
      get_tree().paused = true
   else:
      interact_label.hide()
      get_tree().paused = false

func _sort_by_nearest(area1, area2):
   var area1_dist = global_position.distance_to(area1.global_position)
   var area2_dist = global_position.distance_to(area2.global_position)
   return area1_dist < area2_dist

func _on_interaction_range_area_entered(area: Area3D) → void:
   current_interactions.push_back(area)

func _on_interaction_range_area_exited(area: Area3D) → void:
   current_interactions.erase(area)

What I want to do is now add to that Interaction Component so that there are other types of interactions, in this case, the character being able to jump down platforms or climp up.

I tried to recreate the inspection system by creating a new instance called Movement Interaction with this code:

extends Area3D

@export var power_vector : Vector3
@export var is_interactable: bool = true

var interact_movement: Callable = func():
   pass

And I have this invisible wall with a trigger similar to the 3D objects you inspect:

extends StaticBody3D

@onready var interactable: Area3D =$“Movement Interaction”

func _ready() → void:
   interactable.interact_movement = _on_interact

func _on_interact():

   print("You will jump down")

The print is mostly to know that the script is actually triggering.

Now I’ve changed the main Interaction compenent script to work with everything… but I have no clue what to do now, I don’t know what I should make the function be so that it calls the movement interaction.

extends Node3D

@onready var interact_label: Label = $“Interact Label”

var current_interactions := 

var can_interact := true
var can_move := true

func _input(event: InputEvent) → void:
   if event.is_action_pressed(“interact”) and can_interact:
      if current_interactions:
          can_interact = false
          can_move = false
          Interact_label_pause()
          can_interact = true
          can_move = true
      else:
          interact_label.hide()

   if event.is_action_pressed("interact") and can_move:
	  if current_interactions:
		  can_move = false
		  can_interact = false
		  Interact_Movement()
		  can_interact = true
		  can_move = true
		
		
   else:
	  pass

func Interact_Movement():
   if
   pass

func Interact_label_pause():
   if not interact_label.visible:
      interact_label.text = current_interactions[0].interact_name
      interact_label.show()
      await current_interactions[0].interact.call()
      get_tree().paused = true
   else:
      interact_label.hide()
      get_tree().paused = false

func _sort_by_nearest(area1, area2):
      var area1_dist = global_position.distance_to(area1.global_position)
      var area2_dist = global_position.distance_to(area2.global_position)
      return area1_dist < area2_dist

func _on_interaction_range_area_entered(area: Area3D) → void:
      current_interactions.push_back(area)

func _on_interaction_range_area_exited(area: Area3D) → void:
      current_interactions.erase(area)

I have no clue what to do with the interact movement function and how to call it to call the vector movement of the Movement Interaction node, so that you move in the vector3 that is cuztomized for each type of movement.

The idea is that eventually I will also attach the animation depending on what type of movement it is and I made the Vector3 a export var so that I can make the movement change depending on what is required. Just so I don’t have to create too many scripts.

Format/indent your code properly.
Also read and try to follow rest of the guidelines for posting questions in the help section

Done, I think.
Changed the formatting following that post, I hadn’t seen it before.

Here’s what I think I’d do if I understand your situation correctly. The following looks like it’s not going to really serve you in the long run, and likely to lead to roadblocks:

func _input(event: InputEvent) → void:
   if event.is_action_pressed(“interact”) and can_interact:
      if current_interactions:
          can_interact = false
          can_move = false
          Interact_label_pause()
          can_interact = true
          can_move = true
      else:
          interact_label.hide()

   if event.is_action_pressed("interact") and can_move:
	  if current_interactions:
		  can_move = false
		  can_interact = false
		  Interact_Movement()
		  can_interact = true
		  can_move = true

Every new kind of interaction requires a new code block, which defeats the purpose of what you’re trying to do with the modularity. And that modularity is a good instinct.

I’d make each type of Area3D that enables any kind of interaction implement two functions, can_interact( args ) where args is a generic container of whatever state information any of your Area3Ds would need to know if the interaction is valid. Now, no more can_interact and can_move being dealt with in the script. Each Area3D you create decides for itself and passes back true/fase.

Then one interact( args ) -> return_type: where args in this case is a generic enough to give any of your Area3Ds control. Maybe that’s just the character node itself, but it doesn’t have to be. But each script that extends Area3D should have access to whatever it needs to trigger and complete the interaction. The return type, if needed, passes back info about how the interaction went (or if it failed, was canceled, etc). So, let’s say the interaction actually forcably moves your character in some way… you design your character script to allow itself to be controlled and track whether it’s under “forced movement” or direct player control, and the interaction area is able to take control, toggle that tracker do the motion by manually changing the character either through the physics_system by changing the character’s physics data and still running through the character’s _physics_process or by having that toggle disable the character’s own _physics_process and having the interaction area move the character in its own (the area’s) instead.

What you’re doing works fine with unnamed script, but I’d probably have something like

Abstract, don’t instantiate directly:

@abstract class_name InteractionObject
extends Area3D

@abstract can_interact(character_status : Dictionary) -> bool
@abstract interact(character : Node3D, other_data : Dictionay) -> void

And then have each special interactible Area3D be its own class inheriting this. Again, choose the actual arguments and return type of interact() to serve your needs.

Thinking about it more, it probably will be better long-run to implement it with signals since they are overall more flexible and more extensible than direct function calls, and these Nodes that are interacting are not in a parent-child relationship in the tree. The standard paradigm of “call down, signal up” doesn’t directly tell you what to do when two nodes that are interacting are on totally different branches of the scene tree, but indirectly, it’s saying there should some node higher up that understands both of these and can help manage the interaction, and the character and all the interactables just send out signals whenever anything relevant happens and that “manager” node actually then makes the correct interaction happen (still by accessing the desired interaction code inside the specific interactable).

If you’re not yet comfortable with signals yet, though, I wouldn’t worry about that for now.

Architecturally, your model is actually very close to working nicely as is, and I think is actually fine for a first go. Don’t let the ideal be the enemy of the “hey I got something working!” – if you wait until you understand how to do it perfectly, you’ll never get anything done.