2024-11-03 18:16:30 +00:00
extends CharacterBody2D
#Tutorial: https://www.youtube.com/watch?v=mJ1ZfGDTMCY t=15s
2024-11-06 06:38:44 +00:00
signal car_finished ( playerid , finalTime )
2024-11-04 20:56:07 +00:00
2024-11-03 18:16:30 +00:00
const COLLISIONMASK_FINISH = 3 #set in road_overlay
const COLLISIONMASK_CHECKPOINT = 4 #set in road_overlay
2024-11-05 07:18:14 +00:00
var checkpoints : Array [ String ] = [ ] #gets set on car creation
var checkpointtimes : Array [ float ] = [ ]
2024-11-03 21:37:15 +00:00
const ROAD_R_NAME = " road_r "
const ROAD_L_NAME = " road_l "
2024-11-12 23:47:40 +00:00
const CAR_NAME = " CharacterBody_Car "
2024-11-03 21:37:15 +00:00
2024-11-06 06:38:44 +00:00
const COLLISION_SLOWDOWN_CAR = 1.01
const COLLISION_SLOWDOWN_WALL = 1.2
2024-11-10 17:13:14 +00:00
const MIN_CRASH_VELOCITY = 10
2024-11-06 06:38:44 +00:00
2024-11-03 18:16:30 +00:00
const STANDSTILLSPEED = 0.5
var wheel_base = 60 * 0.5
2024-11-06 18:46:47 +00:00
var engine_power = 350
2024-12-30 21:38:45 +00:00
#var engine_power = 1000 #crazy mode
2024-11-10 16:18:11 +00:00
var applied_engine_power = 0
2024-11-03 18:16:30 +00:00
var friction = - 0.5
var drag = - 0.0005
var braking = - 200
2024-12-30 21:38:45 +00:00
#var braking = -600 #crazy mode
2024-11-03 18:16:30 +00:00
var max_speed_reverse = 100
2024-11-09 13:38:26 +00:00
var slip_speed = 150
var traction_fast = 0.05 #traction when above slip_speed
2024-11-03 18:16:30 +00:00
var traction_slow = 0.5
#Automatic Steering settings
2024-11-16 20:33:32 +00:00
var steering_speed_slow = 100 #speed for slow steering
2024-11-03 18:16:30 +00:00
var steering_angle_slow = 50 #maximum angle slow speed
2024-12-31 14:52:04 +00:00
var steering_distance_far_slow = 256
2024-11-16 20:33:32 +00:00
var steering_distance_close_slow = 16
2024-11-03 18:16:30 +00:00
2024-11-09 09:32:12 +00:00
var steering_speed_fast = 400 #speed for fast steering
2024-12-30 21:38:45 +00:00
#var steering_speed_fast = 3*400 #speed for fast steering #crazy mode
2024-11-09 13:38:26 +00:00
var steering_angle_fast = 1 #maximum angle fast speed
2024-11-03 18:16:30 +00:00
var steering_distance_far_fast = 256
2024-11-16 20:33:32 +00:00
var steering_distance_close_fast = 64
2024-11-03 18:16:30 +00:00
# resetCar
2024-11-06 18:46:47 +00:00
var resetcar_stoppedspeed = 50 #activate timer when below this speed
2024-11-03 18:16:30 +00:00
var resetcar_movingspeed = resetcar_stoppedspeed + 10 #stop timer when above this speed
var resetcar_distance = 128 #196 is roughly when car is in the middle of a two wide road
var resetcar_steerangle = 120
2024-11-12 23:47:40 +00:00
# other car avoidance
var avoid_car_distance_far = 100
var avoid_car_distance_close = 50
var avoid_car_steeringangle_far = 0
var avoid_car_steeringangle_close = 50
var avoid_car_mininum_space = 50 #how much space RayCast_FL/R must have for avoidance to start.
var avoidance_steeringangle : float = 0 #actual set steering angle to avoid car
var avoidance_turndirection : int = 0
2024-11-11 17:18:47 +00:00
#Effects
const BURNOUT_KEEP_ON_AFTER_START = 1
2024-11-03 18:16:30 +00:00
#Variables
2024-11-06 06:38:44 +00:00
var running = false
2024-11-03 18:16:30 +00:00
var acceleration = Vector2 . ZERO
var steer_direction = 0
var autoreset = false
2024-11-04 20:56:07 +00:00
var autosteer_enabled = false
2024-11-11 17:18:47 +00:00
var burnout : float = 0 #at 0 is off
2024-11-03 18:16:30 +00:00
@ onready var ray_cast_fl : RayCast2D = $ RayCast_FL
@ onready var ray_cast_fr : RayCast2D = $ RayCast_FR
2024-12-04 15:30:43 +00:00
2024-11-12 23:47:40 +00:00
2024-11-03 18:16:30 +00:00
@ onready var reset_timer : Timer = $ resetTimer
@ onready var ray_cast_car : RayCast2D = $ RayCast_Car #for tracking markers
2024-11-04 20:56:07 +00:00
@ onready var collision_shape : CollisionShape2D = $ CollisionShape2D
@ onready var collision_enable_timer : Timer = $ collisionEnableTimer
2024-11-10 16:18:11 +00:00
@ onready var enginesound : Node = $ Enginesound
2024-11-10 17:13:14 +00:00
@ onready var sfx : Node = $ SFX
2024-11-10 16:18:11 +00:00
2024-11-11 18:09:24 +00:00
2024-11-03 18:16:30 +00:00
var playerid = 0
2024-11-06 06:38:44 +00:00
var finalTime = - 1
2024-11-03 18:16:30 +00:00
2024-11-04 20:56:07 +00:00
func _ready ( ) - > void :
collision_shape . disabled = true #disable collisions on start. also to avoid collision when initially setting position
2024-11-06 06:38:44 +00:00
finalTime = - 1
2024-11-05 07:18:14 +00:00
2024-11-04 20:56:07 +00:00
2024-11-03 18:16:30 +00:00
func _physics_process ( delta : float ) - > void :
2024-12-30 21:38:45 +00:00
2024-11-03 18:16:30 +00:00
acceleration = Vector2 . ZERO
check_markers ( )
2024-11-10 16:18:11 +00:00
get_input ( delta )
2024-11-03 18:16:30 +00:00
apply_friction ( )
calculate_steering ( delta )
velocity += acceleration * delta
#velocity = transform.x * 200
#vel = move_and_slide()
2024-11-11 17:18:47 +00:00
if burnout > 0 :
$ Burnout_Left . emitting = true
$ Burnout_Right . emitting = true
else :
$ Burnout_Left . emitting = false
$ Burnout_Right . emitting = false
2024-11-03 18:16:30 +00:00
move_and_slide ( )
2024-11-06 06:38:44 +00:00
for i in get_slide_collision_count ( ) :
var collision = get_slide_collision ( i )
2024-11-10 17:13:14 +00:00
if $ " . " . name == collision . get_collider ( ) . name : #collided with another car
sfx . crashCarToCar ( velocity . length ( ) )
2024-11-11 18:09:24 +00:00
#collision.get_collider_velocity().length()
2024-11-06 06:38:44 +00:00
# velocity-=COLLISION_SLOWDOWN_CAR
if ROAD_R_NAME == collision . get_collider ( ) . name or ROAD_L_NAME == collision . get_collider ( ) . name : #collided with road
2024-11-10 17:13:14 +00:00
if ( velocity . length ( ) > MIN_CRASH_VELOCITY ) :
velocity /= COLLISION_SLOWDOWN_WALL
2024-11-11 18:09:24 +00:00
#sfx.crashBarrier(velocity.length())
2024-11-10 17:13:14 +00:00
sfx . crashBarrier ( velocity . length ( ) )
2024-11-11 18:09:24 +00:00
2024-11-06 06:38:44 +00:00
2024-11-09 13:38:26 +00:00
#if get_slide_collision_count()>0:
# velocity/=2
2024-11-03 18:16:30 +00:00
#for i in get_slide_collision_count():
# var collision = get_slide_collision(i)
# print("Collided with: ", collision.get_collider().name)
2024-11-04 20:56:07 +00:00
if velocity . length ( ) < resetcar_stoppedspeed and autosteer_enabled : #moving slow, possibly crash?
2024-11-03 18:16:30 +00:00
if reset_timer . is_stopped ( ) :
reset_timer . start ( )
if velocity . length ( ) > resetcar_movingspeed :
reset_timer . stop ( )
2024-11-11 17:18:47 +00:00
if running :
enginesound . setCarSpeed ( velocity . length ( ) )
enginesound . setCarAcceleration ( applied_engine_power / engine_power , delta )
else : #at start or end of round
enginesound . setCarSpeed ( burnout * 500 )
enginesound . setCarAcceleration ( 1.0 , delta )
2024-11-10 16:18:11 +00:00
#print("Car Accel="+str(acceleration.length()) +"/"+str(engine_power))
2024-11-03 18:16:30 +00:00
func _on_reset_timer_timeout ( ) - > void :
autoreset = true
func apply_friction ( ) :
if velocity . length ( ) < STANDSTILLSPEED : #standstill
velocity = Vector2 . ZERO
var friction_force = velocity * friction
var drag_force = velocity * velocity . length ( ) * drag
acceleration += drag_force + friction_force
2024-11-10 16:18:11 +00:00
func get_input ( delta : float ) :
2024-11-03 18:16:30 +00:00
2024-11-12 23:47:40 +00:00
# Check RayCast to Road
2024-11-03 18:16:30 +00:00
const distance_inf = 1000
var distance_fl = distance_inf
var distance_fr = distance_inf
2024-12-04 15:30:43 +00:00
var distance_guide_l = 0
var distance_guide_r = 0
2024-11-03 18:16:30 +00:00
if ray_cast_fl . is_colliding ( ) :
var origin = ray_cast_fl . global_transform . origin
var collision_point = ray_cast_fl . get_collision_point ( )
distance_fl = origin . distance_to ( collision_point )
2024-11-03 21:37:15 +00:00
var collision_object = ray_cast_fl . get_collider ( )
2024-12-04 15:30:43 +00:00
if collision_object . name == ROAD_R_NAME :
distance_fl *= 2 #fake distance when wrong side of road
2024-11-03 21:37:15 +00:00
2024-11-03 18:16:30 +00:00
#print("DistanceFL "+str(distance_fl))
if ray_cast_fr . is_colliding ( ) :
var origin = ray_cast_fr . global_transform . origin
var collision_point = ray_cast_fr . get_collision_point ( )
distance_fr = origin . distance_to ( collision_point )
2024-12-04 15:30:43 +00:00
var collision_object = ray_cast_fr . get_collider ( )
if collision_object . name == ROAD_L_NAME :
distance_fr *= 2 #fake distance when wrong side of road
2024-11-03 18:16:30 +00:00
#print("DistanceFR "+str(distance_fr))
2024-12-04 15:30:43 +00:00
2024-11-03 18:16:30 +00:00
var distance_min = min ( distance_fl , distance_fr )
var turndirection = 1 if distance_fl < distance_fr else - 1
var steering_angle = constrain ( remap ( velocity . length ( ) , steering_speed_fast , steering_speed_slow , steering_angle_fast , steering_angle_slow ) , steering_angle_fast , steering_angle_slow ) #set maximum steering_angle based on speed
2024-11-12 23:47:40 +00:00
2024-11-03 18:16:30 +00:00
var steering_distance_far = constrain ( remap ( velocity . length ( ) , steering_speed_fast , steering_speed_slow , steering_distance_far_fast , steering_distance_far_slow ) , steering_distance_far_fast , steering_distance_far_slow )
var steering_distance_close = constrain ( remap ( velocity . length ( ) , steering_speed_fast , steering_speed_slow , steering_distance_close_fast , steering_distance_close_slow ) , steering_distance_close_fast , steering_distance_close_slow )
2024-12-04 15:30:43 +00:00
steer_direction = 0 #0=straight
2024-11-04 20:56:07 +00:00
if autosteer_enabled :
if distance_min < steering_distance_far : #wall close, start steering away
2024-12-04 15:30:43 +00:00
steer_direction += turndirection * deg_to_rad ( constrain ( remap ( distance_min , steering_distance_far , steering_distance_close , 0 , steering_angle ) , 0 , steering_angle ) )
2024-12-28 20:37:30 +00:00
'''
2024-12-21 13:05:37 +00:00
if Input . is_action_pressed ( " ui_left " ) :
autosteer_enabled = false
steer_direction = - 1
elif Input . is_action_pressed ( " ui_right " ) :
autosteer_enabled = false
steer_direction = 1
2024-12-28 20:37:30 +00:00
'''
2024-11-12 23:47:40 +00:00
2024-11-10 16:18:11 +00:00
applied_engine_power = 0
2024-11-11 17:18:47 +00:00
var key_accelerator_pressed = Input . is_action_pressed ( Gamestate . userinput_prefix + str ( playerid ) )
if key_accelerator_pressed :
if running :
2024-11-06 06:38:44 +00:00
#velocity = transform.x * 500
2024-11-10 16:18:11 +00:00
applied_engine_power = engine_power
2024-11-06 06:38:44 +00:00
if not autosteer_enabled : #start autosteer when accelerate is pressed
autosteer_enabled = true
collision_enable_timer . start ( )
if autoreset :
autoreset = false
2024-11-11 17:18:47 +00:00
if not running and finalTime == - 1 and key_accelerator_pressed : #at start
burnout = min ( 1.0 , burnout + 1.0 * delta )
else :
burnout = max ( 0 , burnout - 1.0 / BURNOUT_KEEP_ON_AFTER_START * delta )
2024-11-10 16:18:11 +00:00
acceleration = transform . x * applied_engine_power
2024-11-03 18:16:30 +00:00
2024-11-06 06:38:44 +00:00
if autoreset and running :
2024-11-03 18:16:30 +00:00
acceleration = transform . x * braking #drive backwards
if distance_min > = resetcar_distance : #nothing in front of car
steer_direction = resetcar_steerangle #keep steering so turn around if standing in the middle of a track
else :
steer_direction *= - 1 #invert steering
else :
if steer_direction > 1 :
resetcar_steerangle = max ( - resetcar_steerangle , + resetcar_steerangle ) #calculate steering direction for next autoreset
if steer_direction < 1 :
resetcar_steerangle = min ( - resetcar_steerangle , + resetcar_steerangle ) #calculate steering direction for next autoreset
2024-11-10 19:49:52 +00:00
2024-11-03 18:16:30 +00:00
func calculate_steering ( delta : float ) :
var rear_wheel = position - transform . x * wheel_base / 2.0
var front_wheel = position + transform . x * wheel_base / 2.0
rear_wheel += velocity * delta
front_wheel += velocity . rotated ( steer_direction ) * delta
var new_heading = ( front_wheel - rear_wheel ) . normalized ( )
var traction = traction_slow
if velocity . length ( ) > slip_speed :
traction = traction_fast
2024-11-10 19:31:11 +00:00
2024-11-03 18:16:30 +00:00
var d = new_heading . dot ( velocity . normalized ( ) )
2024-11-10 19:31:11 +00:00
2024-11-03 18:16:30 +00:00
if d > 0 :
velocity = velocity . lerp ( new_heading * velocity . length ( ) , traction )
2024-11-11 16:34:23 +00:00
if d < 0.85 and velocity . length ( ) > slip_speed and applied_engine_power > engine_power / 2 :
2024-11-10 19:31:11 +00:00
sfx . sliding ( velocity . length ( ) )
2024-11-11 16:34:23 +00:00
$ PositionFrontLeftTire . setSliding ( true )
$ PositionFrontRightTire . setSliding ( true )
2024-11-10 19:49:52 +00:00
elif d > 0.98 : #keep trails a bit longer
2024-11-11 16:34:23 +00:00
$ PositionFrontLeftTire . setSliding ( false )
$ PositionFrontRightTire . setSliding ( false )
2024-11-10 19:31:11 +00:00
2024-11-03 18:16:30 +00:00
elif d < 0 :
velocity = - new_heading * min ( velocity . length ( ) , max_speed_reverse )
rotation = new_heading . angle ( )
2024-11-09 13:27:34 +00:00
func getRound ( ) :
var i = getNextCPindex ( ) / checkpoints . size ( )
return i
2024-11-03 18:16:30 +00:00
2024-11-10 00:06:25 +00:00
2024-11-03 18:16:30 +00:00
func check_markers ( ) :
if ray_cast_car . is_colliding ( ) :
#print("Marker: "+str(ray_cast_car.get_collider()))
2024-11-05 07:18:14 +00:00
#if ray_cast_car.get_collision_mask_value(COLLISIONMASK_FINISH):
# print("Player "+str(playerid)+" Finished")
#if ray_cast_car.get_collision_mask_value(COLLISIONMASK_CHECKPOINT):
var rcc_collidername = ray_cast_car . get_collider ( ) . name
if rcc_collidername == " area_finish " :
2024-11-09 13:27:34 +00:00
$ label_round . showRounds ( getRound ( ) )
2024-11-05 07:18:14 +00:00
#print("Player "+str(playerid)+" drove through Finish")
2024-11-09 13:27:34 +00:00
if getNextCPindex ( ) == - 1 and finalTime == - 1 : #all checkpoints have times and did not finish = Finished
2024-11-05 07:18:14 +00:00
print ( " Player " + str ( playerid ) + " Finished " )
2024-11-06 06:38:44 +00:00
print ( " Final Time: " + str ( Gamestate . getTimeElapsed ( ) ) )
running = false
finalTime = Gamestate . getTimeElapsed ( )
car_finished . emit ( playerid , finalTime )
elif rcc_collidername . begins_with ( " area_cp " ) :
var nextcp_i = getNextCPindex ( )
2024-11-05 07:18:14 +00:00
var checkpoint_i = checkpoints . find ( rcc_collidername )
2024-11-06 06:38:44 +00:00
if checkpoint_i > = 0 and nextcp_i > = 0 : #found and there is a next checkpoint time free
if ( nextcp_i % checkpoints . size ( ) ) == checkpoint_i : #this cp is next cp
checkpointtimes [ nextcp_i ] = Gamestate . getTimeElapsed ( )
2024-11-10 00:06:25 +00:00
#print("Player "+str(playerid)+" Checkpoint "+str(ray_cast_car.get_collider().name))
#print("New CP array "+str(checkpointtimes))
2024-11-03 18:16:30 +00:00
func constrain ( val , a , b ) :
var vmin = min ( a , b )
var vmax = max ( a , b )
return min ( vmax , max ( vmin , val ) )
2024-11-04 20:56:07 +00:00
2024-11-06 06:38:44 +00:00
func getNextCPindex ( ) :
#returns index of first 0 value in times array
#-1 if all cps have times
#[10.2,15.5,12.2,0,0,0,0] -> 3
var i = 0
for cpt in checkpointtimes :
if cpt == 0 :
return i
i += 1
return - 1
2024-11-04 20:56:07 +00:00
func _on_collision_enable_timer_timeout ( ) - > void :
collision_shape . disabled = false