extends RigidBody3D signal ball_collision_started signal ball_collision_ended var captured_velocity : Vector3 var bounced_velocity : Vector3 var just_collided = false var total_bounce_duration = 0.5 var gravity = Vector3(0, -10, 0) func _physics_process(delta): # Capture the linear velocity unless the ball have stopped on collision if not just_collided: captured_velocity = linear_velocity _get_collision(delta) var collision_normal : Vector3 var collision_angle_factor : float func _get_collision(delta): var collision : KinematicCollision3D # test_only is true so this function doesn't move the ball on top of the main movement collision = move_and_collide(linear_velocity * delta, true) # A collision happens if collision: # Get collision data stuff collision_normal = collision.get_normal() bounced_velocity = captured_velocity.bounce(collision_normal) # Get the dot product between the surface normal and the ball direction on hit collision_angle_factor = collision_normal.dot(captured_velocity.normalized() * -1) # Set the duration of the timer based on the collision angle $TimerCollision.wait_time = total_bounce_duration * collision_angle_factor $TimerCollision.start() # Send the signal after getting the data to avoid having zeroed values just_collided = true emit_signal("ball_collision_started") # Completely stop the ball in place linear_velocity = Vector3.ZERO freeze = true # Move the ball again after the timer runs out # The if check prevents the timeout function from trying to connect every frame if not $TimerCollision.timeout.is_connected(_after_collision): $TimerCollision.timeout.connect(_after_collision) func _after_collision(): just_collided = false emit_signal("ball_collision_ended") # Unfreezes the ball and apply the bounced captured velocity to it freeze = false linear_velocity = bounced_velocity