Trying to get is_on_wall() to return true (with Jolt)

Godot Version

4.6

Question

I have this problem where the player (represented by a capsule) is standing against the wall

and when I jump and I’m in the air, is_on_wall() is not returning true

UNLESS I’m actively pressing forward against the wall.

The moment I let go of forward (and I’m still in the air), is_on_wall() returns false. I would like to detect that I’m on the wall without having me to actively press forward against it (while I’m in the air). How would I go about doing this?

Remember, the capsule is right up against the wall this entire time.

Here’s my code:
Everything is in physics_process and very little was added to the CharacterBody3D: Basic Movement script

func _physics_process(delta: float) -> void:

	# Add the gravity.
	if not is_on_floor():
		velocity += get_gravity() * delta

    # handle jump
	if Input.is_action_just_pressed("ui_accept") && currentState == State.Floor:
		velocity.y = JUMP_VELOCITY

    # handle input direction
	var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	
	if direction:
		velocity.x = direction.x * WALK_SPEED
		velocity.z = direction.z * WALK_SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, WALK_SPEED)
		velocity.z = move_toward(velocity.z, 0, WALK_SPEED)

	# handle state
	if is_on_wall_only():
		currentState = State.Wall
	elif is_on_floor():
		currentState = State.Floor
	else:
		currentState = State.Air

	state_signal.emit(currentState)
	

	move_and_slide()

Edit: So I marked one of the comments as the solution but if anyone knows how to solve this without any additional runtime checks (raycast, shapecast, etc), please let us know, because ideally, I would like is_on_wall_only() to just work (using Jolt) without having to do any extra stuff. Regarding the raycast suggestion, it’s definitely less expensive to detect the collision (compared to shapecast) and is perfect if you’re only concerned about one direction (i.e. in front of you, etc), but for me, shapecast fits my needs because it can detect the wall from all directions with the tradeoff that it’s more expensive to compute compared to a ray.

I’ve been having this same problem, and I’m thinking the solution might be a RayCast.

Yeah, a raycast would be the best solution. The is_on_wall and is_on_floor methods require the character to actively moving into the wall / floor to work properly.

1 Like

When trying to replicate this, it seemed to only be an issue when using Jolt, but not with GodotPhysics3D.

1 Like

You’re right. I just tried switching to GodotPhysics3D and it works like 99% of the time. I do wish this works with Jolt though (without having to do any additional runtime checks), since that physics system is generally faster and more reliable.

Hey,
I’ve managed to replicate your issue and solve this, perhaps in a more appropriate way.
I’ve used a shapecast in the shape of a cilinder, placed in inside the character bean, increased it’s radius and decreased the height (optional) and created a custom is_on_wall_only() -> bool method to detect this using the shapecast.


I made an @export for my OnWallShapecast (but that’s not necessary):

@export var wall_shapecast: ShapeCast3D;

And made custom methods for detecting whether the player is on the wall (not overrides to not make the engine confused):

func custom_is_on_wall() -> bool:
	return wall_shapecast.is_colliding();


func custom_is_on_wall_only() -> bool:
	return !is_on_floor() and !is_on_ceiling() and custom_is_on_wall()

and then in your logic, I just replaced the call, and now it works:

[...]

	# handle state
	if custom_is_on_wall_only():
		currentState = State.Wall
	elif is_on_floor():
		currentState = State.Floor
	else:
		currentState = State.Air

	state_signal.emit(currentState)
	
	move_and_slide()

Here’s a video of how it works:

With this you can achiveve the same effect with Jolt and it is, in my opinion, a much cleaner way to do this, even giving you control over how far away from the wall you detect your player as being on a wall.

And here is the project zip if you wanna have a look for yourself:

2 Likes

Thank you very much, soki! The effort you put into this is amazing (coming up with a solution, explaining it, making a youtube video, and uploading the source)! I’m going to give you lots of headpats, ok? uwu

I actually tried something very similar to your approach, except that instead of using a Shapecast, I used an Area3D (I didn’t know about Shapecasts until now. I’m still a beginner). The results were acceptable but had its caveats. I think using Shapecasts is much better!

1 Like

Awww >w< thanks :3

Did the Shapecast work for you? I’m not sure what the differences might between using an Area3D, CollisionShape3D and a ShapeCast3D might be, but I’m guessing that ShapeCast3D would be most optimised for this use case, it was just the first thing that came to my mind. Btw if you tried my solution and it worked, it would be nice if you marked that reply as a solution for anyone else having the same issue :3

I noticed there are a lot of physics settings under Project Settings. I wonder if changing any of those will fix this problem without having to do any additional runtime checks (raycast/shapecast/area2d, etc). I don’t know what most of those settings do (even with the tooltip explanations) but I will try tweaking them to see if they have any effect when I have time.

1 Like

If I had to guess, I’d say that even if one of them fixed it, it might mess up something else with the physics, because it wouldn’t affect just your is_on_wall_only() call, but all the physics in the entire project, for some basic usages it might be enough, but using a shapecast or just some other custom solution will give you better control over how it is detected and even allow you to add some margin around your player where it’ll still detect as on-wall, which in most cases is actually the desired behaviour.

1 Like

I haven’t tried it with my project yet but I did download your source and that worked. I’ll have to make changes to my project when I get the chance later and see.

1 Like

Sorry for the late reply (I was extremely busy yesterday). Anyway, I made the changes to my project and it worked, so I’m going to mark yours as the solution.

headpat you some more ^^

1 Like