Variable/Dynamic Movement
by Chris Cain · 04/14/2005 (2:57 pm) · 19 comments
Maybe you're making an RPG. You need each player to be unique. You could just allow each player to move exactly the same but do different damage. However, you've decided that you could do better, and add a system to make each player move by their own abilities.
Maybe you want to make players have the ability to sprint. They press a button and suddenly their speed increases and their energy decreases.
In either case, you can benefit by using these two ways to create dynamic movement. The first way is simple. It's a pure scripting method which does not require you to change ANYTHING in the engine. However, there is one disadvantage to using this method, and that is that it uses a bunch of datablocks. The second way is more complex. It uses some scripting, but mostly it is changing the way the engine works. I call this method the Variable Movement method, and it is the way I recommend doing unless you really don't want to change anything in the engine.
Method 1: Scripted dynamic movement
This is super simple to impliment, and takes nearly no time to do.
I'm using the starter.fps mod to do this, but you aren't limited to that. Simply adapt it to work with whatever system you want.
In game.cs, I added this above the onServerCreated() function
I also changed the onServerCreated() function
Now, you're might be asking yourself "Why are you limiting it to 64???" Well, I limit it to that because if you add too many datablocks in total, then things start to act weird. Plus, too many datablocks will make loading for clients extremely slow. I limited it to 64 mainly because there shouldn't be that much need to go above 64 players. If there is, then feel free to increase it, but be forewarned, The maximum number of datablocks I've been able to do without any problems is about 1000 or so. If you have a massive game with that many datablocks, then you will be limited on how many unique players you can make. If you're making an RPG, you might hit this 1000 datablock limit, and in addition, you might want more players too.
The next step to this method is entirely up to you. You can access the player's datablock like this:
Then have a function that checks for open unique datablocks when the player spawns. This is a good starting point:
You need to make sure that when it checks for open slots if it gets a false, then it needs to boot the player because there isn't enough datablocks. This could be caused by an increase in the number of slots a server has while it is running. There might be ways around this (perhaps having some extra datablocks just in case) but there is no way to do it efficiently, which is why I opted the Variable Movement method in the next section.
Now, you might be wondering, "Why did you say you could use sprinting?" Well, the client just needs to send to the server a command, perhaps to toggle on sprinting, and then the server can change his datablock to increase his maxForwardSpeed, maxSideSpeed, and maxBackwardSpeed... as well as add drain for running. I won't go into detail on how to do this, but I hope that this is a big enough hint.
Method 2: Variable Movement
Why call it Variable Movement? Simple: You change how the player moves by the variables attached to his body. Not only that, but variable can also mean dynamic (if you look at it the right way :P). So, this name basically says it all. This is a dynamic movement system based on variables attached to the player, thus I call it Variable Movement. ... Oh yeah, and its Variable Movement, because its very able to make your player move the way you want! :)
Okay okay, I'll stop with the stalling. I'm going to show you the simple part of variable movement, and how it is set up. I won't go into incredible detail, I'll just cover most of the idea.
The two modified files are player.cc and player.h. First open up player.h so that we can get the simple part done.
In player.h
All of that code is in class Player, so there shouldn't be any confusion.I also showed you where I put it, but as long as you keep it in the general area, it should be fine.
First, you need to declare the variables that will be used. Second, you have to add the initPersistFields(); line. Normally, players don't have any different fields than all the way up to GameBase. Thus, I had to add a way to transfer the script data to the engine. Easily done.
Now, just close player.h, because its completely done.
In player.cc, you have a few more things to do.
First, you have to modify Player::Player()
This sets all the variables to 0 initially, which I will set up to mean that it will default to the datablock's description.
Then, right before Player::onAdd(), add this. (it should only be a few lines below Player::Player())
Just add the whole thing. This basically gets information from the player object. I named them by what the datablock calls them, but you don't have to name it that. Remember what you set them to though. %player.runForce will change the runForce for that specific player.
Next, you have to modify Player::updateMove(const Move* move)
This is where you replace things in order to make Variable Movement work.
Now, a good thing to note, You should make it so if it is 0 or less then it defaults to the datablock, but I simply set it to 0 for example.
And lastly, you have to make it so that the clients are up to date. If you don't do this, the code will work, but movement will become very jerky as soon as you change the character's speed or force.
The order is EXTREMELY IMPORTANT. If there is a great enough variation between the order in which you set these up, it will cause the engine to crash. I learned this the hard way. I recommend putting it up near the top, because if you put it at the bottom, most of the time it won't get called and will still cause it to be jerky.
You can make it so everything that is set in a datablock is dynamic this way, but I just wanted to make it so running was dynamic. In nearly the same way as method 1, you can impliment soft-coded sprinting. Soft-coded sprinting isn't nessisarily any better than hard coding it, but it might make your life (and anyone who works on your game) a little bit easier. ... If you're even looking at this document, then you know why it's annoying to hardcode things ^_^
Goodluck on your game!!
Maybe you want to make players have the ability to sprint. They press a button and suddenly their speed increases and their energy decreases.
In either case, you can benefit by using these two ways to create dynamic movement. The first way is simple. It's a pure scripting method which does not require you to change ANYTHING in the engine. However, there is one disadvantage to using this method, and that is that it uses a bunch of datablocks. The second way is more complex. It uses some scripting, but mostly it is changing the way the engine works. I call this method the Variable Movement method, and it is the way I recommend doing unless you really don't want to change anything in the engine.
Method 1: Scripted dynamic movement
This is super simple to impliment, and takes nearly no time to do.
I'm using the starter.fps mod to do this, but you aren't limited to that. Simply adapt it to work with whatever system you want.
In game.cs, I added this above the onServerCreated() function
function createUniqueDatablocks (%maxPlayers)
{
echo("Creating " @ %maxPlayers @ " unique players.");
for(%x = 0; %x < %maxPlayers; %x++) // X is 0, while X is less than maxPlayers, do this and add 1 to X
{
eval("datablock PlayerData(UniquePlayer" @ %x @ " : PlayerBody) { };");
}
echo("Done creating unique datablocks.");
}I also changed the onServerCreated() function
function onServerCreated()
{
// Server::GameType is sent to the master server.
// This variable should uniquely identify your game and/or mod.
$Server::GameType = "Power Warriors";
// Server::MissionType sent to the master server. Clients can
// filter servers based on mission type.
$Server::MissionType = "Deathmatch";
// GameStartTime is the sim time the game started. Used to calculated
// game elapsed time.
$Game::StartTime = 0;
// Load up all datablocks, objects etc. This function is called when
// a server is constructed.
exec("./audioProfiles.cs");
exec("./camera.cs");
exec("./markers.cs");
exec("./triggers.cs");
exec("./inventory.cs");
exec("./shapeBase.cs");
exec("./item.cs");
exec("./health.cs");
exec("./staticShape.cs");
exec("./weapon.cs");
exec("./radiusDamage.cs");
exec("./crossbow.cs");
exec("./player.cs");
exec("./chimneyfire.cs");
exec("./aiPlayer.cs");
[B] if ($Pref::Server::MaxPlayers > 64) // Done because there is an upper limit to how many datablocks can be made
{ // Change at your own risk. Weird stuff happens when this is changed to too
$Pref::Server::MaxPlayers = 64; // High of a number with the createUniqueDatablocks function.
error("Engine cannot process more than 64 players, resetting player count to 64");
}
createUniqueDatablocks ($Pref::Server::MaxPlayers); // Create the number of datablocks needed.[/B]
// Keep track of when the game started
$Game::StartTime = $Sim::Time;
}Now, you're might be asking yourself "Why are you limiting it to 64???" Well, I limit it to that because if you add too many datablocks in total, then things start to act weird. Plus, too many datablocks will make loading for clients extremely slow. I limited it to 64 mainly because there shouldn't be that much need to go above 64 players. If there is, then feel free to increase it, but be forewarned, The maximum number of datablocks I've been able to do without any problems is about 1000 or so. If you have a massive game with that many datablocks, then you will be limited on how many unique players you can make. If you're making an RPG, you might hit this 1000 datablock limit, and in addition, you might want more players too.
The next step to this method is entirely up to you. You can access the player's datablock like this:
%player.datablock.runForce = 5000; %player.getDatablock().runForce = 5000;Either way will work. This is a simple yet effective way to make every player have their own unique movement. When they are spawned, you just need to have a way to see which datablocks are in use. For each datablock that is in use, simply say:
%player.getDatablock().inUse = true;
Then have a function that checks for open unique datablocks when the player spawns. This is a good starting point:
function checkForOpenDatablock(%maxPlayers)
{
for (%x = 0; %x < %maxPlayers; %x++)
{
if(!eval("UniquePlayer" @ %x @ ".inUse")) // Checks to see if that unique player is in use, if it isn't then it returns
return eval("UniquePlayer" @ %x); // What it is checking.
}
return false; // If there is no open slots, then it returns false.
}You need to make sure that when it checks for open slots if it gets a false, then it needs to boot the player because there isn't enough datablocks. This could be caused by an increase in the number of slots a server has while it is running. There might be ways around this (perhaps having some extra datablocks just in case) but there is no way to do it efficiently, which is why I opted the Variable Movement method in the next section.
Now, you might be wondering, "Why did you say you could use sprinting?" Well, the client just needs to send to the server a command, perhaps to toggle on sprinting, and then the server can change his datablock to increase his maxForwardSpeed, maxSideSpeed, and maxBackwardSpeed... as well as add drain for running. I won't go into detail on how to do this, but I hope that this is a big enough hint.
Method 2: Variable Movement
Why call it Variable Movement? Simple: You change how the player moves by the variables attached to his body. Not only that, but variable can also mean dynamic (if you look at it the right way :P). So, this name basically says it all. This is a dynamic movement system based on variables attached to the player, thus I call it Variable Movement. ... Oh yeah, and its Variable Movement, because its very able to make your player move the way you want! :)
Okay okay, I'll stop with the stalling. I'm going to show you the simple part of variable movement, and how it is set up. I won't go into incredible detail, I'll just cover most of the idea.
The two modified files are player.cc and player.h. First open up player.h so that we can get the simple part done.
In player.h
class Player: public ShapeBase
{
typedef ShapeBase Parent;
protected:
...
S32 mMountPending; ///< mMountPending suppresses tickDelay countdown so players will sit until
///< their mount, or another animation, comes through (or 13 seconds elapses).
[B] // Variable Movement! -- Zshazz
F32 mRunForceMod; // Modifies the runForce
F32 mMaxForwardSpeedMod;
F32 mMaxBackwardSpeedMod;
F32 mMaxSideSpeedMod; [/B]
/// Main player state
enum ActionState {
NullState,
MoveState,
RecoverState,
NumStateBits = 3
};
...
public:
...
// Object control
void setControlObject(ShapeBase *obj);
ShapeBase* getControlObject();
[B] // Variable Movement -- Zshazz
static void initPersistFields(); // Has to be declared so that the engine knows to use my persist fields [/B]
...
};All of that code is in class Player, so there shouldn't be any confusion.I also showed you where I put it, but as long as you keep it in the general area, it should be fine.
First, you need to declare the variables that will be used. Second, you have to add the initPersistFields(); line. Normally, players don't have any different fields than all the way up to GameBase. Thus, I had to add a way to transfer the script data to the engine. Easily done.
Now, just close player.h, because its completely done.
In player.cc, you have a few more things to do.
First, you have to modify Player::Player()
Player::Player()
{
...
mBubbleEmitterTime = 10.0;
mLastWaterPos.set( 0.0, 0.0, 0.0 );
mMountPending = 0;
[B] // Variable Movement -- Zshazz
mRunForceMod = 0.0; // I'm implementing it so that if it is 0, then it does nothing
mMaxForwardSpeedMod = 0.0;
mMaxBackwardSpeedMod = 0.0;
mMaxSideSpeedMod = 0.0;[/B]
}This sets all the variables to 0 initially, which I will set up to mean that it will default to the datablock's description.
Then, right before Player::onAdd(), add this. (it should only be a few lines below Player::Player())
// Variable Movement -- Zshazz
// Had to make the initPersistFields entirely because player doesn't normally have it.
void Player::initPersistFields()
{
Parent::initPersistFields();
// Add the Variable Movement things you want.
addField("runForce", TypeF32, Offset(mRunForceMod, Player));
addField("maxForwardSpeed", TypeF32, Offset(mMaxForwardSpeedMod,Player));
addField("maxBackwardSpeed", TypeF32, Offset(mMaxBackwardSpeedMod,Player));
addField("maxSideSpeed", TypeF32, Offset(mMaxSideSpeedMod,Player));
}Just add the whole thing. This basically gets information from the player object. I named them by what the datablock calls them, but you don't have to name it that. Remember what you set them to though. %player.runForce will change the runForce for that specific player.
Next, you have to modify Player::updateMove(const Move* move)
void Player::updateMove(const Move* move)
{
delta.move = *move;
[B] // Variable Movement -- Zshazz
if (mRunForceMod == 0) // If its 0, then use the standard runForce
mRunForceMod = mDataBlock->runForce; // sets mRunForceMod to DataBlock's if it is 0.
if (mMaxForwardSpeedMod == 0)
mMaxForwardSpeedMod = mDataBlock->maxForwardSpeed;
if (mMaxBackwardSpeedMod == 0)
mMaxBackwardSpeedMod = mDataBlock->maxBackwardSpeed;
if (mMaxSideSpeedMod == 0)
mMaxSideSpeedMod = mDataBlock->maxSideSpeed;[/B]
// Trigger images
if (mDamageState == Enabled) {
setImageTriggerState(0,move->trigger[0]);
setImageTriggerState(1,move->trigger[1]);
}
...
// Clamp water movement
// Variable Movement -- Zshazz
if (move->y > 0)
{
if( mWaterCoverage >= 0.9 )
moveSpeed = getMax(mDataBlock->maxUnderwaterForwardSpeed * move->y,
mDataBlock->maxUnderwaterSideSpeed * mFabs(move->x));
else
[B] moveSpeed = getMax(mMaxForwardSpeedMod * move->y,
mMaxSideSpeedMod * mFabs(move->x)); // Modify this so that it uses Variable Movement[/B]
}
else
{
if( mWaterCoverage >= 0.9 )
moveSpeed = getMax(mDataBlock->maxUnderwaterBackwardSpeed * mFabs(move->y),
mDataBlock->maxUnderwaterSideSpeed * mFabs(move->x));
else
[B] moveSpeed = getMax(mMaxBackwardSpeedMod * mFabs(move->y),
mMaxSideSpeedMod * mFabs(move->x)); // Modify this so that it uses Variable Movement[/B]
}
// Cancel any script driven animations if we are going to move.
if (moveVec.x + moveVec.y + moveVec.z != 0 &&
(mActionAnimation.action >= PlayerData::NumTableActionAnims
|| mActionAnimation.action == PlayerData::LandAnim))
mActionAnimation.action = PlayerData::NullAnimation;
...
// Convert to acceleration
if (pvl)
pv *= moveSpeed / pvl;
VectorF runAcc = pv - (mVelocity + acc);
F32 runSpeed = runAcc.len();
// Clamp acceleratin, player also accelerates faster when
// in his hard landing recover state.
[B] // Variable Movement -- Zshazz
F32 maxAcc = (mRunForceMod / mMass) * TickSec; // Replaced mDataBlock->runForce with mRunForceMod [/B]
if (mState == RecoverState)
maxAcc *= mDataBlock->recoverRunForceScale;
if (runSpeed > maxAcc)
runAcc *= maxAcc / runSpeed;
acc += runAcc;
...
}This is where you replace things in order to make Variable Movement work.
Now, a good thing to note, You should make it so if it is 0 or less then it defaults to the datablock, but I simply set it to 0 for example.
And lastly, you have to make it so that the clients are up to date. If you don't do this, the code will work, but movement will become very jerky as soon as you change the character's speed or force.
U32 Player::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
{
U32 retMask = Parent::packUpdate(con, mask, stream);
if (stream->writeFlag((mask & ImpactMask) && !(mask & InitialUpdateMask)))
stream->writeInt(mImpactSound, PlayerData::ImpactBits);
[B] // Variable Movement -- Zshazz
// Makes it so that everyone is synced up correctly
stream->write(mRunForceMod);
stream->write(mMaxForwardSpeedMod);
stream->write(mMaxBackwardSpeedMod);
stream->write(mMaxSideSpeedMod);[/B]
if (stream->writeFlag(mask & ActionMask &&
mActionAnimation.action != PlayerData::NullAnimation &&
mActionAnimation.action >= PlayerData::NumTableActionAnims)) {
stream->writeInt(mActionAnimation.action,PlayerData::ActionAnimBits);
stream->writeFlag(mActionAnimation.holdAtEnd);
...
}
void Player::unpackUpdate(NetConnection *con, BitStream *stream)
{
Parent::unpackUpdate(con,stream);
if (stream->readFlag())
mImpactSound = stream->readInt(PlayerData::ImpactBits);
[B] // Variable Movement -- Zshazz
stream->read(&mRunForceMod);
stream->read(&mMaxForwardSpeedMod);
stream->read(&mMaxBackwardSpeedMod);
stream->read(&mMaxSideSpeedMod);[/B]
// Server specified action animation
if (stream->readFlag()) {
U32 action = stream->readInt(PlayerData::ActionAnimBits);
bool hold = stream->readFlag();
...
}The order is EXTREMELY IMPORTANT. If there is a great enough variation between the order in which you set these up, it will cause the engine to crash. I learned this the hard way. I recommend putting it up near the top, because if you put it at the bottom, most of the time it won't get called and will still cause it to be jerky.
You can make it so everything that is set in a datablock is dynamic this way, but I just wanted to make it so running was dynamic. In nearly the same way as method 1, you can impliment soft-coded sprinting. Soft-coded sprinting isn't nessisarily any better than hard coding it, but it might make your life (and anyone who works on your game) a little bit easier. ... If you're even looking at this document, then you know why it's annoying to hardcode things ^_^
Goodluck on your game!!
#2
I want different player classes to have different speeds, and this looks like just what the doctor ordered. Thanks!!
04/14/2005 (3:26 pm)
This is a very awesome code snippet, that is, if I can get it working. :)I want different player classes to have different speeds, and this looks like just what the doctor ordered. Thanks!!
#3
also makes me want to change player collission so if i run at another player it impluses them away at a force dependent on my speed :-D
04/14/2005 (6:15 pm)
i lmao when i read dreamers post. trip and fall.. im going to purposely try to do that now. also makes me want to change player collission so if i run at another player it impluses them away at a force dependent on my speed :-D
#4
04/14/2005 (6:51 pm)
The running works as it should, but it crashes when i fire the crossbow :-(
#5
#2 adds alot of extra bits that need processed each tick, in an MMO environment this is going to add ALOT of extra overhead that your server is going to need to process.
04/15/2005 (8:39 am)
This is a very useful resource but, for multiplayer make sure to implement strategy #1 only.#2 adds alot of extra bits that need processed each tick, in an MMO environment this is going to add ALOT of extra overhead that your server is going to need to process.
#6
04/15/2005 (9:15 am)
In an MMO though, wouldn't you then be faced with downloading every single players datablock, I'm guessing that would take awhile?
#7
Ed, I'm sorry, I don't know what could be causing your problem... Have you tried re-implimenting it? I don't think either method touches into anything that could cause a crash when you fire a crossbow... Just so I can check it out, which method are you using?
Dreamer again, for mine, I solved that by adding an update variable which I had toggle on and off using the initPersistFields. That removes pretty much all the overhead unless you need to update. A better way would be to check to see if the movement variables changed in the last tick and then toggle update.
Daniel Brown, On my Athlon 64 3000+ 1GB Ram 2x120GB SATA Harddrives in RAID0, it took 10 seconds to load with almost no datablocks (stock starter.fps), and it took 2 minutes to load with 1000+ datablocks, so it does increase time quite alot, which is one of the reasons I'm using method 2 for my game.
04/16/2005 (9:19 am)
Dreamer, I noticed that in testing. Its quite funny when you're running around at the speed of sound on a completely flat surface and suddenly you stop and fall over dead :-P ... I wonder if theres a way to fix that so that the engine isn't so picky about how fast you go.Ed, I'm sorry, I don't know what could be causing your problem... Have you tried re-implimenting it? I don't think either method touches into anything that could cause a crash when you fire a crossbow... Just so I can check it out, which method are you using?
Dreamer again, for mine, I solved that by adding an update variable which I had toggle on and off using the initPersistFields. That removes pretty much all the overhead unless you need to update. A better way would be to check to see if the movement variables changed in the last tick and then toggle update.
Daniel Brown, On my Athlon 64 3000+ 1GB Ram 2x120GB SATA Harddrives in RAID0, it took 10 seconds to load with almost no datablocks (stock starter.fps), and it took 2 minutes to load with 1000+ datablocks, so it does increase time quite alot, which is one of the reasons I'm using method 2 for my game.
#8
You might also want to look into adjusting the physics of the body through these.
If you reduce mass, drag and density all by a factor of 10, you will launch into the air when you hit one of those little speed bumps, which is MUCH better IMHO than dieing.
04/18/2005 (8:39 am)
@Chris in the PlayerBody datablock look forminImpactSpeed = 45; speedDamageScale = 0.4;Increase minImpactSpeed to something Astronomical like 1,000,000 and reduce speedDamageScale to something like 0.01
You might also want to look into adjusting the physics of the body through these.
mass = 90; drag = 0.3; maxdrag = 0.4; density = 10;
If you reduce mass, drag and density all by a factor of 10, you will launch into the air when you hit one of those little speed bumps, which is MUCH better IMHO than dieing.
#9
05/03/2005 (11:00 pm)
Just a suggestion: I would use ConsoleMethods and a mask to keep those variables from being sent over every tick. Also, I would limit it to sending only 10 or 12 bits of each variable, though that might not be the best of ideas given that these will be used in physics calculations.
#10
05/06/2005 (7:49 pm)
Actually, I made it so there is just a 1 bit variable (mUpdate) which is used the same way as those other variables. If mUpdate is true, then it updates everything and sets update to false. Very efficient that way and you don't have to use consolemethods which would be kinda akward (well... depend on what you like... Personally, I like just saying %player.forwardSpeed = 10000; instead of saying setForwardSpeed(%player, 10000); or %player.setForwardSpeed(10000); )
#11
02/07/2006 (2:06 am)
newbie here but, what is the purpose of putting code in the initPersistFields? the docs say its for adding class member fields.. i do not understand what they mean by "fields" perhaps you are using a different nomenclature?
#12
guess it doesn't matter.
02/12/2006 (12:00 pm)
stupid question... if I wanted to be able to control ALL object's moving ability would I move this code to shapebase instead of player?guess it doesn't matter.
#13
02/12/2006 (12:31 pm)
this is a fun resource.... lol
#14
I have implented this and it compiles fine.. How ever I am having problems figuring out what variable I need to edit now through script to change the players movement...
07/19/2006 (1:11 pm)
Alright, I just might be stupid but here is my problem...I have implented this and it compiles fine.. How ever I am having problems figuring out what variable I need to edit now through script to change the players movement...
#15
Funny thing: if you have a zombie game, with slow zombies, try using noclip mode and watch the from above. Now thats some fucking fast zombies aint it?
Oooh, and im at least equally stupid as Isaac, i dont know which variable to change either, is it some built in commando like the setMaxDamage n stuff?
08/18/2007 (2:40 pm)
Found this resource and incorporated it on like 5 minutes. Meaning great work, its really usefull to. Although, i suggest you add some ethical guidline, if you migh refer to it as that, that helps inexperienced wow freaks from making super mega sonic fast characters, there is a reason to why most games have characters moving 2* IRL speed for average human. Funny thing: if you have a zombie game, with slow zombies, try using noclip mode and watch the from above. Now thats some fucking fast zombies aint it?
Oooh, and im at least equally stupid as Isaac, i dont know which variable to change either, is it some built in commando like the setMaxDamage n stuff?
#16
01/06/2008 (6:02 am)
Cool stuff! :-)
#17
01/25/2008 (9:09 pm)
This resource is very useful, thank you.
#18
07/23/2008 (3:47 am)
Gotta try this out - thanks!
#19
Using Method 2 from above, we are not affecting the whole datablock, just the %player object.
Test variables w/ stock settings.
Sprinting Example
Paste at the end of ~/client/scripts/default.bind.cs
Paste at the end of ~/server/scripts/commands.cs
Tested in multi-player.
Hope this saves some time,
Ari.
01/26/2009 (6:51 pm)
Quote:
Now, you might be wondering, "Why did you say you could use sprinting?" Well, the client just needs to send to the server a command, perhaps to toggle on sprinting, and then the server can change his datablock to increase his maxForwardSpeed, maxSideSpeed, and maxBackwardSpeed... as well as add drain for running. I won't go into detail on how to do this, but I hope that this is a big enough hint.
Using Method 2 from above, we are not affecting the whole datablock, just the %player object.
Test variables w/ stock settings.
localClientConnection.player.maxForwardSpeed =13; localClientConnection.player.maxBackwardSpeed =14; localClientConnection.player.maxSideSpeed =13;
Sprinting Example
Paste at the end of ~/client/scripts/default.bind.cs
function ToggleSprint(%val)
{
if (%val)
commandToServer('ToggleSprintOn');
else
commandToServer('ToggleSprintOff');
}
moveMap.bind(keyboard, "x", "ToggleSprint");Paste at the end of ~/server/scripts/commands.cs
function serverCmdToggleSprintOn(%client)
{
//error("Sprinting ON");
if (isObject(%client.player))
{
%client.player.LastmaxForwardSpeed = %client.player.maxForwardSpeed;
%client.player.maxForwardSpeed = %client.player.maxForwardSpeed * 2;
}
}
function serverCmdToggleSprintOff(%client)
{
//error("Sprinting OFF");
if (isObject(%client.player))
%client.player.maxForwardSpeed = %client.player.LastmaxForwardSpeed;
}Tested in multi-player.
Hope this saves some time,
Ari.

Torque Owner Dreamer
Default Studio Name
Also a fast moving object is harder to track and predict than a slower moving one, might want to scale speed so that we don't get ahead of ourselves in the middle of a tick, and wind up with a "rubber band" effect.