Should I use CharacterBody2D

Godot Version

v4.2.2.stable.mono.official [15073afe3]

Question

I am currently working on making a 2D game where the player is a ball (sort of like the Red Ball games). But, after watching a few tutorials on Godot, I’m left really confused on which physics object I should use for my player. I want to give the player the ability to slide up and down slopes, but CharacterBody2D, the method that is recommended in the tutorials, doesn’t really work for that. While the player can slide down slopes, it is completely unable to slide up slopes.

So, I decided to replace it with a RigidBody2D. I scripted the player so that pressing the left and right arrow keys change the snowball’s velocity, which gives a much better sliding effect than the CharacterBody2D. I am struggling a bit on detecting if the player is on the ground or not, which is one drawback of the RigidBody2D. So, would it be worth it to use RigidBody2D as my player? It seems to feel much more natural than CharacterBody2D, but it might cause some unforeseen drawbacks and challenges as I continue developing my game.

If you want the player to be very physics based, rigid body could be the best option. You may have to retrieve collision information from overriding func _integrate_forces

1 Like

Yeah, I already use _integrate_forces for the player controls. But how would I check if the player is touching the ground? It rotates as it moves (because its a ball), so if I give the player raycast2d as a child node, the raycast just rotates with the player. Is there any other way I can detect if the player is touching the ground?

You may want to use a different Node as root node for the player and use the RigidBody as a child so that rotating the Ball does not rotate the whole player. This way, up/down directions stay the same, and you can use a raycast as a sibling of the RigidBody

2 Likes

Yes as substain said, that’s the best way to go about it using a raycast! There’s also contact_monitor if you want to try that as well, but I suspect a raycast may be a cleaner way to do it.

1 Like

Wouldn’t it just be easier to always reset the raycast’s rotation to 0 in the _integrate_forces function call?

No you can get collision data from _integrate_forces like so

func _integrate_forces(state: PhysicsDirectBodyState2D) -> void:
	var i: int = 0
	while i < state.get_contact_count():
		var collided_obejct := state.get_contact_collider_object(i)
		var collision_position := state.get_contact_local_position(i)
		var collision_normal := state.get_contact_local_normal(i)
		
		if collision_normal.y > 0.7:
			pass # probably on floor, tweak the number above
		i += 1

You would have to un-rotate the raycast every frame, it’s just much more costly to do than the already present collision data.

1 Like

I tried tying that into my code, but it doesn’t seem to work.
The player doesn’t respond to the jump button.

It looks like this:

# use contact triggers to detect if the player is grounded or not
func isGrounded(state : PhysicsDirectBodyState2D):
	var i: int = 0
	while i < state.get_contact_count():
		var collided_object := state.get_contact_collider_object(i)
		var collision_position := state.get_contact_local_position(i)
		var collision_normal := state.get_contact_local_normal(i)
		
		if collision_normal.y > 0.7:
			return(true)
		i += 1
	
	return(false)

And then in _integrate_forces:

	# if the jump key is pressed, make the player jump!
	if Input.is_action_pressed("player_jump") and isGrounded(state):
		state.apply_central_impulse(Vector2(0, jump_height))

Right it’s 2D, negative is up, so collision_normal.y < -0.7 I always get it confused!

1 Like

Okay, thanks for the clarification!
Also, I managed to tweak the maximum collision_normal.y value to be 0.48, because it allows the player to jump on flat ground and slopes, while not climbing up walls.

1 Like

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