Dynamically change player speed in script
by Amr Bekhit · 04/10/2007 (9:08 am) · 26 comments
Background
In order to move a player, a client sets the corresponding $mvActionValue variable to 1. So in order to move forward, $mvForwardAction is set to 1. However, it is perfectly possible to set the variable to anything from 0.0 to 1.0. So, if $mvForwardAction is set to 0.5, the player will move forward at 0.5 times their maximum speed (defined in the player's datablock).
This can be used to slow down a player, but can be also used to speed them up. As an example, let's say a player's normal speed is 10 and their boost speed is 40. In the player datablock, maxForwardSpeed would be set to 40. When moving the player normally, $mvForwardAction would be set to 0.25 (0.25*40 = 10). Whenever you want to speed up, set $mvForwardAction to anything above 0.25.
You need to store some sort of speed modifier in your player object to keep track of current speed. Also, the client will need to keep a copy of the current speed modifier, which the server will update when the speed changes.
The following code will add a Speed pickup to the game. When the player picks it up, their forward speed will be 4 times their normal speed (normal = 14, max = 56) for a short period of time, then return to normal. Therefore, for maximum speed, $mvForwardAction needs to be 1 and for normal speed, $mvForwardAction needs to be 0.25.
Note: This code is based on the starter.fps of TGE 1.5. It should work for other versions as well, since I haven't relied on anything fundamentally different
Known Issues
Thanks to Vincent van Delden, it has been pointed out that a client could potentially cheat with this system, because they can type $mvForwardAction = 1; in the console and get a boost of speed whenever they want.
In retrospect though, this might not be so much of a problem, since it is probably not a good idea to give clients access to the console during a multiplayer game anyway, and in a single player game, it is not a problem if a player cheats or no.
-----------------------------------------------------------------------------------
~/server/scripts/player.cs
There's a few things we need to do here. First we need to set up the player's maximum speed, and we also need someway to change the speed and tell the client about it.
In the middle of the PlayerData datablock, modify the following:
Now, we need to write a function that will enable us to change the player's speed and update this information on the client. At the bottom of player.cs add the following function:
When we call this function on the player, the speed will be stored in a variable called speedMod, then we use commandToClient to notify the client of the change. We will write the client-side function in a short while.
Now that we have a way to change player speed, we need to make sure that the player starts off at normal speed, so add this at the bottom of Armor::onAdd:
Now, when a player is added to the level, we immediately set their speed to 0.25 times their maximum speed (0.25*56 = 14), which in our case is the normal speed.
-----------------------------------------------------------------------------------
~/client/scripts/default.bind.cs
It is here that the client will update the movement of the player. First off, we need a function to respond to the commandToServer we wrote in player.cs above. Add this somewhere in the file:
When the server sends the client the player's speed, we store that in a global variable called $SpeedFactor. Then, we need to check if the player is already moving forward. If they are then we need to update their speed.
Finally, modify the function moveForward as follows:
If a key is pressed (%val is 1), then the player will move at the speed defined in $SpeedFactor.
This way, whenever we want to change the player's speed, use player.setSpeed, and this updates the client to use the new speed.
-----------------------------------------------------------------------------------
~/server/scripts/speedPickup.cs
To demonstrate the use of these functions, here is a speed pickup which will speed up the player for 5 seconds when picked up. You'll need to create a new file called speedPickup.cs and place it in ~/server/scripts/. Add the following code to it:
When a player collides with the item, the item is hidden, and then we schedule it to reappear after 5 seconds. Next, we set the player's speed to maximum. We also schedule the player to go back to their original speed after 5 seconds. This way, when the player picks up the speed pickup, there speed will be boosted to max for 5 seconds, then go back to normal.
-----------------------------------------------------------------------------------
~/server/scripts/game.cs
Because we added a new script file, we need to tell the game to load it when we play. Simply add the following line to the bottom of onServerCreated:
Once all the code is in, start up a level and add the speed pickup somewhere (you'll find it under the Items category). Then, run over it and watch how the player speeds up.
-----------------------------------------------------------------------------------
Adding a sprint key
Adding a sprint key is quite simple and works like this: when the player presses the sprint key, the client sends a command to the server telling it that the player wants to sprint. The server then uses the setSpeed function above, which sets the player's speed and updates the client with a new speed. Follow the steps below to add a sprint key to your game.
-----------------------------------------------------------------------------------
~/client/scripts/default.bind.cs
Only one thing we need to do here, and that's adding a sprint key and the function that handles it. Add the following somewhere in the file:
The last command binds left shift to the sprint function. When left shift is pressed, %val is 1, so the function sends a 1 to the server. When the key is released %val is 0 so the server receives a 0. Next, we'll add the server side sprint function.
-----------------------------------------------------------------------------------
~/server/scripts/player.cs
Here is where the work is done. Depending on the value the server gets from the client, the server will either boost the player or bring them back to their normal speed. Add the following at the bottom of the file:
To test the code, first delete the file ~/client/config.cs. This will allow the new key command we added to take effect. After that, simply run your game and hold down left shift to sprint.
In order to move a player, a client sets the corresponding $mvActionValue variable to 1. So in order to move forward, $mvForwardAction is set to 1. However, it is perfectly possible to set the variable to anything from 0.0 to 1.0. So, if $mvForwardAction is set to 0.5, the player will move forward at 0.5 times their maximum speed (defined in the player's datablock).
This can be used to slow down a player, but can be also used to speed them up. As an example, let's say a player's normal speed is 10 and their boost speed is 40. In the player datablock, maxForwardSpeed would be set to 40. When moving the player normally, $mvForwardAction would be set to 0.25 (0.25*40 = 10). Whenever you want to speed up, set $mvForwardAction to anything above 0.25.
You need to store some sort of speed modifier in your player object to keep track of current speed. Also, the client will need to keep a copy of the current speed modifier, which the server will update when the speed changes.
The following code will add a Speed pickup to the game. When the player picks it up, their forward speed will be 4 times their normal speed (normal = 14, max = 56) for a short period of time, then return to normal. Therefore, for maximum speed, $mvForwardAction needs to be 1 and for normal speed, $mvForwardAction needs to be 0.25.
Note: This code is based on the starter.fps of TGE 1.5. It should work for other versions as well, since I haven't relied on anything fundamentally different
Known Issues
Thanks to Vincent van Delden, it has been pointed out that a client could potentially cheat with this system, because they can type $mvForwardAction = 1; in the console and get a boost of speed whenever they want.
In retrospect though, this might not be so much of a problem, since it is probably not a good idea to give clients access to the console during a multiplayer game anyway, and in a single player game, it is not a problem if a player cheats or no.
-----------------------------------------------------------------------------------
~/server/scripts/player.cs
There's a few things we need to do here. First we need to set up the player's maximum speed, and we also need someway to change the speed and tell the client about it.
In the middle of the PlayerData datablock, modify the following:
datablock PlayerData(PlayerBody)
{
renderFirstPerson = false;
emap = true;
.
.
.
[b]maxForwardSpeed = 56;[/b] //Change the player's maximum forward speed
//to the maximum speed you want your player to move at
maxBackwardSpeed = 13;
maxSideSpeed = 13;Now, we need to write a function that will enable us to change the player's speed and update this information on the client. At the bottom of player.cs add the following function:
function Player::setSpeed(%this,%speed)
{
%this.speedMod=%speed;
//Now tell the client that we've sped up the player
commandToClient(%this.client,'SpeedChanged',%this.speedMod);
}When we call this function on the player, the speed will be stored in a variable called speedMod, then we use commandToClient to notify the client of the change. We will write the client-side function in a short while.
Now that we have a way to change player speed, we need to make sure that the player starts off at normal speed, so add this at the bottom of Armor::onAdd:
%obj.setSpeed(0.25);
Now, when a player is added to the level, we immediately set their speed to 0.25 times their maximum speed (0.25*56 = 14), which in our case is the normal speed.
-----------------------------------------------------------------------------------
~/client/scripts/default.bind.cs
It is here that the client will update the movement of the player. First off, we need a function to respond to the commandToServer we wrote in player.cs above. Add this somewhere in the file:
function clientCmdSpeedChanged(%speed)
{
$SpeedFactor=%speed;
//Check to see if the user is already moving forward
if ($mvForwardAction)
$mvForwardAction=$SpeedFactor; //User is moving forward so update their speed
}When the server sends the client the player's speed, we store that in a global variable called $SpeedFactor. Then, we need to check if the player is already moving forward. If they are then we need to update their speed.
Finally, modify the function moveForward as follows:
function moveforward(%val)
{
$mvForwardAction = %val * $SpeedFactor;
}If a key is pressed (%val is 1), then the player will move at the speed defined in $SpeedFactor.
This way, whenever we want to change the player's speed, use player.setSpeed, and this updates the client to use the new speed.
-----------------------------------------------------------------------------------
~/server/scripts/speedPickup.cs
To demonstrate the use of these functions, here is a speed pickup which will speed up the player for 5 seconds when picked up. You'll need to create a new file called speedPickup.cs and place it in ~/server/scripts/. Add the following code to it:
//Speed pickup
datablock ItemData(SpeedPickup)
{
shapeFile="~/data/shapes/items/speedPickup.dts"; //NOTE: You will need to provde
//your own model for the item, or
//just use one of the models already there.
category="Items";
};
function SpeedPickup::onCollision(%this,%obj,%col)
{
//Check to see that the collided object is a player
if (%col.getType() & $TypeMasks::PlayerObjectType)
{
//Hide the shape and show it after 5 seconds
%obj.setHidden(true);
%obj.schedule(5000,"setHidden",false);
%obj.schedule(5000,"startFade",500,0,false);
//Now speed up the player
%col.setSpeed(1); //1 = max speed of 56
//After 5 seconds, we want to slow the player down.
%col.schedule(5000,"setSpeed",0.25); //0.25 = regular speed of 14
}
}When a player collides with the item, the item is hidden, and then we schedule it to reappear after 5 seconds. Next, we set the player's speed to maximum. We also schedule the player to go back to their original speed after 5 seconds. This way, when the player picks up the speed pickup, there speed will be boosted to max for 5 seconds, then go back to normal.
-----------------------------------------------------------------------------------
~/server/scripts/game.cs
Because we added a new script file, we need to tell the game to load it when we play. Simply add the following line to the bottom of onServerCreated:
exec("./speedPickup.cs");Once all the code is in, start up a level and add the speed pickup somewhere (you'll find it under the Items category). Then, run over it and watch how the player speeds up.
-----------------------------------------------------------------------------------
Adding a sprint key
Adding a sprint key is quite simple and works like this: when the player presses the sprint key, the client sends a command to the server telling it that the player wants to sprint. The server then uses the setSpeed function above, which sets the player's speed and updates the client with a new speed. Follow the steps below to add a sprint key to your game.
-----------------------------------------------------------------------------------
~/client/scripts/default.bind.cs
Only one thing we need to do here, and that's adding a sprint key and the function that handles it. Add the following somewhere in the file:
function sprint(%val)
{
commandToServer('sprint',%val);
}
moveMap.bind(keyboard,lshift,sprint);The last command binds left shift to the sprint function. When left shift is pressed, %val is 1, so the function sends a 1 to the server. When the key is released %val is 0 so the server receives a 0. Next, we'll add the server side sprint function.
-----------------------------------------------------------------------------------
~/server/scripts/player.cs
Here is where the work is done. Depending on the value the server gets from the client, the server will either boost the player or bring them back to their normal speed. Add the following at the bottom of the file:
function serverCmdSprint(%client,%val)
{
if (%val)
%client.player.setSpeed(1); //The client wants to sprint, so boost the player's speed up to max
else
%client.player.setSpeed(0.25); //Bring the player's speedback to normal (0.25 * max speed)
}To test the code, first delete the file ~/client/config.cs. This will allow the new key command we added to take effect. After that, simply run your game and hold down left shift to sprint.
#2
I have tested this with multiplayer and it works fine. When one client picks up the speed, the other clients are not affected by it at all.
If you look through my code, you'll see that when a player's speed changes, only that particular client gets the speed update and so no other clients are affected.
Give it a go yourself, and if you run into any problems, let me know.
--Amr
03/26/2007 (1:52 pm)
Hello Vincent.I have tested this with multiplayer and it works fine. When one client picks up the speed, the other clients are not affected by it at all.
If you look through my code, you'll see that when a player's speed changes, only that particular client gets the speed update and so no other clients are affected.
Give it a go yourself, and if you run into any problems, let me know.
--Amr
#3
BAM! we've got ourselfes a cheatcode :-P
edit: typo
03/26/2007 (2:20 pm)
Just an hour after i posted my comment i thought about it again and came to the conclusion that it would probably work. But what if a client would type the following in the console (in case we use your code as a speed up)$mvForwardAction = 1;
BAM! we've got ourselfes a cheatcode :-P
edit: typo
#4
--Amr
EDIT: Reworded the comment
03/26/2007 (2:34 pm)
You're perfectly right about the cheat code issue. However, I believe it might not be so much of an issue, since it is probably not a good idea to give clients access to the console during a multiplayer game anyway, and in single player, it doesn't matter if a player wants to cheat or not. Regardless, I've made a note of your issue in the resource so people can make theirown minds up.--Amr
EDIT: Reworded the comment
#6
03/27/2007 (7:15 am)
Cool work-around with no engine changes, good job. :)
#7
I was looking for this for a frog man! (I'll just change speed for jump)
Thanks!
04/14/2007 (8:23 am)
Great!I was looking for this for a frog man! (I'll just change speed for jump)
Thanks!
#8
anyone got any ideas?
thanks in advance
great tutorial by the way...
05/23/2007 (3:05 pm)
i've been trying to increase player speed by setting it to a key, like using shift to sprint, instead of using power ups... but I don't know how to do it.anyone got any ideas?
thanks in advance
great tutorial by the way...
#9
When the client detects that the sprint button has been pressed, it should use commandToServer to tell the server that it wants to sprint.
The server receives this request, and uses the Player::setSpeed function to change the player's speed. The code in the example should do the rest.
--Amr
05/24/2007 (5:03 am)
In brief, all you'd need to add to the existing code is 2 functions:When the client detects that the sprint button has been pressed, it should use commandToServer to tell the server that it wants to sprint.
The server receives this request, and uses the Player::setSpeed function to change the player's speed. The code in the example should do the rest.
--Amr
#10
the AFX kit alters the default.bind.cs and aiPlayer.cs files. The following will help anyone that is trying to implement this resource into TGE with AFX.
Follow the steps that Amr has provided above.
in "~/client/scripts/default.bind.cs"
change :
to this :
Still in the default.bind.cs file, change these functions:
to this (note, we are passing the %val variable):
Now we have one more change to make so that the ai in the demo doesn't over run their path nodes.
in "~/server/scripts/aiPlayer.cs
in the AIPlayer::spawmAt(%name, %xfm, %dead) function, change:
to this:
This should get this wonderful resource working in TGE 1.5.1 with AFX.
[edit]
I put the changes in bold
05/29/2007 (5:16 pm)
This is an awesome resource, but I wanted to add a few things in case there are other AFX + TGE 1.5.1 users out there.the AFX kit alters the default.bind.cs and aiPlayer.cs files. The following will help anyone that is trying to implement this resource into TGE with AFX.
Follow the steps that Amr has provided above.
in "~/client/scripts/default.bind.cs"
change :
function moveforward()
{
if ($move_fwd_1 || $move_fwd_2 || $move_fwd_3)
$mvForwardAction = $movementSpeed;
else
$mvForwardAction = 0;
}to this :
function moveforward([b]%val[/b])
{
if ($move_fwd_1 || $move_fwd_2 || $move_fwd_3)
{
[b]//$mvForwardAction = $movementSpeed; // WFB - Removed for Speed Boost
$mvForwardAction = %val * $SpeedFactor;[/b]
}
else
$mvForwardAction = 0;
}Still in the default.bind.cs file, change these functions:
function moveforward_1(%val)
{
$move_fwd_1 = %val;
moveforward();
}
//
function moveforward_2(%val)
{
$move_fwd_2 = %val;
moveforward();
}
//
function moveforward_3(%val)
{
$move_fwd_3 = %val;
moveforward();
}to this (note, we are passing the %val variable):
function moveforward_1(%val)
{
$move_fwd_1 = %val;
moveforward([b]%val[/b]);
}
//
function moveforward_2(%val)
{
$move_fwd_2 = %val;
moveforward([b]%val[/b]);
}
//
function moveforward_3(%val)
{
$move_fwd_3 = %val;
moveforward([b]%val[/b]);
}Now we have one more change to make so that the ai in the demo doesn't over run their path nodes.
in "~/server/scripts/aiPlayer.cs
in the AIPlayer::spawmAt(%name, %xfm, %dead) function, change:
%ai_player.setMoveSpeed(0.4);
to this:
[b]%ai_player.setMoveSpeed(0.15);[/b]
This should get this wonderful resource working in TGE 1.5.1 with AFX.
[edit]
I put the changes in bold
#11
Thanks!
Tony
06/15/2007 (8:44 am)
@Amr - As a non-programmer, can you tell how to add the two functions to create the sprint feature that Roy mentioned?Thanks!
Tony
#12
I've added step by step instruction on how to add a sprint key to your game at the bottom of the resource. I hope you all find it useful!
--Amr
06/15/2007 (10:02 am)
Hello all,I've added step by step instruction on how to add a sprint key to your game at the bottom of the resource. I hope you all find it useful!
--Amr
#13
Here's what I have so far-
in player.cs
MaxForwardSpeed = 50;
in default.bind.cs I've changed
$movementSpeed = 0.2;
This works fine. My player now jogs at a leisurely 10 m/s.
Then (still in default.bind.cs) I added this
function sprint(%val)
{
$mvForwardAction = %val * $movementSpeed * 5;
}
and further down (still in default.bind.cs) I added
moveMap.bind(keyboard, "alt w", sprint);
Then, I deleted the config.cs file, deleted all my .dso's, and ran the game. It works! I jog at 10 m/s using the W key, and I run full speed using ALT+W to sprint.
No server calls. Just change two values and add a sprint function to default.bind.cs
Tony
06/18/2007 (11:04 am)
@Amr - I think I did it with a simple key.bind.Here's what I have so far-
in player.cs
MaxForwardSpeed = 50;
in default.bind.cs I've changed
$movementSpeed = 0.2;
This works fine. My player now jogs at a leisurely 10 m/s.
Then (still in default.bind.cs) I added this
function sprint(%val)
{
$mvForwardAction = %val * $movementSpeed * 5;
}
and further down (still in default.bind.cs) I added
moveMap.bind(keyboard, "alt w", sprint);
Then, I deleted the config.cs file, deleted all my .dso's, and ran the game. It works! I jog at 10 m/s using the W key, and I run full speed using ALT+W to sprint.
No server calls. Just change two values and add a sprint function to default.bind.cs
Tony
#14
function moveforward(%val)
{
$mvForwardAction = %val * $SpeedFactor + $movementSpeed;
}
@Infinitum3D
wow your idea worked very well and does not have the diagonal movement problem i had earlier. Works in multiplayer without any problems as well..
06/22/2007 (4:31 pm)
has anyone else noticed that with this resource the player can no longer move diagonally foward is strafe left + foward ???? is there a way to fix this if u add $movementSpeed back you can move diagonally foward again but you player model also does not stop moving foward either .. function moveforward(%val)
{
$mvForwardAction = %val * $SpeedFactor + $movementSpeed;
}
@Infinitum3D
wow your idea worked very well and does not have the diagonal movement problem i had earlier. Works in multiplayer without any problems as well..
#15
edit: engine changes as you do not wish will be a solution as it modifies the max speed, and you cant cheat that.
07/27/2007 (5:37 am)
hmmm i dont want to spoil things, but you can cheat without a console. clients are capable of modifying client scripts and still be able to play on the same server (as long as they dont change server scripts). so you can just in the function change the $SpeedFactor, no console needed. am I not right?edit: engine changes as you do not wish will be a solution as it modifies the max speed, and you cant cheat that.
#16
07/27/2007 (7:38 am)
You are perfectly right thijs. I imagine someone could compile his own client side function insde the pref.cs file. I havent tested this but changing speed clientside just isn't hack/cheat safe.
#17
07/29/2007 (7:40 am)
Thanks for the resource, this saved me some time!
#18
I'm new to TGE, so there may be other issues ... but if you don't distribute your source code, it makes it difficult to modify it.
07/29/2007 (8:42 am)
If you would like to keep a user from cheating by editing scripts, only distribute with the ".dso" version of the scripts - these are compiled and not editable (explained at: http://www.garagegames.com/mg/forums/result.thread.php?qt=33228 ).I'm new to TGE, so there may be other issues ... but if you don't distribute your source code, it makes it difficult to modify it.
#19
And ive just thought of another way to cheat (dont know if it works).
- Change the name of a .dso (client side)
- create a new .cs file with the old name of the just deleted .dso
- in that file add some kind of function to change your speed and exec(..[the path to .dso with new name])
this way you would still have all the functionality but if it doesn't work one could just delete some not important .gui file and replace that with some speed hack funtions.
Im just trying to help. This is still great for prototyping and/or singleplayer games
07/30/2007 (2:28 am)
The pref.cs is automaticaly generated on exit and compiled at start by default because else your prefences would'nt be saved. So this file could be edited.And ive just thought of another way to cheat (dont know if it works).
- Change the name of a .dso (client side)
- create a new .cs file with the old name of the just deleted .dso
- in that file add some kind of function to change your speed and exec(..[the path to .dso with new name])
this way you would still have all the functionality but if it doesn't work one could just delete some not important .gui file and replace that with some speed hack funtions.
Im just trying to help. This is still great for prototyping and/or singleplayer games
#20
01/06/2008 (6:04 am)
Cool stuff! :-) 
Torque 3D Owner Vincent D