teststrecke-game/scripts/car.gd

383 lines
12 KiB
GDScript3
Raw Normal View History

2024-11-03 19:16:30 +01:00
extends CharacterBody2D
#Tutorial: https://www.youtube.com/watch?v=mJ1ZfGDTMCY t=15s
signal car_finished(playerid,finalTime)
2025-01-14 23:57:26 +01:00
signal car_on_checkpoint(cplayerid,checkpointtimes,i)
2024-11-04 21:56:07 +01:00
2024-11-03 19:16:30 +01:00
const COLLISIONMASK_FINISH=3 #set in road_overlay
const COLLISIONMASK_CHECKPOINT=4 #set in road_overlay
2024-11-05 08:18:14 +01:00
var checkpoints :Array[String]=[] #gets set on car creation
var checkpointtimes :Array[float]=[]
2024-11-03 22:37:15 +01:00
const ROAD_R_NAME="road_r"
const ROAD_L_NAME="road_l"
2024-11-13 00:47:40 +01:00
const CAR_NAME="CharacterBody_Car"
2024-11-03 22:37:15 +01:00
const COLLISION_SLOWDOWN_CAR=1.01
const COLLISION_SLOWDOWN_WALL=1.2
2024-11-10 18:13:14 +01:00
const MIN_CRASH_VELOCITY=10
2024-11-03 19:16:30 +01:00
const STANDSTILLSPEED=0.5
var wheel_base = 60*0.5
2024-11-06 19:46:47 +01:00
var engine_power = 350
2024-12-30 22:38:45 +01:00
#var engine_power = 1000 #crazy mode
2024-11-10 17:18:11 +01:00
var applied_engine_power=0
2024-11-03 19:16:30 +01:00
var friction = -0.5
var drag = -0.0005
var braking = -200
2024-12-30 22:38:45 +01:00
#var braking = -600 #crazy mode
2024-11-03 19:16:30 +01:00
var max_speed_reverse = 100
2024-11-09 14:38:26 +01:00
var slip_speed = 150
var traction_fast = 0.05 #traction when above slip_speed
2024-11-03 19:16:30 +01:00
var traction_slow = 0.5
2025-01-14 19:01:07 +01:00
var max_steering_change = 2.5
var turndirection_scale = 0.01
2024-11-03 19:16:30 +01:00
#Automatic Steering settings
2024-11-16 21:33:32 +01:00
var steering_speed_slow = 100 #speed for slow steering
2024-11-03 19:16:30 +01:00
var steering_angle_slow = 50 #maximum angle slow speed
var steering_distance_far_slow=256
2024-11-16 21:33:32 +01:00
var steering_distance_close_slow=16
2024-11-03 19:16:30 +01:00
2024-11-09 10:32:12 +01:00
var steering_speed_fast = 400 #speed for fast steering
2024-12-30 22:38:45 +01:00
#var steering_speed_fast = 3*400 #speed for fast steering #crazy mode
2024-11-09 14:38:26 +01:00
var steering_angle_fast = 1 #maximum angle fast speed
2024-11-03 19:16:30 +01:00
var steering_distance_far_fast=256
2024-11-16 21:33:32 +01:00
var steering_distance_close_fast=64
2024-11-03 19:16:30 +01:00
# resetCar
2024-11-06 19:46:47 +01:00
var resetcar_stoppedspeed = 50 #activate timer when below this speed
2024-11-03 19:16:30 +01: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
2025-03-22 15:13:02 +01:00
var resetcar_steerangle=120.0/360.0*2*PI
2024-11-03 19:16:30 +01:00
2024-11-13 00:47:40 +01: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 18:18:47 +01:00
#Effects
const BURNOUT_KEEP_ON_AFTER_START=1
2024-11-03 19:16:30 +01:00
#Variables
var running=false
2024-11-03 19:16:30 +01:00
var acceleration = Vector2.ZERO
var steer_direction=0
var autoreset=false
2024-11-04 21:56:07 +01:00
var autosteer_enabled=false
2025-03-22 15:53:00 +01:00
var no_input_timeout_flag=false
2024-11-11 18:18:47 +01:00
var burnout:float=0 #at 0 is off
2024-11-03 19:16:30 +01:00
@onready var ray_cast_fl: RayCast2D = $RayCast_FL
@onready var ray_cast_fr: RayCast2D = $RayCast_FR
2024-12-04 16:30:43 +01:00
2024-11-13 00:47:40 +01:00
2024-11-03 19:16:30 +01:00
@onready var reset_timer: Timer = $resetTimer
2025-03-22 15:13:02 +01:00
@onready var no_input_timer: Timer = $NoInputTimer
2024-11-03 19:16:30 +01:00
@onready var ray_cast_car: RayCast2D = $RayCast_Car #for tracking markers
2024-11-04 21:56:07 +01:00
@onready var collision_shape: CollisionShape2D = $CollisionShape2D
@onready var collision_enable_timer: Timer = $collisionEnableTimer
2024-11-10 17:18:11 +01:00
@onready var enginesound: Node = $Enginesound
2024-11-10 18:13:14 +01:00
@onready var sfx: Node = $SFX
2024-11-10 17:18:11 +01:00
2024-11-11 19:09:24 +01:00
2024-11-03 19:16:30 +01:00
var playerid=0
var finalTime=-1
2024-11-03 19:16:30 +01:00
2024-11-04 21:56:07 +01:00
func _ready() -> void:
collision_shape.disabled=true #disable collisions on start. also to avoid collision when initially setting position
finalTime=-1
no_input_timer.wait_time=Gamestate.getRemoveCarsTimeout()
2024-11-05 08:18:14 +01:00
2024-11-04 21:56:07 +01:00
2024-11-03 19:16:30 +01:00
func _physics_process(delta: float) -> void:
2024-12-30 22:38:45 +01:00
2024-11-03 19:16:30 +01:00
acceleration=Vector2.ZERO
check_markers()
2024-11-10 17:18:11 +01:00
get_input(delta)
2024-11-03 19:16:30 +01:00
apply_friction()
calculate_steering(delta)
velocity +=acceleration*delta
#velocity = transform.x * 200
#vel = move_and_slide()
2024-11-11 18:18:47 +01:00
if burnout>0:
$Burnout_Left.emitting=true
$Burnout_Right.emitting=true
else:
$Burnout_Left.emitting=false
$Burnout_Right.emitting=false
2025-03-22 15:53:00 +01:00
update_timeout_visibility()
2024-11-03 19:16:30 +01:00
move_and_slide()
for i in get_slide_collision_count():
var collision = get_slide_collision(i)
2024-11-10 18:13:14 +01:00
if $".".name==collision.get_collider().name: #collided with another car
sfx.crashCarToCar(velocity.length())
2024-11-11 19:09:24 +01:00
#collision.get_collider_velocity().length()
# 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 18:13:14 +01:00
if (velocity.length()>MIN_CRASH_VELOCITY):
velocity/=COLLISION_SLOWDOWN_WALL
2024-11-11 19:09:24 +01:00
#sfx.crashBarrier(velocity.length())
2024-11-10 18:13:14 +01:00
sfx.crashBarrier(velocity.length())
2024-11-11 19:09:24 +01:00
2024-11-09 14:38:26 +01:00
#if get_slide_collision_count()>0:
# velocity/=2
2024-11-03 19:16:30 +01:00
#for i in get_slide_collision_count():
# var collision = get_slide_collision(i)
# print("Collided with: ", collision.get_collider().name)
2024-11-04 21:56:07 +01:00
if velocity.length() < resetcar_stoppedspeed and autosteer_enabled: #moving slow, possibly crash?
2024-11-03 19:16:30 +01:00
if reset_timer.is_stopped():
reset_timer.start()
2025-03-22 15:13:02 +01:00
2024-11-03 19:16:30 +01:00
if velocity.length() > resetcar_movingspeed:
reset_timer.stop()
2024-11-11 18:18:47 +01: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 17:18:11 +01:00
#print("Car Accel="+str(acceleration.length()) +"/"+str(engine_power))
2024-11-03 19:16:30 +01:00
func _on_reset_timer_timeout() -> void:
autoreset=true
2025-03-22 15:13:02 +01:00
func update_timeout_visibility():
2025-03-22 15:53:00 +01:00
var no_input_timer_time_left=no_input_timer.time_left
if (no_input_timer.is_stopped()):
no_input_timer_time_left=no_input_timer.wait_time
if no_input_timeout_flag:
no_input_timer_time_left=0 #car already disappeared
var fadeout=1-constrain((3-no_input_timer_time_left)/3.0,0.0,1.0) #1= full visible, 0=timeouted
2025-03-22 15:13:02 +01:00
$sprite_body.modulate.a=fadeout
$sprite_features.modulate.a=fadeout
$sprite_steeringwheel.modulate.a=fadeout
2025-03-22 15:53:00 +01:00
2024-11-03 19:16:30 +01:00
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 17:18:11 +01:00
func get_input(delta:float):
2024-11-03 19:16:30 +01:00
2024-11-13 00:47:40 +01:00
# Check RayCast to Road
2024-11-03 19:16:30 +01:00
const distance_inf=1000
var distance_fl=distance_inf
var distance_fr=distance_inf
2024-12-04 16:30:43 +01:00
var distance_guide_l=0
var distance_guide_r=0
2024-11-03 19:16:30 +01: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 22:37:15 +01:00
var collision_object=ray_cast_fl.get_collider()
2024-12-04 16:30:43 +01:00
if collision_object.name==ROAD_R_NAME:
distance_fl*=2 #fake distance when wrong side of road
2024-11-03 22:37:15 +01:00
2024-11-03 19:16:30 +01: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 16:30:43 +01: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 19:16:30 +01:00
#print("DistanceFR "+str(distance_fr))
2024-12-04 16:30:43 +01:00
2024-11-03 19:16:30 +01:00
var distance_min= min(distance_fl,distance_fr)
2025-01-14 19:01:07 +01:00
#var turndirection = 1 if distance_fl<distance_fr else -1
var turndirection = constrain((distance_fr-distance_fl)*turndirection_scale, -1,1)
2024-11-03 19:16:30 +01:00
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-13 00:47:40 +01:00
2024-11-03 19:16:30 +01: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)
2025-01-14 19:01:07 +01:00
var steer_direction_aim=0 #0=straight
2024-11-04 21:56:07 +01:00
if autosteer_enabled:
if distance_min<steering_distance_far: #wall close, start steering away
2025-01-14 19:01:07 +01:00
steer_direction_aim += turndirection*deg_to_rad(constrain(remap(distance_min,steering_distance_far,steering_distance_close,0,steering_angle), 0,steering_angle))
2024-12-04 16:30:43 +01:00
'''
2024-12-21 14:05:37 +01: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-11-13 00:47:40 +01:00
2025-01-14 19:01:07 +01:00
2024-11-10 17:18:11 +01:00
applied_engine_power=0
2024-11-11 18:18:47 +01:00
var key_accelerator_pressed=Input.is_action_pressed(Gamestate.userinput_prefix+str(playerid))
2025-03-22 15:53:00 +01:00
#check Input Timeout
if key_accelerator_pressed:
no_input_timer.stop()
else:
if Gamestate.getRemoveCarsTimeout()>0 and no_input_timer.is_stopped() and !no_input_timeout_flag: #reset timer
2025-03-22 15:53:00 +01:00
no_input_timer.start()
2024-11-11 18:18:47 +01:00
if key_accelerator_pressed:
2025-03-22 15:53:00 +01:00
2024-11-11 18:18:47 +01:00
if running:
#velocity = transform.x * 500
2024-11-10 17:18:11 +01:00
applied_engine_power=engine_power
if not autosteer_enabled: #start autosteer when accelerate is pressed
autosteer_enabled=true
collision_enable_timer.start()
if autoreset:
autoreset=false
2024-11-11 18:18:47 +01: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 17:18:11 +01:00
acceleration = transform.x * applied_engine_power
2024-11-03 19:16:30 +01:00
if autoreset and running:
2024-11-03 19:16:30 +01:00
acceleration = transform.x * braking #drive backwards
2025-03-22 15:13:02 +01:00
steer_direction_aim=resetcar_steerangle #keep steering to turn around if standing in the middle of a track
#steer_direction=steer_direction_aim #steer without smoothing
2024-11-03 19:16:30 +01:00
else:
2025-03-22 15:13:02 +01:00
if steer_direction_aim<0.1:
2024-11-03 19:16:30 +01:00
resetcar_steerangle=max(-resetcar_steerangle,+resetcar_steerangle) #calculate steering direction for next autoreset
2025-03-22 15:13:02 +01:00
if steer_direction_aim>0.1:
2024-11-03 19:16:30 +01:00
resetcar_steerangle=min(-resetcar_steerangle,+resetcar_steerangle) #calculate steering direction for next autoreset
2025-03-22 15:13:02 +01:00
steer_direction+=constrain(steer_direction_aim-steer_direction,max_steering_change*delta,-max_steering_change*delta) #smooth steering. towards steer_direction_aim
2025-01-14 19:01:07 +01:00
$sprite_steeringwheel.rotation=steer_direction
2024-11-10 20:49:52 +01:00
2024-11-03 19:16:30 +01: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 20:31:11 +01:00
2024-11-03 19:16:30 +01:00
var d = new_heading.dot(velocity.normalized())
2024-11-10 20:31:11 +01:00
2024-11-03 19:16:30 +01:00
if d > 0:
velocity = velocity.lerp(new_heading * velocity.length(), traction)
2024-11-11 17:34:23 +01:00
if d<0.85 and velocity.length()>slip_speed and applied_engine_power>engine_power/2:
2024-11-10 20:31:11 +01:00
sfx.sliding(velocity.length())
2024-11-11 17:34:23 +01:00
$PositionFrontLeftTire.setSliding(true)
$PositionFrontRightTire.setSliding(true)
2024-11-10 20:49:52 +01:00
elif d>0.98: #keep trails a bit longer
2024-11-11 17:34:23 +01:00
$PositionFrontLeftTire.setSliding(false)
$PositionFrontRightTire.setSliding(false)
2024-11-10 20:31:11 +01:00
2024-11-03 19:16:30 +01:00
elif d<0:
velocity = - new_heading * min(velocity.length(),max_speed_reverse)
rotation = new_heading.angle()
2024-11-09 14:27:34 +01:00
func getRound():
var i=getNextCPindex()/checkpoints.size()
2025-03-22 16:01:34 +01:00
if getNextCPindex()==-1: #all checkpoints collected
2025-03-22 22:04:17 +01:00
return -1
2024-11-09 14:27:34 +01:00
return i
2024-11-03 19:16:30 +01:00
2024-11-10 01:06:25 +01:00
2024-11-03 19:16:30 +01:00
func check_markers():
if ray_cast_car.is_colliding():
#print("Marker: "+str(ray_cast_car.get_collider()))
2024-11-05 08:18:14 +01: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 14:27:34 +01:00
$label_round.showRounds(getRound())
2024-11-05 08:18:14 +01:00
#print("Player "+str(playerid)+" drove through Finish")
2024-11-09 14:27:34 +01:00
if getNextCPindex()==-1 and finalTime==-1: #all checkpoints have times and did not finish = Finished
2024-11-05 08:18:14 +01:00
print("Player "+str(playerid)+" Finished")
print("Final Time: "+str(Gamestate.getTimeElapsed()))
running=false
finalTime=Gamestate.getTimeElapsed()
car_finished.emit(playerid,finalTime)
2025-03-22 15:53:00 +01:00
no_input_timer.stop() #avoid removing player after finished
elif rcc_collidername.begins_with("area_cp"):
var nextcp_i=getNextCPindex()
2024-11-05 08:18:14 +01:00
var checkpoint_i=checkpoints.find(rcc_collidername)
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()
2025-01-14 23:57:26 +01:00
car_on_checkpoint.emit(playerid,checkpointtimes,nextcp_i)
2024-11-10 01:06:25 +01:00
#print("Player "+str(playerid)+" Checkpoint "+str(ray_cast_car.get_collider().name))
#print("New CP array "+str(checkpointtimes))
2024-11-03 19:16:30 +01: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 21:56:07 +01: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 21:56:07 +01:00
func _on_collision_enable_timer_timeout() -> void:
collision_shape.disabled=false
2025-03-22 15:53:00 +01:00
func _on_no_input_timer_timeout() -> void:
no_input_timeout_flag=true
running=false