3D Adjust bounce angle based on collision point

Godot Version

Godot 4.2

Question

I have am working on a 3D Brick Breaker style game. I want to adjust the angle of the bounce of the ball based on the angle the ball collides with the brick or paddle. This is to allow the player to try to get more control over where the ball will go by trying to hit it with a specific part of the paddle.

Ex. If the ball hits the center of the paddle, it should basically reflect the angle it comes in at. If it’s out towards one of the ends of the paddle, it should adjust the angle proportionately.

Bonus points if it will help dealing with when the ball hits the side of the paddle as opposed to the front.

Ball is a CharacterBody3D
Paddle is CharacterBody3D
Bricks are RigidBody3D

Here’s a version I’ve played with that is based on a 2D version I had found/tweaked before. If this can be my starting point, great, but I am not set on using this. This is being called as part of a collision after move_and_collide. Not opposed to using move_and_slide but I feel like with a characterbody3D ball, move_and_collide is ideal for this situation. Please correct me if I’m wrong.

func ball_collision(collider):
	var ball_center = position.y

	var collider_width = collider.get_width()

	var collider_center = collider.position.y
	print("collider_center ", collider_center)
	var velocity_length = velocity.length()

	var collision_x = (ball_center - collider_center) /(collider_width/2)
	print("collision_x ", collision_x)
	if collision_x == 1:
		collision_x = 1.1
	if collision_x == -1:
		collision_x = -1.1
	
	var new_velocity = Vector3.ZERO
	
	new_velocity.x = velocity_length * collision_x
	
	#if collider.get_rid() == last_collider_id && collider is Brick:
		#new_velocity.y = new_velocity.rotated(deg_to_rad(randf_range(135, 225))).y * 10
	#else:
		#last_collider_id == collider.get_rid()
		
		
	new_velocity.z = sqrt(absf(velocity_length* velocity_length - new_velocity.x * new_velocity.x)) * (-1 if velocity.z > 0 else 1)
	
	var speed_multiplier = speed_up_factor if collider.is_in_group("Paddle") else 1.0

	velocity = (new_velocity).limit_length(VELOCITY_LIMIT)
	

Further notes:

  • The x and z axis are used for the ball movement. The Y axis is locked.
  • I plan to use this function primarily for the paddle, potentially the bricks.
  • I feel like there’s probably an easier way to approach this in Godot 4 than my code above. I feel like I should be able to offset the normal with the distance from the center of the collider.
  • I"m unsure on if colliding with the left side of the paddle should send the ball back at a leftward angle regardless of the incoming angle or if it should just reduce the return angle by the distance from the center.
  • Edit: I am using Jolt3D as even with a very basic prototype I was not getting great FPS. Down to use default physics, but it seems like what I’m trying to do isn’t great for default Godot physics. I could be doing something totally wrong.

Vector math is definitely an area I want to get better at!