Referencing object from Parent node

Godot Version

Godot 4.3

Question

I am trying to implement a PlayerInput script in a command pattern as part of my Player Node. Player inherits from Character class, and Player has its own movement logic.

The issue I am facing:
I need to call the movement functions in my Player class from the child node. The way my PlayerInput node is written, it wants the reference to the player object, which is instantiated AFTER its children. How do I pass a reference to my child node in order to call the player methods?

Or is there a better node structure for this functionality? Including node structure below for clarity.

Player
>Sprite2D
>CollisionShape2D
>PlayerInput

Would it be better to attach the Player script to a child node on the same level?

Usually calling up is done using Signals. When calling something that is above you would emit a signal that the parent connected to. This way any where you move your child nodes would not cause problems. Read more here.

https://kidscancode.org/godot_recipes/4.x/basics/node_communication/index.html

1 Like

Thanks, I’ll take a look when I’m back at my desk. Sounds a little like concepts in React, so it might click when I see it.

I have used this same setup myself.
You can make player an export in the PlayerInput script and then just drag the node into it in the editor.

`
#PlayerInput

@export var player:CharacterBody2D`

extends PlayerInput

var Parent : PlayerClass
var iseeyou : bool = false

func get_parent(Node : PlayerClass) -> void:
    Parent = Node
    iseeyou = true
    return

func hey_parent(hey : String) -> String:
    if iseeyou:
        return Parent.bother(hey)
    else:
        return "nobodies home"
extends PlayerClass

var Input : PlayerInput = $PlayerInput

func _ready() -> void:
    Input.get_parent(self)

func bother(huh : String) -> void:
    if(huh == "hey"):
        return "what?"
    else:
        return "go away"

youre probably better off with signals though

I didn’t like the idea of a child node controlling a parent node, it seems fundamentally incorrect from my understanding of node structures. I found another resource that really helped.

Separate control input from movement.

This video does it without calling it the Command Pattern. My error is resolved, and I can verify that my class references are passed properly. Here’s some basic code for future generations.

My node structure looks like this:

PlayerInput
 >Player
   >Sprite2D
   >CollisionShape2D

Player.cs

public partial class Player : Character
{
	[Export]
	public float Speed {get; set;} = 300.0f;
	[Export]
	public float JumpVelocity = -400.0f;


    public override void _Ready()
    {
        base._Ready();
		GD.Print($"player obj ref {this}");
    }
    public override void _PhysicsProcess(double delta)
	{
		Vector2 velocity = Velocity;

		// Add the gravity.
		if (!IsOnFloor())
		{
			velocity += GetGravity() * (float)delta;
		}
		Velocity = velocity;
		MoveAndSlide();
	}

	new public void Jump()
	{
		GD.Print("Inside Player.Jump()");
		if (IsOnFloor())
		{
			Vector2 velocity = Velocity;
			velocity.Y += JumpVelocity;
			Velocity = velocity;
		}

	}

	new public void Run()
	{
		GD.Print("Inside Player.Run()");
		float horizontalInput = Input.GetAxis("moveLeft", "moveRight");
		Vector2 _velocity = Velocity;

		if(horizontalInput != 0)
		{
			_velocity.X = horizontalInput * _speed;
		}
		else 
		{
			_velocity.X = 0;
		}
		Velocity = _velocity;
	}
}

PlayerInput.cs

public partial class PlayerInput : Node
{
	public Player _player;
	public PlayerController _controller;


	// Called when the node enters the scene tree for the first time.
	public override void _Ready()
	{
		_player = GetNode<Player>("Player");
		GD.Print($"_player is {_player}");
		ICommand runCommand = new RunCommand(_player);
		ICommand jumpCommand = new JumpCommand(_player);
		System.Collections.Generic.Dictionary<string, ICommand> commandDict = new()
        {
            {"run", runCommand},
			{"jump", jumpCommand}
		};
		_controller = new PlayerController(commandDict);
	}

	// Called every frame. 'delta' is the elapsed time since the previous frame.
	public override void _Process(double delta)
	{
		if (Input.GetAxis("moveLeft", "moveRight") != 0)
		{
			_controller.Run();
		}
		if (Input.IsActionJustReleased("moveLeft") || Input.IsActionJustReleased("moveRight"))
		{
			_controller.Run();
		}
		
		if (Input.IsActionJustPressed("jump"))
		{
			_controller.Jump();
		}
	}
}

There’s a couple more parts to make this work as I have it written (and as I understand proper Command Pattern structure)
A basic interface called ICommand.cs

public interface ICommand
{
    void Execute();
}

A set of commands that use the interface called PlayerCommands.cs

class JumpCommand : ICommand
{
    Player _player;
    public JumpCommand(Player player)
    {
        _player = player;
    }
    public void Execute()
    {
        _player.Jump();
    }
}

class RunCommand : ICommand
{
    Player _player;
    public RunCommand(Player player)
    {
        _player = player;
    }
    public void Execute()
    {
        _player.Run();
    }
}

and an invoker called PlayerController.cs

public class PlayerController
{
    private Dictionary<string, ICommand> _commandDict;
    public PlayerController(Dictionary<string, ICommand> commandDict)
    {
        _commandDict = commandDict;
    }

    public void Run()
    {
        _commandDict["run"].Execute();
    }

    public void Jump()
    {
        _commandDict["jump"].Execute();
    }
}

I need to go through and fine-tune this code, but this all works together. I hope it helps someone out there.

Thanks for this tip! I am (pretty obviously) new to Godot and I did not know I could do that. I’m sure it will come in handy.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.