From 1ea89fb2c9455555984964fd46f4b88f4ec2f226 Mon Sep 17 00:00:00 2001 From: sergeypdev Date: Fri, 2 May 2025 21:11:48 +0400 Subject: [PATCH] Improve engine friction, add a rev limiter --- assets/tyre_longitudinal.csv | 2 +- game/game.odin | 8 +++-- game/physics/debug.odin | 2 +- game/physics/scene.odin | 53 +++++++++++++++++------------ game/physics/simulation.odin | 65 +++++++++++++++++++++--------------- 5 files changed, 78 insertions(+), 52 deletions(-) diff --git a/assets/tyre_longitudinal.csv b/assets/tyre_longitudinal.csv index 30a19e7..8496077 100644 --- a/assets/tyre_longitudinal.csv +++ b/assets/tyre_longitudinal.csv @@ -1,6 +1,6 @@ b 1.6 --80 +-120 1100 0 300 diff --git a/game/game.odin b/game/game.odin index 54bde23..2b08810 100644 --- a/game/game.odin +++ b/game/game.odin @@ -434,13 +434,15 @@ update_runtime_world :: proc(runtime_world: ^Runtime_World, dt: f32) { "assets/ae86_rpm_torque.csv", ), lowest_rpm = 1100, - inertia = 0.264, - internal_friction = 0.00, + rev_limit_rpm = 7800, + rev_limit_interval = 0.025, + inertia = 0.264 * 0.5, + internal_friction = 0.01, gear_ratios = []f32{3.48, 3.587, 2.022, 1.384, 1, 0.861}, axle = physics.Drive_Axle_Config { wheels = {wheel_rl, wheel_rr}, wheel_count = 2, - diff_type = .Open, + diff_type = .Fixed, final_drive_ratio = 4.1, }, }, diff --git a/game/physics/debug.odin b/game/physics/debug.odin index 9569933..0e12ff9 100644 --- a/game/physics/debug.odin +++ b/game/physics/debug.odin @@ -151,7 +151,7 @@ draw_debug_ui :: proc(ctx: ^ui.Context, scene: ^Scene, config: Solver_Config) { sim_state := get_sim_state(scene) - active_wheels := []int{2, 3} + active_wheels := []int{} w, h: i32 = 200, 200 diff --git a/game/physics/scene.odin b/game/physics/scene.odin index 98088bc..1f74e2b 100644 --- a/game/physics/scene.odin +++ b/game/physics/scene.odin @@ -217,39 +217,46 @@ Gear_Ratios_Handle :: distinct spanpool.Handle // This actually handles everything, engine, transmission, differential, etc. It's easier to keep it in one place Engine :: struct { - alive: bool, + alive: bool, // Engine Params - rpm_torque_curve: Engine_Curve_Handle, - lowest_rpm: f32, - inertia: f32, - internal_friction: f32, + rpm_torque_curve: Engine_Curve_Handle, + lowest_rpm: f32, + // Rpm when rev limiter activates + rev_limit_rpm: f32, + // Time in seconds for how long rev limiter cuts the throttle + rev_limit_interval: f32, + inertia: f32, + internal_friction: f32, // Transmission Params // 0 - reverse, 1 - first, etc. - gear_ratios: Gear_Ratios_Handle, - axle: Drive_Axle, + gear_ratios: Gear_Ratios_Handle, + axle: Drive_Axle, // Engine State // Angular velocity, omega - q: f32, - w: f32, + q: f32, + w: f32, + + // Reset to -rev_limit_interval when rpm exceeds rev_limit_rpm, has to be >= 0 for throttle to work + rev_limit_time: f32, // Impulse applied when engine is stalling (rpm < lowest_rpm) - unstall_impulse: f32, + unstall_impulse: f32, // Friction that makes rpm go down when you're not accelerating - friction_impulse: f32, + friction_impulse: f32, // Impulse applied from releasing throttle - throttle_impulse: f32, + throttle_impulse: f32, // Transmission State // -1 - reeverse, 0 - neutral, 1 - first, etc. - gear: i32, + gear: i32, // Controls - throttle: f32, + throttle: f32, // Free list - next_plus_one: i32, + next_plus_one: i32, } // Index plus one, so handle 0 maps to invalid body @@ -378,15 +385,17 @@ Drive_Axle_Config :: struct { } Engine_Config :: struct { - rpm_torque_curve: [][2]f32, - lowest_rpm: f32, - inertia: f32, - internal_friction: f32, + rpm_torque_curve: [][2]f32, + lowest_rpm: f32, + rev_limit_rpm: f32, + rev_limit_interval: f32, + inertia: f32, + internal_friction: f32, // Transmission Params // 0 - reverse, 1 - first, etc. - gear_ratios: []f32, - axle: Drive_Axle_Config, + gear_ratios: []f32, + axle: Drive_Axle_Config, } calculate_body_params_from_config :: proc( @@ -500,6 +509,8 @@ update_engine_from_config :: proc( engine.gear_ratios = add_gear_ratios(sim_state, config.gear_ratios) engine.lowest_rpm = config.lowest_rpm + engine.rev_limit_rpm = config.rev_limit_rpm + engine.rev_limit_interval = config.rev_limit_interval engine.inertia = config.inertia engine.internal_friction = config.internal_friction diff --git a/game/physics/simulation.odin b/game/physics/simulation.odin index 7359627..10c2e3c 100644 --- a/game/physics/simulation.odin +++ b/game/physics/simulation.odin @@ -571,36 +571,48 @@ pgs_solve_engines :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32, engine.w += applied_impulse / engine.inertia } + rpm := angular_velocity_to_rpm(engine.w) + throttle := engine.throttle + + if engine.rev_limit_time < 0.0 { + engine.rev_limit_time += dt + throttle = 0 + } else if (rpm >= engine.rev_limit_rpm) { + engine.rev_limit_time = -engine.rev_limit_interval + throttle = 0 + } + // Throttle { - rpm := angular_velocity_to_rpm(engine.w) - torque: f32 + { + torque: f32 - idx, _ := slice.binary_search_by( - rpm_torque_curve, - rpm, - proc(a: [2]f32, k: f32) -> slice.Ordering { - return slice.cmp(a[0], k) - }, - ) + idx, _ := slice.binary_search_by( + rpm_torque_curve, + rpm, + proc(a: [2]f32, k: f32) -> slice.Ordering { + return slice.cmp(a[0], k) + }, + ) - if idx > 0 && idx < len(rpm_torque_curve) - 1 { - cur_point := rpm_torque_curve[idx] - next_point := rpm_torque_curve[idx + 1] - rpm_diff := next_point[0] - cur_point[0] - alpha := (rpm - cur_point[0]) / rpm_diff + if idx > 0 && idx < len(rpm_torque_curve) - 1 { + cur_point := rpm_torque_curve[idx] + next_point := rpm_torque_curve[idx + 1] + rpm_diff := next_point[0] - cur_point[0] + alpha := (rpm - cur_point[0]) / rpm_diff - torque = math.lerp(cur_point[1], next_point[1], alpha) - } else { - torque = rpm_torque_curve[math.clamp(idx, 0, len(rpm_torque_curve) - 1)][1] + torque = math.lerp(cur_point[1], next_point[1], alpha) + } else { + torque = rpm_torque_curve[math.clamp(idx, 0, len(rpm_torque_curve) - 1)][1] + } + + // log.debugf("torque: %v Nm", torque) + torque *= throttle + + engine.w += (torque / engine.inertia) * dt } - - // log.debugf("torque: %v Nm", torque) - torque *= engine.throttle - - engine.w += (torque / engine.inertia) * dt } // Internal Friction @@ -610,13 +622,14 @@ pgs_solve_engines :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32, inv_w := engine.inertia friction := - math.pow( - max(engine.w - rpm_to_angular_velocity(engine.lowest_rpm), 0) * 0.002, - 4, - ) * + math.pow(max(engine.w - rpm_to_angular_velocity(engine.lowest_rpm), 0), 2) * + 0.0001 * engine.internal_friction + engine.internal_friction + // Not physically based, but we assume when throttle is open there is no friction + friction *= (1.0 - throttle) + incremental_impulse := inv_w * delta_omega new_total_impulse := math.clamp( engine.friction_impulse + incremental_impulse,