Godot Version
v4.5.1.stable.mono.official [f62fdbde1]
Question
I’ve created a trigger action system for my game, this basically means that I can create actions without code and have them execute. This is the base:
[GlobalClass]
public abstract partial class TriggerAction : Resource
{
public abstract void Execute(Node caller);
}
And as an example, this is my “show dialogue” function:
[GlobalClass]
public partial class ShowDialogueAction : TriggerAction
{
[Export] private DialoguePart _dialogueToShow;
public override void Execute(Node caller)
{
if (_dialogueToShow == null)
{
GD.PushWarning("No dialogue to show for " + nameof(ShowDialogueAction));
return;
}
GameController.Instance.Dialogues.ShowDialogue(_dialogueToShow);
}
}
What I want to achieve is something very similar to what the AnimationPlayer’s Call Method Track can do, where I can pick a Node using NodePath and then give it a method name, then call it. However, I ran into trouble when trying to actually pick out a node. The Node picker shows up correctly, but no matter what Node I pick, in the inspector it’s simply . if it’s the current Root node, or if it’s not, it doesn’t include the root node’s name, so I can’t be sure where the nodes are.
As an example, if I have a root node called MainMap, and under it I have MainMap/Sprite2D, then if I use NodePath and select Sprite2D, it’ll only show up as Sprite2D but I obviously have no way of knowing this specific Sprite2D is under the node MainMap.
You don’t get an absolute NodePath in the inspector, it stores a relative NodePath by design.
To resolve it to an absolute path, call this at runtime:
public override void Execute(Node caller)
{
var tree = caller.GetTree();
var root = tree.Root;
var target = caller.GetNode(_pickedNodePath);
if (target != null)
{
string absolutePath = root.GetPathTo(target);
GD.Print($"Absolute path: {absolutePath}");
}
}
This is basically what the AnimationPlayer call track does internally - inspector shows “.” but it resolves via GetPathTo() when used.
This has the same problem unfortunately. Because the caller in almost every case is the class that actually handles executing these Actions (such as a ProximityTrigger or the DialogueBox), and so it’s almost never guaranteed that the NodePath will be a child of the caller.
So if I add an action to a ProximityTrigger on my map, then I pick the root node, the target node path will simply say “root” which is not useful.
Store the path as a string.
It is already a string. The issue is that NodePath doesn’t know where that node is related to the root node in the tree. That’s why the previous solution doesn’t work.
Your’re confusing things.
Firstly, NodePath is not String. Even though it stores the path in a string, they are a different type and they’ll behave differently in the editor (NodePaths gets a picker etc). Declaring it as a String will let you type the path, so you can type whatever you want.
Secondly, node path picking in the editor works relative to currently edited scene. It has no way of knowing your global scene tree structure that’ll exist at runtime, or anything that’ll appear “above” the scene when it gets instantiated. It cannot have the concept of root other than the currently edited scene root.
So if you want to pick relative to some other scene’s root, create the resource object in the inspector while that other scene is being edited. If your resource is referenced in a node in some other scene, save the resource object to disk and reload it while the scene you want to pick from is active in the editor, and then pick.