How to make bouncing ball with constant jumping height and speed

Godot Version

<stable 4.2>

Question

<I am trying to make a projectile that bounce at constant height and speed. Right now with my code, characterbody2d will bounce but will eventually stop bouncing and will change the speed depending on the angle of it collided. I would appreciate if you could teach me how to make it bounce with constant height and speed. thanks.

extends CharacterBody2D

var speed := 150

const gravity = 300

var direction = Vector2.ZERO

func _init():
    set_as_top_level(true)

func _ready():
    velocity = speed * direction

func _physics_process(delta: float) -> void:
    
    velocity.y += gravity * delta
    
    var collision = move_and_collide(velocity * delta)
    if collision:
        velocity = velocity.bounce(collision.get_normal())

There’s a formula for that!

In 3D, where the positive Y axis points upwards, it’s this:

var jump_height = 500 # or whatever height you want
var jump_force = sqrt(-2 * gravity * jump_height)

However, in a 2D game, the positive Y axis points downwards, the gravity is a positive number, and we need our jump_force to be a negative number, so we change it to this:

var jump_height = 500 # or whatever height you want
var jump_force = -sqrt(2 * gravity * jump_height)

Now, to make your ball jump jump_height pixels into the air, you can just do:

velocity = Vector2(0, jump_force)

Thank you very much for your help tayacan!
This worked perfectly and now it bounces at constant height all the time.
However, now it does not move at all since x value is set to zero. how should i modify velocity.x ?

Well, how do you want it to behave? I don’t know very much about your game… If you liked how the bounce method behaved, other than the jump height, then you could do something like:

var bounce_vector = velocity.bounce(collision.get_normal())
if bounce_vector.y < 0:
  bounce_vector.y = jump_force
velocity = bounce_vector

That should make it bounce based on the normal of whatever it collides with, but if it ends up moving upwards at all, it will do so using our calculated jump_force.

well i want it to move at constant speed, cuz it sometimes move faster depending on the angle it collided.
also, id like it to move in opposite direction once hit the wall.

That doesn’t sound terribly difficult! Some functions you’ll probably want to play around with, then, are the built-in abs and sign functions. abs takes a number and makes it positive (so 5 remains 5, but -3 becomes 3). sign tells you whether a number is positive or negative (so 5 becomes 1, and -3 becomes -1, for example). You should be able to use those, along with collision.get_normal().x, to figure out which direction you need to move on the X axis.

if is_on_wall():  # Check if colliding with wall
			velocity.x = abs(speed) * sign(collision.get_normal().x)

with this, I was able to change the direction with constant speed. thanks to your advice. I am not sure this is the best way to do though… Now, I want to disable jump force when it collided with wall, so it will only bounce while on floor. so i made this code.

if is_on_floor():
			var jump_height = 100 # or whatever height you want
			var jump_force = -sqrt(2 * gravity * jump_height)
			velocity.y = jump_force

However, this somehow does not work, and it will eventually stop bouncing. is there any way to make it only bounce while is on floor?

Weird that is_on_floor isn’t working for you… You could try, again, using the normal vector of the collision point, and seeing if the y component of it is negative. If you wanna be fancy, you could also use something like collision.get_normal().angle_to(Vector2(0, -1)) to see whether your normal vector is close to vertical.

what do you mean by “You could try, again, using the normal vector of the collision point, and seeing if the y component of it is negative.”? how can i check?

With an if statement:

if collision.get_normal().y < 0:
  # do stuff
if collision.get_normal().y < 0:
			var jump_height = 100 # or whatever height you want
			var jump_force = -sqrt(2 * gravity * jump_height)
			velocity.y = jump_force

this worked perfectly and it will only bounce when it collided to floor. However, the speed is still not constant. What I mean is, it will move faster when it hit the edge or was launched at downward angle, but slower when it was launched at upward angle. I want it to have same speed no matter the angle.

i think i managed to make it work.

var normalized_velocity_x = velocity.x / abs(velocity.x)
		velocity.x = normalized_velocity_x * speed

i am not sure im doing right way. but it is working i believe.