[TEST] Controller Tweaks - Analog Stick to Steering Wheel Script

Make your game even better
Post Reply
a1337cookie
Posts: 132
Joined: June 14th, 2021, 2:11 am

[TEST] Controller Tweaks - Analog Stick to Steering Wheel Script

Post by a1337cookie » July 22nd, 2024, 1:58 am

This setup allows an analog stick to act like a steering wheel, for smoother and more precise control when driving with a controller. Push the stick against the rim to "grab" the wheel, then spin it against the rim to steer. Degrees of rotation and deadzone values are adjustable.

It uses a custom Joystick Gremlin plugin to translate controller inputs to a vJoy virtual controller. ATS or ETS2 can then use the vJoy controller as the steering input. Should be compatible with most controllers, though XBox One controllers may require additional steps.

This is a remake of an AutoHotkey script I made years ago that I never made a guide for. The setup was very convoluted, requiring multiple other programs and some guessing and checking with IDs. The new solution should be more efficient and simpler to use.

Below are some temporary instructions, once it's been tested more and I have time, I will add pictures/videos to make it easier to follow.
  • Install Joystick Gremlin and vJoy, links: https://whitemagic.github.io/JoystickGremlin/, https://github.com/shauleiz/vJoy/
  • Use Configure vJoy to create a virtual controller, it just needs at least one axis
  • Copy code below to new text file, and rename the extension from .txt to .py
  • Open Joystick Gremlin as admin and add the .py file as a plugin under the plugins tab
  • Press the gear icon next to "Default" to configure the parameters (hover over each option for tooltips)
  • Press Activate and check if it's working (use vJoy Monitor or Input Viewer tool in Joystick Gremlin)
  • If it works, open the game and add the vJoy device as an input in control settings
  • Bind the vJoy axis as steering in control settings (if it keeps binding to your controller, temporarily remove your controller from the input list)
  • Make sure you use wheel mode, and set visual steering angle to what you want in gameplay options

Code: Select all

import math
import gremlin
from gremlin.user_plugin import *
from vjoy.vjoy import AxisName


# Configurable variables, adjust using GUI
mode = ModeVariable(
        "Mode",
        "Mode in which to use these settings"
)
phys_x = PhysicalInputVariable(
        "Physical X Axis",
        "Physical stick X axis input",
        [gremlin.common.InputType.JoystickAxis]
)
phys_y = PhysicalInputVariable(
        "Physical Y Axis",
        "Physical stick Y axis input",
        [gremlin.common.InputType.JoystickAxis]
)
vjoy_axis = VirtualInputVariable(
        "vJoy Axis",
        "vJoy wheel axis output",
        [gremlin.common.InputType.JoystickAxis]
)
rec_btn = PhysicalInputVariable(
        "Recenter Button",
        "Press to reset wheel to center",
        [gremlin.common.InputType.JoystickButton]
)
inner_dz = IntegerVariable(
        "Inner Deadzone",
        "Size of the inner deadzone",
        80,
        1,
        100
)
max_rot = IntegerVariable(
        "Max Degrees of Rotation",
        "Degrees of rotations from lock to lock",
        1800,
        1,
        99999
)
is_invert = BoolVariable(
        "Invert Rotation",
        "Whether to reverse the rotation direction",
        False
)


# Internal variables
in_x = 0.0
in_y = 0.0
is_steering = False
prev_angle = 0.0
out_axis = 0.0


# Decorators
dec_x = phys_x.create_decorator(mode.value)
dec_y = phys_y.create_decorator(mode.value)
dec_rec = rec_btn.create_decorator(mode.value)


# Main function
def set_axis(vjoy):
    global in_x
    global in_y
    global is_steering
    global prev_angle
    global out_axis
    
    # Only affect steering if not in deadzone
    if math.hypot(in_x, in_y) >= (inner_dz.value/100):
        
        # Current stick angle
        curr_angle = math.atan2(in_y, in_x)
        
        # Main steering logic
        if is_steering:
            
            # Difference from previous angle moving counterclockwise
            diff_angle_ccw = (curr_angle - prev_angle + 2*math.pi) % (2*math.pi)
            
            # Convert to positive or negative wheel rotation
            steer_angle = (2*math.pi - diff_angle_ccw) if diff_angle_ccw > math.pi else -diff_angle_ccw
            
            # Increase or decrease output axis according to amount of rotation
            out_axis += -((bool(is_invert.value) - 0.5) * 2) * steer_angle / (2*math.pi) / (max_rot.value/720)
            
            # Update vJoy axis to new value, clamped between -1 and 1
            vjoy[vjoy_axis.vjoy_id].axis(vjoy_axis.input_id).value = max(-1.0, min(out_axis, 1.0))
            
        else:
            is_steering = True
            
        prev_angle = curr_angle
    
    else:
        is_steering = False
        
        # When steering stopped, bring axis back in bounds if needed
        out_axis = max(-1.0, min(out_axis, 1.0))


# When stick input received, update internal variables and run main function
@dec_x.axis(phys_x.input_id)
def axis1(event, vjoy):
    global in_x
    in_x = event.value
    set_axis(vjoy)

@dec_y.axis(phys_y.input_id)
def axis2(event, vjoy):
    global in_y
    in_y = -event.value
    set_axis(vjoy)
    

# Recenter function
@dec_rec.button(rec_btn.input_id)
def recenter(event, vjoy):
    global out_axis
    if event.is_pressed: 
        out_axis = 0.0
        vjoy[vjoy_axis.vjoy_id].axis(vjoy_axis.input_id).value = out_axis


# Press 1 to print variable values to log for debugging, uncomment to use
# @gremlin.input_devices.keyboard("1", mode.value)
# def debug(event):
    # global in_x
    # global in_y
    # global is_steering
    # global prev_angle
    # global out_axis
    # if event.is_pressed:
        # gremlin.util.log(" ".join(["in_x:", str(in_x), "in_y:", str(in_y), "prev_angle", str(math.degrees(prev_angle)), "out_axis", str(out_axis)]))
Read the Joystick Gremlin documentation page for more info. You can do lots of other stuff with rebinding if you'd like.

XBox One controllers may have an issue where Joystick Gremlin cannot detect inputs when not in focus. For me, the issue went away after restarting and unplugging/plugging the controller back in. If not, you may have to use UCR to remap the physical controller to a second vJoy controller, then use that as the input in Joystick Gremlin. Link: https://github.com/Snoothy/UCR

To get this working in other games, you may have to bind everything to the vJoy controller if the game doesn't support simultaneous input from multiple devices. Then you'll probably need something like HIDhide to hide your physical controller from the game.

If you try it, let me know how it goes, I'm open to feedback and suggestions.
a1337cookie
Posts: 132
Joined: June 14th, 2021, 2:11 am

Re: [TEST] Controller Tweaks - Analog Stick to Steering Wheel Script

Post by a1337cookie » July 22nd, 2024, 2:00 am

Reserved for changelog
NNG
Posts: 3
Joined: July 27th, 2024, 12:04 pm

Re: [TEST] Controller Tweaks - Analog Stick to Steering Wheel Script

Post by NNG » July 27th, 2024, 12:28 pm

Thanks this works for me, it is just that the xinput driver updates with hid compatible one, and then i dont need to use ucr at all, i am using it anyways because the game i play, can, take 2 devices' inputs at a time, but i dont have the option to choose, it takes xbox controller inputs, so i did remap and hide the xb1 controller.
after playing for abt 1 to 2 hrs, i find it difficult to steer, even with adjusting the steering wheel rotation, the most weirdest difficult part i cant get over is the steering wheel resetting, is there a way when i dont move the stick or if it is in the center, the game physics control the steering and when i give input i get back the control, just like how steering wheel is used in games.
Is there a way to emulate xb controller as sm steering wheel, i also dont know about how the steering wheels work, apart from the fact that they send rotation angle or data.
a1337cookie
Posts: 132
Joined: June 14th, 2021, 2:11 am

Re: [TEST] Controller Tweaks - Analog Stick to Steering Wheel Script

Post by a1337cookie » July 27th, 2024, 9:32 pm

@NNG - For auto centering, I can add a configurable smooth recenter option to the script. This will just gradually center the steering when you're not providing input. This is similar to how some low budget wheels work, they just spring back to the center.

What you're describing with the game physics controlling the wheel is force feedback (FFB). In FFB wheels, a motor physically turns the wheel according to what the game tells it to do.

I have no idea how I would retrieve FFB data and use it in the script, and even if I could, there would be significant issues. There would be no physical feedback, so it may feel very disjointed.

I'll look into it more and update the post when I have something.
NNG
Posts: 3
Joined: July 27th, 2024, 12:04 pm

Re: [TEST] Controller Tweaks - Analog Stick to Steering Wheel Script

Post by NNG » July 28th, 2024, 12:09 pm

I dont think smooth recenter is much beneficial, i understood about the ffb part, i ll keep searching about how it works and all.
Also i am able to be very accurate with the steering script ( like get very small and good adjustments, also there is very less delay between the input and output, big ups), just that i lose the sense of steering rotation at some point and the point of contact in an actual mini steering wheel doesnt change, our finger tip is always at the farthest point in that steering angle, but with joystick it is just weird depends on which part u start to grip on the thumbgrip, i ll get used to it, probably a bit weird learning curve because of this, all self problems, sript is absolutely fine, so i ll just have to put more time.
physically reducing outer dead zone would also be very great, then i can comfortably hold at the center of the thumbstick, and not effect or be worried about the other inputs with the the rest of the fingers, like braking and clutch on the left hand side.
a1337cookie
Posts: 132
Joined: June 14th, 2021, 2:11 am

Re: [TEST] Controller Tweaks - Analog Stick to Steering Wheel Script

Post by a1337cookie » July 28th, 2024, 9:27 pm

@NNG - Yeah, definitely quite a learning curve to it. I purposefully let you "grab" the wheel from any stick angle, so you can steer from whichever position is most comfortable. It's like how you can put your hand wherever you want on a real wheel.

If you frequently lose track of the steering angle, you can try to always start steering by moving the stick up, then don't let go until you're back to center. That way, the stick is always pointing at the top of the steering wheel, which is more intuitive.

I could maybe add something to the script that makes sure when you start steering by moving up, the steering angle aligns perfectly to the stick angle.
NNG
Posts: 3
Joined: July 27th, 2024, 12:04 pm

Re: [TEST] Controller Tweaks - Analog Stick to Steering Wheel Script

Post by NNG » July 31st, 2024, 8:36 pm

True True,
I dont think any changes are required to the script, i just have to rememeber some things in my head before playing using the script.
If i can feel or think of some good additions to the script as i keep playing, i'll let you know. : )
Post Reply