# How to keep extra impulse applied to characterbody3d?

4.2.2.stable

### Question

Hello! So I added a “rocket jump” mechanic of sorts to my game, which adds a vector to an extra velocity variable, and then the velocity adds the extra velocity to itself. (so basically, extraVelocity += Vector3(blah,blah,blah), then velocity += extraVelocity)

What I want to happen is, when rocket jumping, you keep the impulse from the rocket jump but are also able to change directions and whatnot, but what actually happens is if you rocket jump, the vertical impulse gets applied, but all horizontal velocity still depends on whether you are holding a directional key or not, and has 0 horizontal velocity applied from the explosion.

My current code to handle the velocity and extra velocity looks like this (in _physics_process)

``````if direction:
velocity.x = direction.x * speed
velocity.z = direction.z * speed
else:
velocity.x = move_toward(velocity.x, 0, speed)
velocity.z = move_toward(velocity.z, 0, speed)

velocity += extraVelocity

extraVelocity.x = move_toward(extraVelocity.x, 0, speed)
extraVelocity.y = move_toward(extraVelocity.y, 0, speed)
extraVelocity.z = move_toward(extraVelocity.z, 0, speed)
``````

and the portion of the explosion code that applies impulse like this

``````func _physics_process(delta):
for o in get_overlapping_bodies():
if o is Player:
var force = (o.global_position - global_position).normalized()
force *= 3.25
``````

Any help is appreciated. Thanks!

Based on the code I’m looking at, it should work.

Are you sure that you’re actually adding any horizontal `extraVelocity`?
Where is the explosion object located relative to the player?
Also, it would be nice to see the entire code for your movement script. Context is important.

I’ll check if it’s adding any horizontal extraVelocity real quick!

The explosion object is located wherever the rocket projectile fired from the rocket launcher hits.

Player script:

``````@export var DEFAULT_SPEED = 5.0
@export var RUNNING_SPEED = 7.5
@export var JUMP_VELOCITY = 4.5

var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")

var speedMultiplier := 1.0
var jumpMultiplier := 1.0
var gravityMultiplier := 1.0
var extraVelocity := Vector3.ZERO

var running := false

@onready var hud: CanvasLayer = \$HUD

func _input(event):
if event is InputEventMouseMotion:
camera.rotate_x(-event.relative.y * Options.sensitivity)

func _physics_process(delta):
gravity *= gravityMultiplier
speed *= speedMultiplier

if not is_on_floor():
velocity.y -= gravity * delta

if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = JUMP_VELOCITY * jumpMultiplier

var input_dir := Input.get_vector("left", "right", "forward", "backward")
var direction := (head.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()

running = Input.is_action_pressed("run")
if !running: speed = DEFAULT_SPEED
else: speed = RUNNING_SPEED

if direction:
velocity.x = direction.x * speed
velocity.z = direction.z * speed
else:
velocity.x = move_toward(velocity.x, 0, speed)
velocity.z = move_toward(velocity.z, 0, speed)

velocity += extraVelocity

extraVelocity.x = move_toward(extraVelocity.x, 0, speed)
extraVelocity.y = move_toward(extraVelocity.y, 0, speed)
extraVelocity.z = move_toward(extraVelocity.z, 0, speed)

move_and_slide()

extraVelocity += vel
``````

After checking, the explosion DOES add horizontal `extraVelocity`, but still gets stopped if you’re not holding any directional keys

1 Like

Hmm… I suspect it has something to do with these lines:

``````	extraVelocity.x = move_toward(extraVelocity.x, 0, speed)
extraVelocity.y = move_toward(extraVelocity.y, 0, speed)
extraVelocity.z = move_toward(extraVelocity.z, 0, speed)
``````

This code moves `extraVelocity` towards `(0, 0, 0)`.
Perhaps you would want to only do that if you’re grounded (i.e. `is_on_floor()`)?

### Another approach

Your velocity system is actually a little tricky.
`velocity` is a variable inherited from `CharacterBody3D` and its value is modified not only in your code but also in the call to `move_and_slide()`.

I think what you should do is modify `add_extra_velocity()` to be this instead:

``````func add_extra_velocity(vel: Vector3):
velocity += vel
``````

…and then remove the `extraVelocity` altogether.
I’ve made a similar system before where I had two “`extraVelocity`” variables; one for each movement sub-system. The more I used that system, the more I realized how much of a headache it creates - having to select and administer multiple velocities whenever I wanted to modify the player’s velocity.

I propose that you use a simpler system.

### Sidenote

``````gravity *= gravityMultiplier
speed *= speedMultiplier
``````

I reckon you shouldn’t be doing this. If you were to modify `gravityMultiplier`, your `gravity` would very quickly spiral to infinity. The same would happen to `speed` if not for the fact that you override the change through:

``````    running = Input.is_action_pressed("run")
if !running: speed = DEFAULT_SPEED
else: speed = RUNNING_SPEED
``````

I’m still analyzing your code to try and come up with an exact solution. Bear with me.

1 Like

Here are two ways I think you can fix it.

### Simple fix

Remove the following `extraVelocity` lines

``````velocity += extraVelocity  # Permanently adds extraVel every frame (not good)
# The x- and z-components are reset but the y is never.
# The y-velocity will keep increasing as long
# as extraVel is more than zero

extraVelocity.x = move_toward(extraVelocity.x, 0, speed)
extraVelocity.y = move_toward(extraVelocity.y, 0, speed)
extraVelocity.z = move_toward(extraVelocity.z, 0, speed)
``````

…and replace them with

``````velocity += Vector3(extraVelocity.x, 0f, extraVelocity.y)

# Reset the extra velocity once the ground is hit
if is_on_floor() and extraVelocity.length() > 0:
extraVelocity = Vector3.ZERO
``````

…and add this to the `add_extra_velocity`-function:

``````    # Since the y-component of extraVelocity is no longer added
# in _physics_process(), it must be added here.
velocity.y += vel.y
``````

### System Redesign

Here’s an alternate solution. This is what I usually do.

Code

I’m gonna use the variable names from your pre-existing script.

``````if direction:
# Movement (w. acceleration)
velocity.x = direction.x * speed * delta
velocity.z = direction.z * speed * delta

# The y-velocity and xz-velocity
var yVel = velocity.y
var xzVel = velocity.slide(Vector3.UP)

# The maximum velocity in the XZ-plane
# Here, the limit can be higher than "speed" if the player is not on the floor
var speedLimit = speed if is_on_floor() else xzVel.length()

# Clamp the XZ-velocity to the speed limit
velocity = xzVel.limit_length(speedLimit)
# Restore the y-velocity
velocity.y = yVel
else:
# Deceleration (for grounded and airborne)
if is_on_floor():
velocity.x = move_toward(velocity.x, 0, speed * delta)
velocity.z = move_toward(velocity.z, 0, speed * delta)
else
# Insert deceleration code for the air here.
# I suspect you want the player to keep flying even if they let go?
# In that case, just delete the else-statement
``````

Let me know if either works for you.
I always feel a little iffy with writing code like this without testing it. Might not work.

1 Like

It worked!!! Thank you so much!!!

1 Like

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