The online racing simulator
vJoy + FreePIE for scripting virtual controllers
Some people (including me) have asked about creating virtual controllers and programming some unusual controller behavior here. I've been using vJoy and FreePIE recently and the solution is quite convenient. You can quickly script on a virtual controller to convert keyborad/mouse/whatever input, remap axes, do conditional logic or make other fancy stuff.

vJoy is a PPJoy-like driver that can create virtual game controllers. This driver is signed, so unlike PPJoy you can install it on 64bit Win7/8 without using test mode. It does not have any input generation function, so you'll need a "feeder" program do make it functional. This is where FreePIE comes in. The FreePIE Programmable Input Emulator is something that allows you to emulate different kinds of inputs with Python scripts. It supports vJoy natively. Just install both, create at least one virtual controller in vJoy and fire up FreePIE. The documentation is lacking tbh but it's still easy to get started.

This is what I use on my laptop for driving games that do not support mouse control. It also gives me independent steering sensitivity setting in LFS.

from System import Int16

if starting:
system.setThreadTiming(TimingTypes.HighresSystemTimer)
system.threadExecutionInterval = 5 # loop delay
max = Int16.MaxValue*0.5+2
min = -Int16.MaxValue*0.5+1
x = 0 # steering
senX = 3
y = min # throttle
z = min # brake
rx = min # clutch
ry = min # handbrake
senP = 1500

x += mouse.deltaX * senX
y += (1 if keyboard.getKeyDown(Key.D) else -1) * senP
z += (1 if keyboard.getKeyDown(Key.S) else -1) * senP
rx += (1 if keyboard.getKeyDown(Key.A) else -1) * senP
ry += (1 if mouse.middleButton else -1) * senP

if keyboard.getKeyDown(Key.LeftControl) and mouse.middleButton: # steering centering
x = 0

if (x > max):
x = max
elif (x < min):
x = min

if (y > max):
y = max
elif (y < min):
y = min

if (z > max):
z = max
elif (z < min):
z = min

if (rx > max):
rx = max
elif (rx < min):
rx = min

if (ry > max):
ry = max
elif (ry < min):
ry = min

vJoy[0].x = x
vJoy[0].y = y
vJoy[0].z = z
vJoy[0].rx = rx
vJoy[0].ry = ry
vJoy[0].setButton(0,int(mouse.leftButton))
vJoy[0].setButton(1,int(mouse.rightButton))
#vJoy[0].setButton(2,int(mouse.middleButton))

if mouse.wheelUp: # pit menu +
keyboard.setKeyDown(Key.RightArrow)
else:
keyboard.setKeyUp(Key.RightArrow)

if mouse.wheelDown: # pit menu -
keyboard.setKeyDown(Key.LeftArrow)
else:
keyboard.setKeyUp(Key.LeftArrow)

This one is for tristancliffe who asked for Sequential AND paddle shift script over one year ago. I know it's way too late, but it's still good as a demonstration.

# Using x axis on vJoy device 0 as desired throttle input.
# The real wheel is joystick 0 and throttle input is y axis. Ignition cut is button 10.

from System import Int16

if starting:
system.setThreadTiming(TimingTypes.HighresSystemTimer)
system.threadExecutionInterval = 5 # loop delay
max = Int16.MaxValue*0.5+2
min = -Int16.MaxValue*0.5+1

if joystick[0].getPressed(10):
vJoy[0].x = min # ignition cut
else:
vJoy[0].x = joystick[0].y # normal throttle

If you're still on 32bit/XP and PPJoy does work without test mode, GlovePIE (more mature, but no vJoy support) will work in a similar fashion as FreePIE. You can also ignore FreePIE/GlovePIE and code your own vJoy/PPJoy feeder application if you like.

FFB can not pass through such a setup so don't do it on your wheel steering axis.
I use GlovePIE + PPJoy to create a virtual combined axis from my T500 clutch/gas pedals for flight sims. Brake pedal is acting as a secondary axis, haven't thought of any use for it in flying though.

Here's the GlovePIE script if anyone is after something like this:


///T500 RS clutch/accelerator combined axis for rudder use
///This presumes T500 is the #3 device from top in Game Controllers
///Clutch (Slider0) and accelerator (Rotation RZ) as Analog0 (X)
///Brake pedal (Rotation Y) as Analog1 (Z)

PPJoy1.Analog0 = (.5*Joystick3.slider) - (.5*Joystick3.rz)
PPJoy1.Analog1 = (-Joystick3.y)

Well, if we're comparing PIE scripts.. I use this for AC. Some variations on steering linearity at the bottom.
Quote :// Binds
var.throttle = keyboard.A
var.brake = keyboard.Z
var.clutch = keyboard.X
var.handbrake = keyboard.Q
var.steering = mouse.x
var.shiftup = mouse.LeftButton
var.shiftdown = mouse.RightButton

// Sensitivities
var.throttleincreaserate = 0.1
var.throttledecreaserate = 0.1
var.brakeincreaserate = 0.2
var.brakedecreaserate = 0.05
var.clutchincreaserate = 0.4
var.clutchdecreaserate = 0.2
var.handbrakeincreaserate = 0.4
var.handbrakedecreaserate = 0.4
var.throttleblip = 0.4
var.brakeratemax = 0.8
var.braketrail = 0.1
var.braketrailrate = -0.002
var.throttletrail = -0.6
var.throttlecut = -0.8

// Script

// Throttle
if var.throttle > 0 && ppjoy.Analog0 < 1 then ppjoy.Analog0 += var.throttleincreaserate
if var.throttle == 0 && ppjoy.Analog0 > -1 then ppjoy.Analog0 -= var.throttledecreaserate

// Brake
if var.brake > 0 && var.isBraking != True && ppjoy.Analog1 < var.brakeratemax then ppjoy.Analog1 += var.brakeincreaserate
if ppjoy.Analog1 >= var.brakeratemax then var.isBraking = True
if ppjoy.Analog1 > var.brakeratemax then ppjoy.Analog1 = var.brakeratemax
if var.brake == 0 then var.isBraking = False
// Trail brake
if ppjoy.Analog1 > var.brakeratemax-var.braketrail && var.isBraking == True && var.brake > 0 then
ppjoy.Analog1 += var.braketrailrate
end if
if var.brake == 0 && ppjoy.Analog1 > -1 then ppjoy.Analog1 -= var.brakedecreaserate
// Trail throttle
if var.throttletrail != -1 && var.brake > 0 then ppjoy.Analog0 = var.throttletrail
debug = var.isBraking
// Clutch
if var.clutch > 0 && ppjoy.Analog2 < 1 then ppjoy.Analog2 += var.clutchincreaserate
if var.clutch == 0 && ppjoy.Analog2 > -1 then ppjoy.Analog2 -= var.clutchdecreaserate

// Handbrake
if var.handbrake > 0 && ppjoy.Analog3 < 1 then ppjoy.Analog3 += var.handbrakeincreaserate
if var.handbrake == 0 && ppjoy.Analog3 > -1 then ppjoy.Analog3 -= var.handbrakedecreaserate

// Shift up & throttle cut
ppjoy.Digital0 = var.shiftup
if var.throttlecut != 0 && var.shiftup > 0 then ppjoy.analog0 = var.throttlecut

// Shift down
ppjoy.Digital1 = var.shiftdown

// Blip
if var.throttleblip != -1 && var.shiftdown then ppjoy.Analog0 = var.throttleblip

// Steering
//ppjoy.Analog4 = -1 + (var.steering * 2)
//ppjoy.analog4 = MapRange(-1 + (var.steering * 2), -1, 1, -0.8, 0.8)

var.foo = (mouse.x*2-1)*(mouse.x*2-1)
if mouse.x < 0.5 then var.foo = -var.foo
if mouse.x > 0.5 then var.foo = MapRange(var.foo, 0, 1, 0, exp(var.foo, 0.5))/2.72
if mouse.x < 0.5 then var.foo = MapRange(-var.foo, -1, 0, exp(-var.foo, 0.5), 0)/2.72
if mouse.x < 0.5 then var.foo = -var.foo
var.foo = var.foo^1.1
if mouse.x < 0.5 then var.foo = -var.foo

//var.foo = (mouse.x*2-1)*(mouse.x*2-1)*(mouse.x*2-1)
//var.foo = intPower((mouse.x*2-1), 2)
//if mouse.x < 0.5 then var.foo = -var.foo

//var.foo = (abs(mouse.x*2-1)^1.5)^1.5
//if mouse.x < 0.5 then var.foo = -var.foo
//var.foo = MapRange(var.foo, -1, 1, -0.9, 0.9)
//debug = var.foo
ppjoy.analog4 = var.foo

Quote from NotAnIllusion :Well, if we're comparing PIE scripts.. I use this for AC. Some variations on steering linearity at the bottom.

That's a much more interesting design.
Here's my FreePIE + vJoy script for mouse steering. It was initially made to mimic the LFS style of mouse steering in other racing games, and the axes are therefor mapped to the axes which the default G27 profile had in the games I tested.

It isn't very elegant because I ran into scoping problems as soon as I tried to solve this using functions and classes/objects.

The script features:
  • Easy axis inversion controls
  • LFS style sensitivity center reduction for mouse steering
  • Timer based ignition cut so you don't manually cut the ignition for longer than necessary by holding down the shift up key
  • Throttle blip that can be controlled by how long you hold down the shift down key
  • Independent rate controls for increasing and decreasing throttle, brakes and clutch
  • Addition rate controls for increasing and decreasing throttle and clutch during ignition cut and throttle blip events

if starting:
system.setThreadTiming(TimingTypes.HighresSystemTimer)
system.threadExecutionInterval = 5

def set_button(button, key):
if keyboard.getKeyDown(key):
v.setButton(button, True)
else:
v.setButton(button, False)

def calculate_rate(max, time):
if time > 0:
return max / (time / system.threadExecutionInterval)
else:
return max

int32_max = (2 ** 14) - 1
int32_min = (( 2** 14) * -1) + 1

v = vJoy[0]
v.x, v.y, v.z, v.rx, v.ry, v.rz, v.slider, v.dial = (int32_min,) * 8

# =============================================================================================
# Axis inversion settings (multiplier): normal = 1; inverted = -1
# =============================================================================================
global throttle_inversion, braking_inversion, clutch_inversion
throttle_inversion = 1
braking_inversion = 1
clutch_inversion = 1

# =============================================================================================
# Mouse settings
# =============================================================================================
global mouse_sensitivity, sensitivity_center_reduction
mouse_sensitivity = 25.0
sensitivity_center_reduction = 5.0

# =============================================================================================
# Ignition cut settings
# =============================================================================================
global ignition_cut_time, ignition_cut_elapsed_time
ignition_cut_enabled = True
ignition_cut_time = 100
ignition_cut_elapsed_time = 0

global ignition_cut, ignition_cut_released
# Init values, do not change
ignition_cut = False
ignition_cut_released = True

# =============================================================================================
# Steering settings
# =============================================================================================
global steering, steering_max, steering_min, steering_center_reduction
# Init values, do not change
steering = 0.0
steering_max = float(int32_max)
steering_min = float(int32_min)
steering_center_reduction = 1.0

# =============================================================================================
# Throttle settings
# =============================================================================================
global throttle_blip_enabled
throttle_blip_enabled = True

# In milliseconds
throttle_increase_time = 100
throttle_increase_time_after_ignition_cut = 0
throttle_increase_time_blip = 50
throttle_decrease_time = 100

global throttle, throttle_max, throttle_min
# Init values, do not change
throttle_max = int32_max * throttle_inversion
throttle_min = int32_min * throttle_inversion
throttle = throttle_min

global throttle_increase_rate, throttle_decrease_rate
# Set throttle behaviour with the increase and decrease time,
# the actual increase and decrease rates are calculated automatically
throttle_increase_rate = calculate_rate(throttle_max, throttle_increase_time)
throttle_increase_rate_after_ignition_cut = calculate_rate(throttle_max, throttle_increase_time_after_ignition_cut)
throttle_increase_rate_blip = calculate_rate(throttle_max, throttle_increase_time_blip)
throttle_decrease_rate = calculate_rate(throttle_max, throttle_decrease_time) * -1

# =============================================================================================
# Braking settings
# =============================================================================================
# In milliseconds
braking_increase_time = 100
braking_decrease_time = 100

global braking, braking_max, braking_min
# Init values, do not change
braking_max = int32_max * braking_inversion
braking_min = int32_min * braking_inversion
braking = braking_min

global braking_increase_rate, braking_decrease_rate
# Set braking behaviour with the increase and decrease time,
# the actual increase and decrease rates are calculated automatically
braking_increase_rate = calculate_rate(braking_max, braking_increase_time)
braking_decrease_rate = calculate_rate(braking_max, braking_decrease_time) * -1

# =============================================================================================
# Clutch settings
# =============================================================================================
# In milliseconds
clutch_increase_time = 0
clutch_decrease_time = 50

global clutch, clutch_max, clutch_min
# Init values, do not change
clutch_max = int32_max * clutch_inversion
clutch_min = int32_min * clutch_inversion
clutch = clutch_min

global clutch_increase_rate, clutch_decrease_rate
# Set clutch behaviour with the increase and decrease time,
# the actual increase and decrease rates are calculated automatically
clutch_increase_rate = calculate_rate(clutch_max, clutch_increase_time)
clutch_decrease_rate = calculate_rate(clutch_max, clutch_decrease_time) * -1

# =============================================================================================
# Button and key assignments
# =============================================================================================
global clutch_key
clutch_key = Key.C

global shift_up_key, shift_up_button
shift_up_key = Key.W
shift_up_button = 1

global shift_down_key, shift_down_button
shift_down_key = Key.S
shift_down_button = 2

global look_left_key, look_left_button
look_left_key = Key.A
look_left_button = 3

global look_right_key, look_right_button
look_right_key = Key.D
look_right_button = 4

global look_back_key, look_back_button
look_back_key = Key.X
look_back_button = 5

global change_view_key, change_view_button
change_view_key = Key.V
change_view_button = 6

global indicator_left_key, indicator_left_button
indicator_left_key = Key.Q
indicator_left_button = 7

global indicator_right_key, indicator_right_button
indicator_right_key = Key.E
indicator_right_button = 8

# =================================================================================================
# LOOP START
# =================================================================================================

# =================================================================================================
# Steering logic
# =================================================================================================
if steering > 0:
steering_center_reduction = sensitivity_center_reduction ** (1 - (steering / steering_max))
elif steering < 0:
steering_center_reduction = sensitivity_center_reduction ** (1 - (steering / steering_min))

steering = steering + ((float(mouse.deltaX) * mouse_sensitivity) / steering_center_reduction)

if steering > steering_max:
steering = steering_max
elif steering < steering_min:
steering = steering_min

v.x = int(round(steering))

# =================================================================================================
# Clutch logic
# =================================================================================================
if (throttle_blip_enabled and keyboard.getKeyDown(shift_down_key)) or (ignition_cut_enabled and ignition_cut_released and keyboard.getKeyDown(shift_up_key)) or keyboard.getKeyDown(clutch_key):
clutch = clutch_max
else:
clutch = clutch + clutch_decrease_rate

if clutch > clutch_max * clutch_inversion:
clutch = clutch_max * clutch_inversion
elif clutch < clutch_min * clutch_inversion:
clutch = clutch_min * clutch_inversion

v.slider = clutch

# =================================================================================================
# Throttle logic
# =================================================================================================
if ignition_cut_enabled and ignition_cut and ignition_cut_elapsed_time < ignition_cut_time:
ignition_cut_elapsed_time = ignition_cut_elapsed_time + system.threadExecutionInterval

if ignition_cut_enabled and not ignition_cut_released and keyboard.getKeyUp(shift_up_key):
ignition_cut_released = True

if throttle_blip_enabled and ((ignition_cut_enabled and not ignition_cut) or (not ignition_cut_enabled)) and keyboard.getKeyDown(shift_down_key):
# Throttle blip
throttle = throttle + throttle_increase_rate_blip
elif ignition_cut_enabled and ignition_cut_released and keyboard.getKeyDown(shift_up_key):
# Ignition cut
throttle = throttle_min
ignition_cut = True
ignition_cut_released = False
ignition_cut_elapsed_time = 0
elif mouse.leftButton:
if ignition_cut_enabled and ignition_cut and ignition_cut_elapsed_time >= ignition_cut_time:
throttle = throttle_max
else:
throttle = throttle + throttle_increase_rate
else:
throttle = throttle + throttle_decrease_rate

if ignition_cut_enabled and ignition_cut and ignition_cut_elapsed_time >= ignition_cut_time:
ignition_cut = False
ignition_cut_elapsed_time = 0

if throttle > throttle_max * throttle_inversion:
throttle = throttle_max * throttle_inversion
elif throttle < throttle_min * throttle_inversion:
throttle = throttle_min * throttle_inversion

v.y = throttle

# =================================================================================================
# Braking logic
# =================================================================================================
if mouse.rightButton:
braking = braking + braking_increase_rate
else:
braking = braking + braking_decrease_rate

if braking > braking_max * braking_inversion:
braking = braking_max * braking_inversion
elif braking < braking_min * braking_inversion:
braking = braking_min * braking_inversion

v.rz = braking

# =================================================================================================
# Buttons post-throttle logic
# =================================================================================================
set_button(look_left_button, look_left_key)
set_button(look_right_button, look_right_key)
set_button(look_back_button, look_back_key)
set_button(change_view_button, change_view_key)
set_button(indicator_left_button, indicator_left_key)
set_button(indicator_right_button, indicator_right_key)

# =================================================================================================
# PIE diagnostics logic
# =================================================================================================
diagnostics.watch(v.x)
diagnostics.watch(v.y)
diagnostics.watch(v.rz)
diagnostics.watch(v.slider)
diagnostics.watch(steering_center_reduction)
diagnostics.watch(throttle_blip_enabled)
diagnostics.watch(ignition_cut_enabled)

Anyone got this strange error?
Quote :Axis HID_USAGE_Z not enabled, enabled it from VJoy config

I suddenly got this (without changing VJoy nor FreePIE) on all the axises but X/Y on vJoy[0] and everything on vJoy[1]. Only X/Y are useable now.

All the axises are enabled in VJoy config and the feeder demo program works just fine.
Mouse X for steering
Hello,

Can someone please write a code (to be used with vJoy and FreePie) for just mouse steering. I'll be using it for rFactor. I recently got a new laptop which came with Win 8.1 - no PPJoy. I just need X- and X+. I'll map the rest with keyboard.

Please help!

P.S. Axis HID_USAGE_Z not enabled, enabled it from VJoy config - I'm getting this error also using the codes pasted above.
Quote from hereticlohani :Hello,

Can someone please write a code (to be used with vJoy and FreePie) for just mouse steering. I'll be using it for rFactor. I recently got a new laptop which came with Win 8.1 - no PPJoy. I just need X- and X+. I'll map the rest with keyboard.

Please help!

P.S. Axis HID_USAGE_Z not enabled, enabled it from VJoy config - I'm getting this error also using the codes pasted above.

It's probably a temporary bug caused by recent Windows updates. I'll see if I can contact the devs to find out what's going on.

For now, just comment out all the output lines but vJoy[0].x and you should be set.
Hey, thanks for replying Kelling. I'm having difficulty understanding how both tools work, actually. I installed vJoy (204_10_80714) and FreePie (1.5.475.0).

Let's say that I'll be using Slagen's code above. What do I do first? With PPJoy it was simple, configure PPJoy, calibrate joystick with PPJoyMouse open, and I'd see the mouse x input on the calibration.

I've got vJoy feeder open, game controller open, vJoy monitor open and the FreePie script program open (with slagen's code pasted and run). The calibration window doesn't seem to read and calibration input.

rFactor's also running; doesn't detect mouse movements or buttons.
Quote from hereticlohani :I've got vJoy feeder open, game controller open, vJoy monitor open and the FreePie script program open (with slagen's code pasted and run). The calibration window doesn't seem to read and calibration input.

rFactor's also running; doesn't detect mouse movements or buttons.

Which "feeder"? If you mean the demo program you should turn it off. You can only control each vJoy controller with one program at a time. Using multiple would mess up the input.

If you have no error after running the script it should work.
Quote from Keling :Which "feeder"? If you mean the demo program you should turn it off. You can only control each vJoy controller with one program at a time. Using multiple would mess up the input.

If you have no error after running the script it should work.

Hey, thanks again. I got it figured out. I'm using a simple script from the internet for mouse steering (below). It's working better than PPJoy on rFactor (I'm very happy . Can you please tell me what to add to get the mouse right and middle button also assigned?

from System import Int16

if starting:
x = 0
sensibilidadX = 50

x += mouse.deltaX * sensibilidadX

if (x > Int16.MaxValue):
x = Int16.MaxValue
elif (x < -Int16.MaxValue):
x = -Int16.MaxValue

vJoy[1].x = x

diagnostics.watch(vJoy[1].x)
I figured out how to map the mouse buttons. I find python interesting. Will try to learn.
I figured that FreePIE can receive OutGauge and OutSim packets, so you can read realtime data about the car and the game into your PIE scripts. Really cool.

Simple example to monitor the car speed and gear with a none-blocking socket. Note the exception handling when there was no UDP packet available for the given loop iteration.
Another thing to note is that to my knowledge, FreePIE does not have a "stopping" bool like the "starting" bool, so you cannot properly close the socket when you stop the script. To re-run it, you need to restart FreePIE in order to free up the socket.

It can be used, for instance, to reduce the max braking power when the speed goes below a threshold to help prevent flatspotting. Or if you're really pro, you can make your PIE script be aware of oversteer and understeer in realtime. Lots of ideas springs to mind.


if starting:
system.setThreadTiming(TimingTypes.HighresSystemTimer)
system.threadExecutionInterval = 5

import socket
import struct

global sock, outgauge
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Set the socket to be none-blocking, otherwise the
# loop will have to wait for each UDP packet to be received
sock.setblocking(0)
sock.bind(('127.0.0.1', 30000))
outgauge = None

global car_speed_unit, car_speed, car_gear
# 1 = m/s, 3.6 = km/h
car_speed_unit = 3.6
car_speed = 0
car_gear = 0

global shift_up_key, shift_down_key
shift_up_key = Key.W
shift_down_key = Key.S

# ====================================================
# LOOP START
# ====================================================

try:
outgauge = struct.unpack('I3sxH2B7f2I3f15sx15sx', sock.recv(256))
car_speed = int(round(outgauge[5] * car_speed_unit))
car_gear = int(round(outgauge[3])) - 1
except socket.error, e:
# This code prevents the car_gear to be async while waiting for a UDP packet with the real gear value
if keyboard.getKeyDown(shift_up_key) and not keyboard.getKeyDown(shift_down_key):
car_gear = car_gear + 1
elif keyboard.getKeyDown(shift_down_key) and not keyboard.getKeyDown(shift_up_key):
car_gear = car_gear - 1

diagnostics.watch(car_speed)
diagnostics.watch(car_gear)

Yes, Free/GlovePIE have the capability to be used for cheating (e.g. ABS and TC for cars that don't have it, or when they aren't allowed to be used). Shouldn't use such things in serious online racing though, on ethical principles.
-
(Wenom) DELETED by Wenom
Quote from Wenom :Does this mean that anyone could actually make an "anti-fry-front-wheels system" for xbox 360 pad? Much like Forza on the xbox; it prevents you from turning your wheels too much while racing and doesn't cause unnecessary heat and wear to the tires.

And yes I might aswell say this: to me analogue steering smooth inside LFS is CRAP so nobody thinks that fiddling with that setting solves anything.

Yes. And it's not very difficult. Will try. You can already do that in KB mode though.

Unfortunately we can't get FFB info or kinematics of each wheel from OutSim/Gauge.


Speaking of controllers, can anyone recommend a controller for PC (preferably wireless) that has nice linearity, small/no deadzone (at both center and edge), good amount of analog travel and reasonably heavy springs? I've tried some controllers at my console-oriented friends' and always find the small travel and light springs very different to control.
Good news. The axis not enabled error is fixed by FreePIE's dev. Go grab the fix on their forum.
Isn't socket.close() sufficient for freeing the socket? I tried this

if keyboard.getKeyDown(exitKey):
sockOS.shutdown(0)
sockOS.close()
sys.exit(0)

at the beginning of the loop but rerunning the script still fail.
Quote from Wenom :
Quote from Skagen :Or if you're really pro, you can make your PIE script be aware of oversteer and understeer in realtime. Lots of ideas springs to mind.

Does this mean that anyone could actually make an "anti-fry-front-wheels system" for xbox 360 pad? Much like Forza on the xbox; it prevents you from turning your wheels too much while racing and doesn't cause unnecessary heat and wear to the tires.

And yes I might aswell say this: to me analogue steering smooth inside LFS is CRAP so nobody thinks that fiddling with that setting solves anything.

Got a prototype running.


LFS only sends out OutSim packages when driver view or custom view is used, so you have to use one of the two for the script to work. With other views it would fall back to normal behavior.


I don't have a controller for testing, therefore I added an extra axis (vJoy[1].x) to simulate the input with keyboard. To test the script with real hardware, remove the two "REMOVE THIS FOR REAL CONTROLLER" blocks and edit the "EDIT THIS FOR REAL CONTROLLER" line so it points to your real input. OutSim port is set as 30000.

Edit the "EDIT BELOW/ABOVE" block to suit your preference (see the comments). Make sure 'steerLock' value is correct. You can leave the rest as defaults.

The script monitors the direction of movement and restricts your steering angle accordingly so the slip angle on the front won't get too high. This is effective regardless of car body's orientation as long as it's not hitting steering lock, so when you're over steering heavily it basically does the counter steering for you. Maybe I've gone a bit too far?

"Absolute positioning" version behaves in a way similar to Forza's assist function, with the steering input mapped 1:1 to the output, but then restricted for controlling slip angle. "Relative positioning" version maps your input onto the allowed steering angle range, so the center is zero lateral slip, left lock is maximum lateral slip to the left and vice versa. This is similar to stabilized keyboard mode but with analog control. It should provide significantly higher precision, but may feel awkward at first, and very unstable near the center if your input is not smooth. (Some more fancy PID design or stuff could handle this.) The steering behavior will fall back to normal if car is too slow or going in reverse.


Code is too long to post directly. See attachment.
Attached files
LFS_Steer_Assist_FreePIE.zip - 4.4 KB - 780 views
Quote from Keling :Isn't socket.close() sufficient for freeing the socket? I tried this

if keyboard.getKeyDown(exitKey):
sockOS.shutdown(0)
sockOS.close()
sys.exit(0)

at the beginning of the loop but rerunning the script still fail.

I thought it would be. I actually never tried to use the close() method properly and I thought that was the reason I could not create new sockets within the Python interpreter in FreePIE.

You could ask the FreePIE guy, I'm not very familiar with neither FreePIE nor Python in general. There might be some other trick to it.
Well, looks like it's happy if you run the same routine twice. This is weird.


# initialize exitCnt as 0
# hold [exitKey] for half a second to stop

if keyboard.getKeyDown(exitKey):
exitCnt += 1
if (exitCnt == 1 or exitCnt == 10):
sockOS.shutdown(2)
sockOS.close()
elif (exitCnt > 100):
sys.exit(0)

Sorry for the bump

i just recently got starting to play NFSS2 and i'm trying to use the mouse as steering

I tryed with ppjoy and mousejoystic and for some reason X axis doesnt work

so now i'm trying vjoy with glovepie
anyone can explay steep by steep what i have to do?
Quote from Inouva :Sorry for the bump

i just recently got starting to play NFSS2 and i'm trying to use the mouse as steering

I tryed with ppjoy and mousejoystic and for some reason X axis doesnt work

so now i'm trying vjoy with glovepie
anyone can explay steep by steep what i have to do?

GlovePIE appears to have no vJoy support. You'll need FreePIE.

Install both. Go to vJoy config and make sure at least one virtual device is created. The default setup should be fine. You can turn on all the axes just to be safe.

Start FreePIE and load a script. The mouse driving one on #1 will do. Run script from the menu (or F5?). Then go to NFSS2 and config your controls.


NFSS2 has very little steering angle for the animation, so mouse steering would be a bit awkward.
Quote from Keling :
Quote from Inouva :Sorry for the bump

i just recently got starting to play NFSS2 and i'm trying to use the mouse as steering

I tryed with ppjoy and mousejoystic and for some reason X axis doesnt work

so now i'm trying vjoy with glovepie
anyone can explay steep by steep what i have to do?

GlovePIE appears to have no vJoy support. You'll need FreePIE.

Install both. Go to vJoy config and make sure at least one virtual device is created. The default setup should be fine. You can turn on all the axes just to be safe.

Start FreePIE and load a script. The mouse driving one on #1 will do. Run script from the menu (or F5?). Then go to NFSS2 and config your controls.


NFSS2 has very little steering angle for the animation, so mouse steering would be a bit awkward.

thanks worked, and true have little angle of animation still is much better that use keyboard to steer

btw i fixed my missing X axis on ppjoy
now i'm gona enjoy the F12014 game and NFSS2
hello

for a flightsim i need a script like yours, with additional y-axis and the functionality of changing the sensitivity in the middle. can somebody help me?
Quote from michi.graf :hello

for a flightsim i need a script like yours, with additional y-axis and the functionality of changing the sensitivity in the middle. can somebody help me?

Just to be sure, you're trying to map stick/yoke x/y onto mouse x/y in an absolute manner, is that true? Is there anything else you need to control via the proxy?

Be aware that the stick/yoke does not have much range of motion visually, so without physical feedback control could be difficult.



if starting:
system.setThreadTiming(TimingTypes.HighresSystemTimer)
system.threadExecutionInterval = 5 # loop delay
max = Int16.MaxValue*0.5+2
min = -Int16.MaxValue*0.5+1
x = 0.0
y = 0.0
sen = 8 # sensitivity


x += mouse.deltaX * sen
y += mouse.deltaY * sen

if (x > max):
x = max
elif (x < min):
x = min

if (y > max):
y = max
elif (y < min):
y = min

if keyboard.getKeyDown(Key.LeftControl) and mouse.middleButton: # centering
x = 0
y = 0

vJoy[0].x = x
vJoy[0].y = y


Change sensitivity constant "sen" to what you perfer. Ctrl + Mouse Middle for centering.
1

(Update: bug fixed) vJoy + FreePIE for scripting virtual controllers
(45 posts, started )
FGED GREDG RDFGDR GSFDG