Springs in a 2d platformer - is there a better way to handle high-speed collisions?

Godot Version

4.4.1.stable.official[49a5bc7b6]

Question

So I’m getting back into gamedev after a bit and I’m working on a 2d platformer. The game has an emphasis on High-Speed vertical movement which is a bit tricky already but I’m here to specifically focus on the spring system.

Throughout the game there are springs that react to your momentum and for what that’s worth that seems to work fine. But I’ve noticed that actually getting detection to be consistent has been difficult. I’ve tried two methods in particular.

jump1

Attempt One - Area2d

I attached the collision detection to the springs themselves , making them an Area2D with CollisionShapes that were slightly larger than the sprite. Then used a check to see if a player entered the body.

func _on_body_entered(body: Node2D):
	if body is CharacterBody2D:
		if body is CharacterBody2D:
		   if(body.velocity.y < 500):
		    body.velocity.y = -500
		   else: body.velocity.y = (-body.velocity.y * .90);

As previously stated, this mostly works fine. The momentum code works enough to the point where I didn’t feel the need to include it. At low and medium speeds the spring works fine with some cosmetic issues but anything too high will cause the player to phase through. This is partially mitigated by the momentum threshold I’ve added and making the collider much bigger than it needs to be but whether the player just phases through the springs our bounces in a way the player wasn’t expecting it doesn’t feel good when that just randomly happens - even cosmetically seeing the player bow lower than they should before bouncing off feels wrong- though these are problems beyond my query at the moment.

Attempt Two - Raycasting

I saw some people on here talking about Raycasting to solve issues like this. That’s what I tried next

func _physics_process(delta: float) -> void:
	
	if not is_on_floor():
        //Jumping System 
            if springraycast.is_colliding(): 
				if(velocity.y < 500.0):
					velocity.y = -500.0
				else:
					velocity.y = -velocity.y  * .9


I also went ahead and capped your velocity too. Probably should’ve done that a long time ago but after double checking it wouldn’t have mattered much with anything from Attempt 1.
boimg

This works way better than the previous attempt (especially when i enabled “Hit from Inside”) The player seems to be bouncing on top of the spring (before animations are added) instead of phasing through it. And there isn’t as much erratic-ness to the physics

There are a handful of glaring issues though.

  1. A bug with how I handle variable jump height if you release the jump button while in midair all your momentum cuts , if you do it too late it kills your momentum. Fixable, might also be a cool mechanic.
  2. It’s still a bit finnecky. Sometimes collisions blank or the momentum detection breaks.
    2.5: I’m unsure how this would work if i wanted other objects to be bouncy as well, outside of setting them all to the same layer. But also I wanna have some objects be bouncy only if you perform certain actions so. Likely a different system?
  3. Imprecision - Or more accurately over precision. The raycast is a bit too laser-focused and it can lead to some dead-zones. It also seems to be biased towards the right for whatever reason.
    bouncy

The main thing I’m wondering is if there are better ways to handle this in ways that are both percise and broadly applicable. My game heavily relies on momentum so I’m not really sure how to handle this. I saw some ideas thrown around with an Area 2D towards the players feet and having that dedicated towards spring collision but i feel like that’d just run into the same issues i had before.

Any help is appreciated. Thank you <3.

RayCast2D is just 1 pixel wide, while your character is not. So if the center of your character doesn’t touch the spring, it won’t activate.
What you’d rather want is a ShapeCast2D, which allows you to check a whole shape of your character, which will solve your problem. You should achieve the same effect with Area2D which you mentioned already:

The issue you ran before with character being sank into the spring is because the detection only works once the areas/bodies overlap. Meaning your character has to sink into the spring. If that’s not what you want, you could indeed create another Area2D and offset it a few pixels below the feet of your character - then it will detect it earlier.
You can also snap the character’s vertical position to the top of the spring once it detects the spring underneath. This way it should look like it always lands on top.

In general, I don’t see any glaring issues with any of the solutions you mentioned, seems solid work, you just need a few tweakings here and there.