A better vehicle/simulations control thru the keyboard
by Bruno Grieco · 06/03/2004 (12:55 pm) · 20 comments
First of all, we need to understand why controlling the vehicle thru the keyboard doesn't work :
problem 1) When you bind a key to a function using the moveMap.bind command ie.
the function will be called once when the key is pressed ( in this case the "a" key ) setting %val as 1 and once when the key is depressed setting %val as 0.
This is the equivalent of steering the wheel all the way left as soon as you press the "a" key. This is not what you expect a simulator to do. Simulators increment the steering value while the key is pressed. If you press it for longer, you will steer the whell further right than if you press it shortly.
the same doesn't happen when you use the mouse because the mouse displacement is used to calculate the ammount of steering you need.
Problem 2) The same happens with the acceleration/brake mechanism. When you press the forward key you pump all the gas and when you release it you take your foot off the acelerator.
Problem 2 is actually easier to deal with, first check out this resource by Alex "Bathala" Rufon. It will explain how to move your character forward without pressing a key. Next just type in the following code in your default.bind.cs module ( usually in your game's client folder ) :
This code snippet will acellerate/brake you by incrementing/decrementing the global $movementSpeed variable. Since I'm doing a flight sim, I don't want my planes to stop in mid air so there is an if to clamp the value at 0.1 but you could change it to 0 as well.
Now you should be able to control the throttle in a more gentle way thru the "p" and "o" keys.
OK, now that Problem 2 is solved, let's take care of Problem 1. This is a more complex task. Since I don't know for how long the key is preesed, what I need to do is spawn ( schedule ) a thread that will continuasly incremennt my value until told otherwise.
If %val is 1 ( the key is pressed ), then the "incYaw" function will be scheduled to run in the next 100 miliseconds with the value "-1" ( left) as a parameter. The thread number of incYaw will be stored in the global $incYawId. we need to store it in a global because this value will be changed by the IncYaw function.
If %val is 0 then the incYaw function will be cancelled and $mvYaw will be set to 0;
When incYaw is executed, it will update $mvYaw AND schedule itself to run again in 100 miliseconds. Note that $incYawId will receive a new value here, the value of the next scheduled incYaw function. This value will be needed by the yawLeft function that will cancel the next incYaw that will be executed and not the incYaw that it spawned.
Next, all you need to do is add a keyboard binding for yawLeft, create a YawRight - note that you don't have to create a new incYaw function, just pass 1 as the parameter in YawRight instead of the "-1" parameter of YawLeft. And replicate this structure for pitch.
And, since I've done it before, here goes the complete code :
Note that I'm using a FlySim convention here, down key means pulling the stick towards you, hence going up.
Enjoy !!
problem 1) When you bind a key to a function using the moveMap.bind command ie.
moveMap.bind( keyboard, a, moveleft );
function moveleft(%val)
{
$mvLeftAction = %val * $movementSpeed;
}the function will be called once when the key is pressed ( in this case the "a" key ) setting %val as 1 and once when the key is depressed setting %val as 0.
This is the equivalent of steering the wheel all the way left as soon as you press the "a" key. This is not what you expect a simulator to do. Simulators increment the steering value while the key is pressed. If you press it for longer, you will steer the whell further right than if you press it shortly.
the same doesn't happen when you use the mouse because the mouse displacement is used to calculate the ammount of steering you need.
Problem 2) The same happens with the acceleration/brake mechanism. When you press the forward key you pump all the gas and when you release it you take your foot off the acelerator.
Problem 2 is actually easier to deal with, first check out this resource by Alex "Bathala" Rufon. It will explain how to move your character forward without pressing a key. Next just type in the following code in your default.bind.cs module ( usually in your game's client folder ) :
function incmove(%val)
{
$movementSpeed += 0.1;
if($movementSpeed > 1.0 )
$movementSpeed = 1.0;
$mvForwardAction = $movementSpeed;
}
function decmove(%val)
{
$movementSpeed -= 0.1;
if($movementSpeed < 0.1 )
$movementSpeed = 0.1;
$mvForwardAction = $movementSpeed;
}
moveMap.bind(keyboard, p, incMove);
moveMap.bind(keyboard, o, decMove);This code snippet will acellerate/brake you by incrementing/decrementing the global $movementSpeed variable. Since I'm doing a flight sim, I don't want my planes to stop in mid air so there is an if to clamp the value at 0.1 but you could change it to 0 as well.
Now you should be able to control the throttle in a more gentle way thru the "p" and "o" keys.
OK, now that Problem 2 is solved, let's take care of Problem 1. This is a more complex task. Since I don't know for how long the key is preesed, what I need to do is spawn ( schedule ) a thread that will continuasly incremennt my value until told otherwise.
function yawLeft(%val)
{
if(%val)
$incYawId = schedule(100,0,"incYaw",-1);
else
{
cancel($incYawId);
$mvYaw = 0;
}
}If %val is 1 ( the key is pressed ), then the "incYaw" function will be scheduled to run in the next 100 miliseconds with the value "-1" ( left) as a parameter. The thread number of incYaw will be stored in the global $incYawId. we need to store it in a global because this value will be changed by the IncYaw function.
If %val is 0 then the incYaw function will be cancelled and $mvYaw will be set to 0;
function incYaw(%val)
{
$mvYaw += %val;
$incYawid = schedule(100,0,"incYaw",%val);
}When incYaw is executed, it will update $mvYaw AND schedule itself to run again in 100 miliseconds. Note that $incYawId will receive a new value here, the value of the next scheduled incYaw function. This value will be needed by the yawLeft function that will cancel the next incYaw that will be executed and not the incYaw that it spawned.
Next, all you need to do is add a keyboard binding for yawLeft, create a YawRight - note that you don't have to create a new incYaw function, just pass 1 as the parameter in YawRight instead of the "-1" parameter of YawLeft. And replicate this structure for pitch.
And, since I've done it before, here goes the complete code :
function yawLeft(%val)
{
if(%val)
$incYawid = schedule(100,0,"incYaw",-1);
else
{
cancel($incYawid);
$mvYaw = 0;
}
}
function yawRight(%val)
{
if(%val)
$incYawid = schedule(100,0,"incYaw",1);
else
{
cancel($incYawid);
$mvYaw = 0;
}
}
function incYaw(%val)
{
$mvYaw += %val;
$incYawid = schedule(100,0,"incYaw",%val);
}
function pitchUp(%val)
{
if(%val)
$incPitchId = schedule(100,0,"incPitch",-1);
else
{
cancel($incPitchId);
$mvPitch = 0;
}
}
function pitchDown(%val)
{
if(%val)
$incPitchId = schedule(100,0,"incPitch",1);
else
{
cancel($incPitchId);
$mvPitch = 0;
}
}
function incPitch(%val)
{
$mvPitch += %val;
$incPitchId = schedule(100,0,"incPitch",%val);
}
moveMap.bind( keyboard, up, pitchDown );
moveMap.bind( keyboard, down, pitchUp);
moveMap.bind( keyboard, left, yawLeft );
moveMap.bind( keyboard, right, yawRight );Note that I'm using a FlySim convention here, down key means pulling the stick towards you, hence going up.
Enjoy !!
About the author
Recent Blogs
• Starting 2D development• The Flight Compendium
• Plan for Bruno Grieco
• Plan for Bruno Grieco
#2
08/21/2004 (8:34 pm)
Do you have to do anything piticular to allow "rolling".
#3
You must implement rolling on your model first. In Flying Vehicles, roling is done together with Yaw. you would have to separate it first to then apply this resource.
08/23/2004 (7:45 pm)
@GrantYou must implement rolling on your model first. In Flying Vehicles, roling is done together with Yaw. you would have to separate it first to then apply this resource.
#4
09/07/2004 (11:07 pm)
Thanks.
#5
10/01/2004 (6:56 pm)
Excellent... I think this is just what I was looking for. My movements are different, but I can adapt your script to at least get smooth movement. Thanks!
#6
For example, if you take a look in the default.bind.cs file, you can see the following functions:
Unlike $mvYaw, the $mvYawRightSpeed and $mvYawLeftSpeed variables are persistent, so if you were to press the right turn key, it will continue to take right. Same applys for left turn key going left until the key is released.
If you want to see which variables are persistent and which are not, turn to
[game/gameConnectionMoves.cc], at around line 184. Variables that are zeroes out are non persistent.
To add the hooks for turning then would only require the following:
10/04/2004 (1:51 pm)
This is a good resource, but there is much simpler way to accomplish similar controls w/o making any adjustment for pitch ang yaw movements, or scheduling a task to re-iterate the control. In TGE, there are several key variables that are persistent (doesn't get zeroed out every frame), and you just need to know what they are.For example, if you take a look in the default.bind.cs file, you can see the following functions:
function turnLeft( %val )
{
$mvYawRightSpeed = %val ? $Pref::Input::KeyboardTurnSpeed : 0;
}
function turnRight( %val )
{
$mvYawLeftSpeed = %val ? $Pref::Input::KeyboardTurnSpeed : 0;
}Unlike $mvYaw, the $mvYawRightSpeed and $mvYawLeftSpeed variables are persistent, so if you were to press the right turn key, it will continue to take right. Same applys for left turn key going left until the key is released.
If you want to see which variables are persistent and which are not, turn to
bool GameConnection::getNextMove(Move &curMove)function in
[game/gameConnectionMoves.cc], at around line 184. Variables that are zeroes out are non persistent.
To add the hooks for turning then would only require the following:
moveMap.bind(keyboard, "left", turnLeft); moveMap.bind(keyboard, "right", turnRight);.
#8
To make a flying vehicle move only in 2D space just make rollForce = 0 and verticalSurfaceForce = 0 inside of your data block.
This will make your vehicle slide left and right and not do any roll.
01/18/2005 (6:56 pm)
Just incase anyone cares to know...To make a flying vehicle move only in 2D space just make rollForce = 0 and verticalSurfaceForce = 0 inside of your data block.
This will make your vehicle slide left and right and not do any roll.
#9
04/19/2005 (5:47 pm)
Outstanding. Much thanks. I've enjoyed the flight help around this site, but the controls were the one thing no one really got right until I read this.
#10
04/22/2005 (2:29 pm)
EDIT: Problem was my making.
#11
07/06/2005 (3:49 pm)
this has come in handy.
#12
11/18/2005 (3:42 pm)
Excellent thanks for this, I really didn't see why my keyboard input was working properly with these controls until now!
#13
centered after releasing the buttons?.....it looks quite odd to hold on steered after releasing..
Great job....
12/25/2005 (10:11 pm)
That's an excellent for vehicles steering system.....but does anybody know how to make the wheelscentered after releasing the buttons?.....it looks quite odd to hold on steered after releasing..
Great job....
#14
The script Akio showed used a turnLeft, turnRight function that toggled from 0 to 1 with a keypress.. ie. (true/false key pressed or not) once the key is no longer pressed the function ceases and the initial wheel state would be restored...
Buy Bravetree's "car pack" for a good set of vehicle tools and scripts.
05/10/2006 (6:20 pm)
Diac,The script Akio showed used a turnLeft, turnRight function that toggled from 0 to 1 with a keypress.. ie. (true/false key pressed or not) once the key is no longer pressed the function ceases and the initial wheel state would be restored...
Buy Bravetree's "car pack" for a good set of vehicle tools and scripts.
#15
"noobish"
08/03/2006 (1:18 pm)
sorry to add something after so long time to this thread, but i can't get this one to work..."noobish"
#16
It seems that a schedule is needed to make it return to zero.
08/22/2006 (5:48 pm)
the wheel state doesn't get restored though. I have been testing it and in a vehicle situation using turnleft makes it continuosly turn left. It seems that a schedule is needed to make it return to zero.
#17
Luck!
Guimo
09/20/2006 (3:52 am)
Same problem here... it rotates right (or left) and keeps rotating even when I release the key. As Scott said... looks like a thread is required to restore the direction.Luck!
Guimo
#18
03/23/2007 (8:01 pm)
I am using simple controls for the hover vehicles I am using, basically I just changed the bind from moveLeft to turnLeft/right to remove strafing in the vehicles. It works great for turning and control, but it doesn't slow down and stop due to any drag. I worked on a brute force method that basically just figures out which way you're rotating and applies a small counter rotation force in a quick schedule to simulate slowing down and stopping. It doesn't work very well. Has anybody had similar problems and figured out a way to fix it? One last clarification, the $mvYawLeftSpeed/rightspeed can be set back to 0 but with the hover vehicle it still has the inherited force that isn't cancelled out when the speed is.
#19
Open example\demo\client\config.cs
//moveMap.bind(keyboard, "pageup", pageMessageHudUp);
//moveMap.bind(keyboard, "pagedown", pageMessageHudDown);
//************************************************************
$speed = 1; // time in ms to wait , play with it
// global namespace triggers, need to read next trigger in local namespace
$val1 = 0; //yaw left
$val2 = 0; //yaw right
$val3 = 0; //pitch Up
$val4 = 0; //pitch Down
function yawLeft($val1)
{ $advCamera::Yaw = 0; cancel($incYawid);
if($val1) { $incYawid = schedule($speed,0,"incYaw",-1);}
else { cancel($incYawid); $advCamera::Yaw = 0;}
}
function yawRight($val2)
{ $advCamera::Yaw = 0; cancel($incYawid);
if($val2) { $incYawid = schedule($speed,0,"incYaw",1); }
else { cancel($incYawid); $advCamera::Yaw = 0; }
}
function incYaw($val)
{
$advCamera::Yaw += $val;
$incYawid = schedule($speed,0,"incYaw",$val);
}
function pitchUp(%val3)
{ $advCamera::Pitch = 0; cancel($incPitchId);
if(%val3) $incPitchId = schedule($speed,0,"incPitch",-1);
else {cancel($incPitchId); $advCamera::Pitch = 0;}
}
function pitchDown(%val4)
{ $advCamera::Pitch = 0; cancel($incPitchId);
if(%val4) $incPitchId = schedule($speed,0,"incPitch",1);
else {cancel($incPitchId); $advCamera::Pitch = 0;}
}
function incPitch(%val)
{
$advCamera::Pitch += %val;
$incPitchId = schedule($speed,0,"incPitch",%val);
}
moveMap.bind( keyboard, "pagedown", pitchDown );
moveMap.bind( keyboard, "pageup", pitchUp);
moveMap.bind( keyboard, "q", yawLeft );
moveMap.bind( keyboard, "e", yawRight );
01/06/2008 (5:54 pm)
I've fixed the thread issue and moded to use the orbit advanced camera (first implement this)Open example\demo\client\config.cs
//moveMap.bind(keyboard, "pageup", pageMessageHudUp);
//moveMap.bind(keyboard, "pagedown", pageMessageHudDown);
//************************************************************
$speed = 1; // time in ms to wait , play with it
// global namespace triggers, need to read next trigger in local namespace
$val1 = 0; //yaw left
$val2 = 0; //yaw right
$val3 = 0; //pitch Up
$val4 = 0; //pitch Down
function yawLeft($val1)
{ $advCamera::Yaw = 0; cancel($incYawid);
if($val1) { $incYawid = schedule($speed,0,"incYaw",-1);}
else { cancel($incYawid); $advCamera::Yaw = 0;}
}
function yawRight($val2)
{ $advCamera::Yaw = 0; cancel($incYawid);
if($val2) { $incYawid = schedule($speed,0,"incYaw",1); }
else { cancel($incYawid); $advCamera::Yaw = 0; }
}
function incYaw($val)
{
$advCamera::Yaw += $val;
$incYawid = schedule($speed,0,"incYaw",$val);
}
function pitchUp(%val3)
{ $advCamera::Pitch = 0; cancel($incPitchId);
if(%val3) $incPitchId = schedule($speed,0,"incPitch",-1);
else {cancel($incPitchId); $advCamera::Pitch = 0;}
}
function pitchDown(%val4)
{ $advCamera::Pitch = 0; cancel($incPitchId);
if(%val4) $incPitchId = schedule($speed,0,"incPitch",1);
else {cancel($incPitchId); $advCamera::Pitch = 0;}
}
function incPitch(%val)
{
$advCamera::Pitch += %val;
$incPitchId = schedule($speed,0,"incPitch",%val);
}
moveMap.bind( keyboard, "pagedown", pitchDown );
moveMap.bind( keyboard, "pageup", pitchUp);
moveMap.bind( keyboard, "q", yawLeft );
moveMap.bind( keyboard, "e", yawRight );
#20
@Diac Ovidiu
To make steering to move center when you pressed up the key, refer the following:
www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=6305
01/10/2008 (2:28 pm)
Cool!@Diac Ovidiu
To make steering to move center when you pressed up the key, refer the following:
www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=6305
Torque Owner Aaron Shumaker