To set the scene I’m currently following and online course for a 2D platformer game. In this section I have an enemy character on a flat piece of ground. To the left there’s a hole and to the right there’s a wall. I want him to walk back and forth changing direction whenever it gets to a gap or hits a wall.
To achieve this. I have a CharacterBody2D parent node with a Sprite2D, a CollisionShape2D and a RayCast2D set up like this:
On the parent node (CharacterBody2D) I have the following basic script:
extends CharacterBody2D
@onready var sprite_2d = $Sprite2D
@onready var ray_cast_2d = $RayCast2D
@export var speed: float = 50.0
var _gravity: float = 800.0
func _physics_process(delta):
if !is_on_floor():
velocity.y += _gravity * delta
else:
if is_on_wall() or !ray_cast_2d.is_colliding():
scale.x *= -1
velocity.x = -speed if scale.x > 0 else speed
move_and_slide()
My theory here is that if the Character collides with a wall, or if the Raycast stops colliding with the ground (detects a gap), it should flip the whole CharacterBody2D. Raycast, sprite and all, and also change direction.
What happens however is that the sprite and the raycast successfully flip but the character continues in the same direction. scale.x *= -1 is only called once, however the value only sits at -1 for a single frame, then it goes back to 1.
By comparison the following code works just fine. But all i’m doing differently is assigning the direction to a variable instead of using a value which should reflect the current direction, and flipping the sprite and raycast individually.
Why doesn’t the above work?
Working code:
extends CharacterBody2D
@onready var sprite_2d = $Sprite2D
@onready var ray_cast_2d = $RayCast2D
@export var speed: float = 50.0
var _gravity: float = 800.0
var direction: int = 1
func _physics_process(delta):
if !is_on_floor():
velocity.y += _gravity * delta
else:
if is_on_wall() or !ray_cast_2d.is_colliding():
direction *= -1
sprite_2d.scale.x = -sprite_2d.scale.x
ray_cast_2d.position.x = -ray_cast_2d.position.x
velocity.x = -speed * direction
move_and_slide()
I’m not at my PC but I’ll try to take a stab at it. Open to being corrected if I’m wrong.
“Scale” is likely a property of the Sprite2D in this case. When you alter the scale of the sprite2d, it does nothing other than flip the image. Raycast is untouched.
In the 2nd example where you flip the Sprite2d’s scale and then you flip the Raycast, both have been changed.
Thanks @SpoopCats, in the 2nd example I’m referencing the sprite and racast and calling scale on those nodes though? On its own surely it should just be referencing the node that the script is attached to? In this case the CharacterBody2D?
The Sprite and Raycase do successfully flip in the top example. The issue is that once they have, scale returns to 1 after a single frame at -1 so the velocity doesn’t change.
@SpoopCats But scale.x is a global value isn’t it? So changing it directly should hold outside of the scope of the codeblock?
If I print(scale.x) on its own before move_and_slide(), for a single frame I get -1, then it goes back to 1 even though scale.x *= -1 is never called again.
If I flip a child node such as the Sprite2D the value holds. If it try to flip the Parent though it doesn’t? Starting to wonder if it just doesn’t work like that on a Characterbody2D? Or I’m misunderstanding something else?
Lol s7ewie I’m sorry I cant lie, I was on my phone in my bed waking up and you’re right about all the things you pointed out. Not my best “help” I’ve ever provided. If I can revisit your question when I’m at my PC I will. Sorry friend!
@SpoopCats No worries at all! If anything it just helps to have someone else’s eyes. I’m not sure if I’m being daft or something just doesn’t work as I expect it to.
I just found that if I add a child Node2D to the CharacterBody2D and move the Sprite2D and Raycast2D to be children of the Node2D (grandchildren of the CharacterBody2D). Then refactor the code to flip the Node2D.scale.x then everything works as expected, so maybe physics bodies just don’t work in the same way?