Help understanding 3D velocity, .bounce(), and move_and_collide

Godot Version



I’m working on a 3D brick breaker game (game uses the x and z axis for movement). I want to give the option for the player to have arkanoid like physics when hitting the paddle or standard breakout ones. I can easily let the player pick the option and change how the ball acts when hitting the paddle. However, the actual physics is where I’m having a bit of trouble. Can someone help me out?

The ball is a CharacterBody3D (floating mode) (not opposed to rigidbody, but with me wanting to potentially alter the physics CharacterBody3D seemed like the better choice). Here’s the relevant code:

func _physics_process(delta):
    var ball_speed = velocity * SPEED * delta
    var collision_object = move_and_collide(ball_speed)

    if collision_object:
        var collider = collision_object.get_collider()
        var normal = collision_object.get_normal()
        var reflect = collision_object.get_remainder().bounce(collision_object.get_normal())

        if collider.is_in_group("BRICKS"):
            collider.apply_central_impulse(-normal * push_force)

            velocity = velocity.bounce(normal)
        elif collider.is_in_group("PADDLES"):
            var arkanoid : Vector3 = paddle_collision(collider)
            velocity = arkanoid * (SPEED * 1.5) * delta

            velocity = velocity.bounce(normal)

## Paddle Collision
func paddle_collision(collider) -> Vector3:
    var ball_center = position

    var direction : Vector3
    direction.x = (ball_center.x - collider.position.x)
    direction.z = (ball_center.z - collider.position.z)
    direction.y = 1.5 // only using X and Z planes, Y will stay constant)

    return direction

I’ll clean this up as I go to make it clearer that BRICKS and other objects use the same collision code. The code for the standard reflection is straight from Godot docs.

I believe this will give me the reflection like I expect for the walls and bricks.

Part of what I’m not understanding is why I do velocity.bounce(normal) then call move_and_collide again. Why do I want to call bounce then move and collide? Can someone tell me a bit more about what bounce is calculating? I couldn’t really find much in the docs about what it actually does underneath.

Secondly, for when a collision with a paddle happens. The returned direction Vector3 is giving me pretty close to the angle I want (at least I think it is). But the ball slows down a lot. I’m a bit unsure of how I can get it to speed back up. I tried scaling the vector with SPEED and even multiplying SPEED by 1.5. I’ve tried a similar approach of adding the velocity = velocity.bounce(nromal) and then move_and_collide(arkanoid) but the angles don’t end up being right. Any tips or guidance here would be appreciated!

I do have vector math, trig, and linear algebra on my list to learn more about, I’m just not as sure within those where the answer to this would be.

Currently using Jolt as having the ball roll around with standard physics seemed to give me pretty rough FPS even at a very basic prototype. I’ll also take any tips there if anyone has any thoughts on improving the performance with Godot base physics if possible.

Imagine that you’ve asked to move the ball 1 meter (that is of course too far to move on a single frame, but it’s a simple number to work with).

However, 0.6 meters along its path, a collision is detected, which stops the ball. Now it has only moved just over half a meter! That means that, in order to keep the ball at a consistent velocity, it still has to move another 0.4 meters on this frame. The second move_and_collide call moves it this remaining distance, in whatever direction we’ve decided to bounce it.

Try normalizing it:

var arkanoid : Vector3 = paddle_collision(collider)
velocity = arkanoid.normalized() * SPEED

Normalizing a vector means setting its length to 1, while keeping its direction. So with the above code, the ball would move with the default speed defined in the SPEED variable. Alternatively, you could save the speed it was at when it hit the paddle and use that.

Also, you shouldn’t multiply with delta when setting the velocity, because you are already multiplying with delta at the start of the _physics_process function (when you define ball_speed). You probably don’t wanna multiply with delta twice.

Oh yeah, and bounce basically does this:

  • v1 is the vector we’re bouncing
  • we’re using the normal vector of the plane represented by the black line
  • v2 is the result
  • v1 and v2 have the same length (yeah I know they don’t in the drawing, I made it in paint in 2 minutes)
  • the angles A and B are the same

Got ya. So then I call move_and_collide with that velocity value I just set? Based on your other post, I’m unsure if I should include bounce since the angle the ball leaves will be based on where it hits the paddle as opposed to the incoming angle.

velocity = arkanoid.normalized() * SPEED

I think this is working! I had tried normalizing the vector before, but had included bounce or tried other things. I hadn’t just applied it to the velocity and called move_and_collide

Also, thanks for the tip on not needing to include delta again. I had done so because without it my ball would get so fast it would go outside the level. I think this was a combination of the velocity being too high and me not normalizing the vector possibly.

Hmm. In the other branches you are calling it with the reflect vector, which is the remaining movement that didn’t get to happen (because of the collision), but bounced. You probably want to do something similar:

  1. Calculate the remaining distance to be moved
  2. Calculate the direction to move using your paddle_collision function
  3. Normalize the direction and multiply it with the remaining distance, and then use that to move_and_collide
  4. Set the velocity to the new direction (normalized), multiplied by the speed you want it to move at. This will then be used on the next frame.

Thanks. I tried this two different ways. using arkanoid_reflect doesn’t work at all. I suspected not, but wanted to try it out to see what would happen, from what I can tell, not much.

new_direction does reflect the ball, however, something about the remainder or the way I’m getting the remainder is making the new vector pretty unpredictable. When coming in at very similar angles and hitting the same space on the paddle, the new angle can be totally different.

var remainder : Vector3 = collision_object.get_remainder()
var new_direction : Vector3 = arkanoid.normalized() * remainder
var arkanoid_reflect : Vector3 = collision_object.get_remainder().bounce(arkanoid.normalized()) * SPEED

velocity = new_direction.normalized() * SPEED

I’ll also head back to your first answer and mark this as solved as you’ve definitely helped me understand the base questions I had!

I think the problem with arkanoid_reflect is that you’re trying to use arkanoid (which at least in your previous code was the movement direction you wanted for the ball) as a normal vector of a plane to bounce against - those are two different things.

Try this for new_direction, since multiplying two vectors will just give you the component-wise product, which is not what you want:

var new_direction : Vector3 = arkanoid.normalized() * remainder.length()
1 Like

Thanks so much for your help. Worked as expected. Instead of using SPEED I also simply multiplied the new vector by velocity.length() and that retained my velocity. I could then multiply by a speed up factor and have an overall limit on velocity for the ball too.

Final setting for the ball’s velocity:

velocity = (new_direction.normalized() * velocity.length() * speed_up_factor).limit_length(VELOCITY_LIMIT)

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