Godot Version
4.4.1
Situation
Wanted functionality in my game for vaulting over stuff, but there weren’t any good tutorials on how to do it so I coded it myself.
It’s working okay so far, but the problem is that the vaulting speed doesn’t match up with the player’s speed at high velocities since I’m using tweening to move the player. If the player is moving slowly the tween speed matches up, but if we’re moving fast it feels a bit off since it effectively slows the player down for the duration of the vault.
I’m using the GoldGdt character controller as my base.
Question
Is there a better way to handle this?
Code
class_name Vault extends Node3D
@export_category("Components")
@export var raycastabove : RayCast3D # eye level raycast
@export var raycastbelow : RayCast3D # mid level raycast
@export var raycastshort : RayCast3D # feet level raycast
@export var raycastsurface : RayCast3D # raycast pointing directly down to get the floor we're vaulting onto
@export var body : GoldGdt_Body # character body
@export var camera_anim : AnimationPlayer
@export var thinobjecttarget : Node3D
@export var vaultsound : AudioStreamPlayer
var vel
var targetpoint : Vector3
var velocitystored = false
var unvaultable : bool
var vaulting : bool
func _physics_process(_delta: float) -> void:
# force update the raycasts so we don't get "double vaulting" - even after the position tween below,
# the raycasts were saying that we were still in front of the object, forcing another vault
# even though we were already on top of or past the object
for ray in [raycastabove, raycastbelow, raycastshort]:
ray.force_raycast_update()
if !body.is_on_floor() and (raycastbelow.is_colliding() or raycastshort.is_colliding()) and !raycastabove.is_colliding():
# if either of the lower raycasts hit a node that is in the group "unvaultable", stop
# (this is a fix for surf ramps and other sloped surfaces)
for ray in [raycastbelow, raycastshort]:
if ray.is_colliding() and ray.get_collider().is_in_group("unvaultable"):
unvaultable = true
if !unvaultable:
vaulting = true
vaultsound.play()
var tween = create_tween()
# we need to store the player velocity so we can give it back to them after the vault
if !velocitystored: # only store the velocity once per vault
vel = body.velocity
velocitystored = true
# if we do find floor under the point we're testing, vault up to the floor...
if raycastsurface.is_colliding():
targetpoint = raycastsurface.get_collision_point()
# ...otherwise vault through to the other side of the short object we're jumping towards
else:
targetpoint = thinobjecttarget.global_position
# the actual vaulting code itself
body.velocity = Vector3.ZERO # stop the player on a dime
body.collision_hull.disabled = true # disable the collision hull so we noclip through the thing
# the raycast just gets the point on the floor and the character body would move to that point,
# so we add a little offset to the Y position of the target point so the body doesn't
# move halfway into the thing we're vaulting on
tween.tween_property(body, "global_position", targetpoint + Vector3(0,1,0), 0.2)
# wait for the tween to finish, then call on_tween_finished
tween.connect("finished", on_tween_finished)
# can't forget about the camera movement (it just tilts the camera a little.
# could've probably used a tween here instead)
camera_anim.play("vault")
# clear the unvaultable flag when we hit the floor, so we can vault stuff again
# if we previously hit a node that was unvaultable
if body.is_on_floor():
unvaultable = false
# give the player their velocity back after the vault but clear vertical velocity.
# this runs after we've vaulted
func on_tween_finished():
body.velocity = vel
body.velocity.y = 0
body.falldamageflag = false
body.collision_hull.disabled = false
velocitystored = false
vaulting = false