stesim / godot-vehicle-sandbox

Vehicle simulation in Godot Engine
MIT License
11 stars 2 forks source link

Simplified Reverse help #3

Open brickyt opened 2 months ago

brickyt commented 2 months ago

Hello! Love this vehicle controller but I'm trying to simplify the reverse so that the player doesn't have to put the vehicle in reverse gear and then press accelerate.

I'd appreciate some guidance, but understand if this is out of scope or not of interest to you.

Current Behavior

Currently, the car uses the accelerate input to move both forward and backward, depending on the gear.

Desired Functionality

I want to implement the following:

  1. When the car is stationary and the brake is held down, it should automatically switch to reverse gear.
  2. While in this reverse mode, the brake input should act as the accelerator for reverse motion.
  3. This should happen without releasing the brake button.
  4. The car should exit reverse mode once it's moving forward in a positive gear.
  5. All other driving mechanics should remain unchanged.

What I've Tried

I've attempted to add new variables (is_in_reverse and brake_held_while_stationary) and a function to check for reverse activation. Here's a simplified version of what I tried:

func check_reverse_activation(delta: float) -> void:
    var current_speed = abs(get_speed())
    var brake_strength = Input.get_action_strength("brake")

    if brake_strength > 0 and current_speed < 0.1:
        if not brake_held_while_stationary:
            brake_held_while_stationary = true
            is_in_reverse = true
            if transmission != null:
                transmission.gear = -1  # Set reverse gear
    else:
        brake_held_while_stationary = false

    # Exit reverse mode if moving forward in a positive gear
    if not is_zero_approx(get_speed()) and get_speed() > 0 and transmission.gear > 0:
        is_in_reverse = false

I then modified the input handling to use the brake input as acceleration when in reverse mode.

Issues Encountered

  1. I'm unsure if I'm correctly interfacing with the Transmission class.
  2. The car doesn't switch to reverse when brake is held down

Questions

  1. Is there a more elegant way to implement this reverse functionality?
  2. How can I ensure this doesn't interfere with the existing driving mechanics?
  3. Are there any specific methods or properties in the Transmission class I should be using for gear management?

Any insights or suggestions would be greatly appreciated. Thank you in advance for your help!

stesim commented 1 month ago

Hi and sorry for the late reply, I'm kinda swamped at the moment.

To be honest, I'm too tired to figure out how your approach works (5:00 AM here), but after a quick glance I don't see anything that would interfere with other mechanics, so if it does what you want you should be fine.

Since it's been a while since I've last worked on the project, here is a suggestion that may or may not work. :smiley:

Looking at your problem description and the code, there's generally two things that need to be implemented:

It may not be the cleanest solution, but I think a somewhat simple and non-intrusive approach would be to handle the two things separately.

Swapping Inputs

The inputs can be swapped directly when reading them in the Vehicle class.

func _update_inputs(delta : float) -> void:
    _engine_input = move_toward(_engine_input, Input.get_action_strength(&"accelerate"), delta * input_speed)
    _brake_input = move_toward(_brake_input, Input.get_action_strength(&"brake"), delta * input_speed)
    # swap acceleration and reverse inputs when in reverse
    if transmission != null and transmission.gear < 0:
        _swap_acceleration_and_brake_inputs()
   # ...

func _swap_acceleration_and_brake_inputs() -> void:
    var temporary = _engine_input
    _engine_input = _brake_input
    _brake_input = temporary

This should not interfere with other parts of the code since the remaining code should rely on the computed input values rather than the actual action strengths.

Automatic Shifts

Having swapped the inputs already, it becomes relatively straightforward to determine when to switch between forward and reverse since the _brake_input value always represents actual braking, regardless of the selected gear.

# NOTE: in the context of this function, 'brake' and 'accelerator' refer to the respective effects in the
#       current gear rather than the input actions, e.g. 'accelerator' actually maps to 'brake' input in reverse
func _check_reverse_activation() -> void:
    # do not shift if a shift is already in progress or the accelerator is pressed, even if the brake is held
    if transmission == null or transmission.is_shifting() or not is_zero_approx(_engine_input):
        return

    var gear_direction := -1 if transmission.gear < 0 else +1
    var is_stopped_or_moving_in_opposite_direction := gear_direction * get_speed() < 0.01
    var brake_is_held := not is_zero_approx(_brake_input)
    if is_stopped_or_moving_in_opposite_direction and brake_is_held:
        # shift to first gear in opposite direction
        transmission.shift(+1 if transmission.gear < 0 else -1)
        # swap the inputs to avoid braking in the current frame
        _swap_acceleration_and_brake_inputs()

Now, I'm not sure myself what the best place to perform this check is, but calling it in Vehicle._process() right after the call to _update_inputs() should be fine.

Hopefully, this accomplishes what you're after. I don't remember how the transmission automation works exactly, just that it was a mess, but let's hope that it can cope with the additional shift logic. Otherwise, there should not be any interference with the remaining driving mechanics, since this approach basically emulates the user performing the required actions (switching inputs, shifting) at the right time.

(Disclaimer: The code snippets were typed directly in GitHub, since I don't have a compatible Godot version at the moment to try it out, so there may be typos and/or other errors.)