Godot_Bouncy_Ball_Prototype/Ball/ball.gd

86 lines
2.4 KiB
GDScript

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 bounce_factor_cap = 0.25
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)
### DEBUG
# Delete balls that escape the level
if global_position.length() > 20.0:
print("ball deleted, total: ", Debug.get_total_ball_count())
queue_free()
var collision_position : Vector3
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_position = collision.get_position()
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)
# Cap the factor for collision angles that are too shallow
if collision_angle_factor < bounce_factor_cap:
collision_angle_factor = 0
# Bounce back immediately if factor is 0
if collision_angle_factor == 0:
_after_collision()
# Do the sticky behaviour otherwise
else:
# 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
### DEBUG
# freeze = true
# queue_free()