123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- extends RigidBody
- class_name VehicleController
- signal query_driver(driver)
- signal update_motor_sound(vehicle_controller)
- export var force_curve: Curve
- export var force_max_value: float = 20000
- export var vmax: float = 20
- export var vmin_grip: float = 2
- export var vmax_wheel_spin: float = 6
- export var omega_curve: Curve
- export var omega_max: float = 0.6
- export var spring_distance_max: float = 0.14
- # Hard spring.
- export var spring_constant: float = 42214
- export var spring_damping: float = 7904
- onready var steering_controller: Node = get_node("SteeringController")
- onready var omega_controller: Node = get_node("OmegaController")
- onready var fl: WheelController = get_node("FL")
- onready var fr: WheelController = get_node("FR")
- onready var rl: WheelController = get_node("RL")
- onready var rr: WheelController = get_node("RR")
- var driver = Driver.new()
- var vehicle_state = VehicleState.new()
- var wheel_state = WheelState.new()
- var drift_angle_max_degree: float = 1
- var brake_value: float = 20000
- var has_grip: bool = false
- var grip_force: float
- var driving_force_position: Vector3 # The force to move the car is applied at this position (local to the car).
- var offset_drive: Vector3
- var omega_reference: float
- var wheelbase: float
- var turn_radius: float
- var velocity_measurement: float
- var acceleration_measurement: float
- var acceleration_force: float
- func _ready():
- wheelbase = rl.transform.origin.z - fl.transform.origin.z
- driving_force_position = Vector3(0, -rl.wheel_radius, wheelbase * 0.5) # At the rear axis and at the contact point of the wheel.
- fl.init_suspension(weight / 4, spring_distance_max, spring_constant, spring_damping)
- fr.init_suspension(weight / 4, spring_distance_max, spring_constant, spring_damping)
- rl.init_suspension(weight / 4, spring_distance_max, spring_constant, spring_damping)
- rr.init_suspension(weight / 4, spring_distance_max, spring_constant, spring_damping)
- func _physics_process(delta: float):
- var torque: float
- var torque_vector: Vector3
- var vehicle_velocity_magnitude: float = linear_velocity.length()
- var vehicle_rotation = Quat(transform.basis)
- var on_ground = update_suspension(delta, vehicle_rotation)
- if !on_ground:
- has_grip = false
- grip_force = steering_controller.reset()
- omega_controller.reset()
- return
- offset_drive = vehicle_rotation * driving_force_position
- vehicle_state.update(vehicle_rotation, linear_velocity)
- acceleration_measurement = (vehicle_velocity_magnitude - velocity_measurement) / delta
- velocity_measurement = vehicle_velocity_magnitude
- var steering: float = vehicle_state.drift_angle_measurement
- turn_radius = get_turn_radius(vehicle_velocity_magnitude)
- if vehicle_velocity_magnitude < vmin_grip:
- has_grip = false
- else:
- if driver.did_accelerate:
- if vehicle_state.drift_angle_measurement > deg2rad(-drift_angle_max_degree) && vehicle_state.drift_angle_measurement < deg2rad(drift_angle_max_degree):
- has_grip = true
- if has_grip:
- adjust_steering(vehicle_rotation)
- steering = asin(wheelbase / turn_radius)
- else:
- grip_force = steering_controller.reset()
- if driver.did_accelerate:
- accelerate()
- if vehicle_state.velocity_rear_axis < vmax_wheel_spin:
- vehicle_state.velocity_rear_axis = vmax_wheel_spin
- else: if driver.did_reverse:
- reverse()
- else:
- acceleration_force = 0
- if driver.did_brake:
- brake(vehicle_velocity_magnitude)
- control_omega(delta, vehicle_velocity_magnitude)
- torque = omega_controller.adjust(omega_reference, angular_velocity.y)
- torque_vector = vehicle_rotation * Vector3.UP * torque
- add_torque(torque_vector)
- emit_signal("query_driver", driver)
- emit_signal("update_motor_sound", self)
- wheel_state.update(delta, vehicle_state.velocity_front_axis, vehicle_state.velocity_rear_axis)
- update_wheel_rotation(delta, steering)
- func adjust_steering(vehicle_rotation: Quat):
- grip_force = steering_controller.adjust(0, vehicle_state.velocity_sideways)
- var direction = vehicle_rotation * Vector3.LEFT
- var grip_force_vector: Vector3 = direction * grip_force
- add_force(grip_force_vector, offset_drive)
- func control_omega(delta: float, velocity: float):
- var arg: float = velocity / vmax
- var extent: float = omega_curve.interpolate(arg)
- var direction: float
- if driver.did_steer_left:
- direction = 1
- if driver.did_steer_right:
- direction = -1
- if vehicle_state.velocity_rear_axis > velocity: # Obstacle detected ?
- extent = 1
- else:
- if !vehicle_state.vehicle_moving_forward:
- direction = -direction
- if driver.did_steer:
- omega_reference = extent * lerp(omega_reference, omega_max * direction, 2 * delta)
- else:
- omega_reference = lerp(omega_reference, 0, 2 * delta)
- func update_wheel_rotation(delta: float, steering: float):
- fl.rotate_wheel(delta, wheel_state.total_movement_front, steering)
- fr.rotate_wheel(delta, wheel_state.total_movement_front, steering)
- rl.rotate_wheel(delta, wheel_state.total_movement_rear, 0)
- rr.rotate_wheel(delta, wheel_state.total_movement_rear, 0)
- func accelerate():
- acceleration_force = force_max_value * force_curve.interpolate(velocity_measurement / vmax)
- var force_vector: Vector3 = vehicle_state.vehicle_direction * acceleration_force
- add_force(force_vector, offset_drive)
- func reverse():
- var velocity_max_reverse: float = 7
- if velocity_measurement < velocity_max_reverse:
- acceleration_force = force_max_value * 0.5
- var force_vector: Vector3 = vehicle_state.vehicle_direction * acceleration_force
- add_force(-force_vector, offset_drive)
- func brake(vehicle_velocity_magnitude: float):
- var brake_force: Vector3
- vehicle_state.brake()
- omega_reference = 0
- if vehicle_velocity_magnitude < 0.5:
- brake_force = brake_value * vehicle_velocity_magnitude * vehicle_state.velocity_direction
- else:
- brake_force = brake_value * vehicle_state.velocity_direction
- add_force(-brake_force, offset_drive)
- func get_grip() -> bool:
- return has_grip
- func get_grip_force() -> float:
- return grip_force
- func get_velocity_rear_axis() -> float:
- return vehicle_state.velocity_rear_axis
- func get_omega_reference() -> float:
- return omega_reference
- func get_turn_radius(vehicle_velocity_magnitude: float) -> float:
- var radius: float
- var radius_min: float = 1.1 * wheelbase
- if angular_velocity.y > 0:
- radius = min(999, vehicle_velocity_magnitude / angular_velocity.y)
- if radius < radius_min:
- radius = radius_min
- else: if angular_velocity.y < 0:
- radius = max(-999, vehicle_velocity_magnitude / angular_velocity.y)
- if radius > -radius_min:
- radius = -radius_min
- else:
- radius = 999
- if radius > -radius_min && radius < radius_min:
- print_debug("turn radius?")
- return radius
- func get_vmax() -> float:
- return vmax
- func get_acceleration_force_relative() -> float:
- return acceleration_force / force_max_value
- func update_suspension(delta: float, vehicle_rotation: Quat) -> bool:
- var contact_front: bool
- var contact_rear: bool
- contact_front = fl.add_spring_force(delta, self, vehicle_rotation)
- contact_front = fr.add_spring_force(delta, self, vehicle_rotation) && contact_front
- contact_rear = rl.add_spring_force(delta, self, vehicle_rotation)
- contact_rear = rr.add_spring_force(delta, self, vehicle_rotation) && contact_rear
- return contact_front && contact_rear
- class VehicleState:
- var velocity_front_axis: float
- var velocity_rear_axis: float
- var velocity_sideways: float
- var vehicle_direction: Vector3
- var velocity_direction: Vector3
- var vehicle_moving_forward: bool
- var drift_angle_measurement: float
- func update(vehicle_rotation: Quat, vehicle_velocity: Vector3):
- var vehicle_direction_sideways: Vector3 = vehicle_rotation * Vector3.LEFT
- vehicle_direction = vehicle_rotation * Vector3.FORWARD
- velocity_front_axis = vehicle_velocity.length()
- velocity_rear_axis = vehicle_velocity.dot(vehicle_direction)
- velocity_sideways = vehicle_velocity.dot(vehicle_direction_sideways)
- if velocity_front_axis > 0:
- velocity_direction = vehicle_velocity.normalized()
- else:
- velocity_direction = vehicle_direction
- vehicle_moving_forward = vehicle_direction.dot(velocity_direction) > 0
- var cross_product: Vector3
- if vehicle_moving_forward:
- cross_product = vehicle_direction.cross(velocity_direction)
- else:
- cross_product = velocity_direction.cross(vehicle_direction)
- if velocity_front_axis > 0.1:
- drift_angle_measurement = asin(cross_product.y)
- if !vehicle_moving_forward:
- velocity_front_axis = -velocity_front_axis
-
- func brake():
- velocity_front_axis = 0
- velocity_rear_axis = 0
- class WheelState:
- var total_movement_front: float
- var total_movement_rear: float
- func update(delta: float, velocity_front_axis: float, velocity_rear_axis: float):
- total_movement_front += delta * velocity_front_axis
- total_movement_rear += delta * velocity_rear_axis
- class Driver:
- var did_steer: bool
- var did_steer_left: bool
- var did_steer_right: bool
- var did_accelerate: bool
- var did_brake: bool
- var did_reverse: bool
- func start_query():
- did_steer = false
- did_steer_left = false
- did_steer_right = false
- did_accelerate = false
- did_reverse = false
- did_brake = false
- func turn_left():
- did_steer_left = true
- did_steer = true
- func turn_right():
- did_steer_right = true
- did_steer = true
- func accelerate():
- did_accelerate = true
- func brake():
- did_brake = true
- func reverse():
- did_reverse = true
|