Calling 'move_and_slide()' outside of CharacterBody2D?

Godot Version

4.4.stable

Question

I am learning how to use Godot coming from both Unity and Unreal Engine. I have been following a Zenva course, and the most basic thing it teaches is how to move a 2D scene using the directional keys. I wanted to change the hierarchical structure of my scene for better organization, but this has resulted in the movement not working at all.

Having the CharacterBody2D node as the root for my ‘Player’ scene (this node containing all the code) makes the implementation work as inteded, but I wanted to have a base scene root (Node2D) with the main script for said scene, and then the rest of the necessary nodes (Sprite2D, CharacterBody2D) as children with their respective scripts.

I have been trying different approaches for this structure, but none seem to work. I have tried calling ‘move_and_slide()’ from the root (_characterBody2D.move_and_slide()), or calling specific functions that call ‘move_and_slide()’ from within the CharacterBody2D node, but while the code seems to work when debugging it (breakpoints inside the CharacterBody2D script are reached and execution paused) the ‘Player’ scene doesn’t move.

What would be the correct approach to do this? Do I always need to have the CharacterBody2D node as the scene root if I want it to ‘move_and_slide()’ or ‘move_and_collide()’?

Code

When the implementation worked I had all the code on a single script inside the CharacterBody2D node. When I rearranged the hierarchy, I tried to add one more script since the previous one didn’t work anymore (breakpoints were reached and execution paused, but the ‘Player’ scene didn’t move), and slightly modified the existing one. The updated hierarchy and code are below:

· Root (Node2D) code:

extends Node2D

@onready var _characterBody2D: PlayerMovement = $Player

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _physics_process(delta: float) -> void:
	_characterBody2D.call_move_and_slide()

· Child (CharacterBody2D) code:

class_name PlayerMovement

extends CharacterBody2D

@export var _speed: float = 200

func _process(delta: float) -> void:
	velocity = Vector2.ZERO
	
	GetInput()
	
	velocity *= _speed

func GetInput():
	var inputDir = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	velocity = inputDir * _speed
	velocity = velocity.normalized()

func call_move_and_slide() -> void:
	move_and_slide()

The move functions do update the position of the node itself, which means you need to make the visual a child of this node.
Seeing your tree screenshot, it seems your code is working just fine, but the Sprite2D is just not following because it’s not a child of the moving node. It would explain why your breakpoints are reached while the code not seeming to work.

Oh, okay!
I turned the Sprite2D node into a child of the CharacterBody2D and it does indeed move! Thank you very much!
This, though, means that the actual position of the whole ‘Player’ scene is not being updated, right? Only the CharacterBody2D?
If I manually updated the position of the root (Node2D) to match the one of the CharacterBody2D (say in the ‘_process()’ function), would that be a good solution, or would it be too expensive in the long run?

It sounds a bit like you’re fighting the Godot way. The physics nodes (CharacterBody2D, RigidBody2D, etc) are meant to be the root of physicalised scenes. If you’re doing it differently just because you’re not used to it from Unity and Unreal, you will having some pains using and enjoying Godot.

I mean, as @ratrogue said, you don’t want to do that, not because it would be expensive or something, just because it does not fit how Godot tends to work. The CharacterBody2D node moves and bring with it every children, including the visual.
I don’t know about UE, but even in Unity, I don’t see you’d have a GameObject with a Rigidbody2D and a Collider2D attached, and a GameObject on the side with a SpriteRenderer, instead of just using simple parenting to make it work.

I’ve been using Unity for 7 years and I’m now focusing on Godot, and I understand that one might tend to work in a “Unity friendly way”, but Godot has a different approach and you don’t want to go against it.