Run Walk speed
by Scottie Sirius · in Torque 3D Professional · 01/13/2010 (6:37 pm) · 20 replies
I'd like to be able to set a slower speed for the walk speed then be able to hold down the ctrl key while walking to increase the speed to a run.
I'm not too worried about changing the animation, just the speed.
How might I go about this?
Thanks!
I'm not too worried about changing the animation, just the speed.
How might I go about this?
Thanks!
About the author
Creating the AlterVerse! www.AlterVerse.com
#2
And then..
Not a whole mess of code changes. Basically what happens is there's a new field under the Player Block called sprintSpeed. It's in the last item in the movement roll-out. This is where you'll want to set the sprint or "run" speed.
To make the player sprint, you'll need some Torque Script (which I, apologize for, do not have at the moment) and a key binding.
If in the console, you can type:
I hope this helps you. I've actually been wondering about this for a while. Now that I'm learning a bit more about the engine I was able to implement it!
01/14/2010 (12:54 am)
Here's a little bit of code I wrote up this night:// in player.h // Find the following, near line 53 F32 maxBackwardSpeed; ///< Maximum backward speed when running F32 maxSideSpeed; ///< Maximum side speed when running // We'll add a new line just below this. // Should look like this now: F32 maxBackwardSpeed; ///< Maximum backward speed when running F32 maxSideSpeed; ///< Maximum side speed when running F32 sprintSpeed; ///< Added code to sprint! // In the same file, around line 550 I added: bool mSprinting; ///< Are we sprinting or no?
And then..
// in player.cpp
// Around 177 look for:
maxBackwardSpeed = 10.0f;
maxSideSpeed = 10.0f;
// Then just below that add a line of code to look like:
maxBackwardSpeed = 10.0f;
maxSideSpeed = 10.0f;
sprintSpeed = 10.0f; // new line
// Find the following around line 513:
addField("upResistSpeed", TypeF32, Offset(upResistSpeed, PlayerData));
addField("upResistFactor", TypeF32, Offset(upResistFactor, PlayerData));
// Just below that we'll want to add a new line of code.
// It should look something like this:
addField("upResistSpeed", TypeF32, Offset(upResistSpeed, PlayerData));
addField("upResistFactor", TypeF32, Offset(upResistFactor, PlayerData));
addField("sprintSpeed", TypeF32, Offset(sprintSpeed, PlayerData)); // new line
// Now, around line 687 look for:
stream->write(maxSideSpeed);
stream->write(runSurfaceAngle);
// And yup.. add a new line below it. Result should look like...
stream->write(maxSideSpeed);
stream->write(runSurfaceAngle);
stream->write(sprintSpeed); // new line
// Around line 834 we need to find:
stream->read(&maxSideSpeed);
stream->read(&runSurfaceAngle);
// And add some code to look like below:
stream->read(&maxSideSpeed);
stream->read(&runSurfaceAngle);
stream->read(&sprintSpeed); // new line
// Now around line 1036 you should see:
mNSLinkMask = LinkSuperClassName | LinkClassName;
mPhysicsPlayer = NULL;
// You want to add the following line to look like:
mNSLinkMask = LinkSuperClassName | LinkClassName;
mPhysicsPlayer = NULL;
///< Don't need this space but make the code cleaner
mSprinting = false; ///< new line
// Now around line 1813 we'll actually need to REPLACE the following
else // StandPose
moveSpeed = getMax(mDataBlock->maxForwardSpeed * move->y,
mDataBlock->maxSideSpeed * mFabs(move->x));
//With is new code
else // StandPose
{
if( !mSprinting )
moveSpeed = getMax(mDataBlock->maxForwardSpeed * move->y,
mDataBlock->maxSideSpeed * mFabs(move->x));
else
moveSpeed = getMax(mDataBlock->sprintSpeed * move->y,
mDataBlock->maxSideSpeed * mFabs(move->x));
}
// Ok, near 4486 you should find:
// Ghost need energy to predict reliably
stream->writeFloat(getEnergyLevel() / DataBlock->maxEnergy,EnergyLevelBits);
// You'll want to make it look like:
// Ghost need energy to predict reliably
stream->writeFloat(getEnergyLevel() / mDataBlock->maxEnergy,EnergyLevelBits);
stream->writeFlag(mSprinting); // new line
// All most done! Around 4652 you'll find:
F32 energy = stream->readFloat(EnergyLevelBits) * mDataBlock->maxEnergy;
setEnergyLevel(energy);
// Add a line of code:
F32 energy = stream->readFloat(EnergyLevelBits) * mDataBlock->maxEnergy;
setEnergyLevel(energy);
mSprinting = stream->readFlag(); // new line
// And finally!!!
// After or around line 4660 you'll want to add in a ConsoleMethod:
ConsoleMethod( Player, Sprint, void, 3, 3, "Sets the player to sprint.")
{
object->mSprinting = dAtob(argv[2]);
}Not a whole mess of code changes. Basically what happens is there's a new field under the Player Block called sprintSpeed. It's in the last item in the movement roll-out. This is where you'll want to set the sprint or "run" speed.
To make the player sprint, you'll need some Torque Script (which I, apologize for, do not have at the moment) and a key binding.
If in the console, you can type:
// To Sprint clientGroup.getObject(0).Player.Sprint(1); // To Stop Sprint clientGroup.getObject(0).Player.Sprint(0);
I hope this helps you. I've actually been wondering about this for a while. Now that I'm learning a bit more about the engine I was able to implement it!
#3
I guess it should at least get some one on the right track.
01/14/2010 (1:38 am)
Hmmm... not sure if this is a fluke or the code.. but after the change, my player seems to have "super jump" enabled and while in the air can move really fast... Player also seems to studder when running with sprint.I guess it should at least get some one on the right track.
#4
01/14/2010 (9:35 am)
That addresses sprinting, but to address variable speeds, what needs to be done is to stop relying on the datablocks for speed references altogether and to create a variable in the Player class that is networked that carries the speed variable. There are resources out there that show how to do this with TGE/TGEA, and the changes are not at all incompatible with T3D. Hope that helps.
#5
For example, you would use %player.setSpeedMult(1.5) for sprinting. It basically works a lot like the code up above, but instead of adding another datblock value, I add a networked F32 to the Player class and the appropriate set/get functions.
01/14/2010 (4:26 pm)
What I do is create a speed multiplier on the Player class I can access through script. This way, I can still use the datablocks to establish base values for different types of movement (swimming, crouching, prone) and can change the speed by script for things like sprinting, leg injury, equiped items, etc.For example, you would use %player.setSpeedMult(1.5) for sprinting. It basically works a lot like the code up above, but instead of adding another datblock value, I add a networked F32 to the Player class and the appropriate set/get functions.
#6
By changing it on the server and waiting for ghosting or a NetEvent to send the new speed to the client you'll have a few ticks where the client is out of sync to the server and you'll get a correction packet.
Depending on how drastic your speed changes are and how good your connection is this can be ok or it can be really bad. Making a sprint mode a feature of the Player C++ and toggled via one of the Move.trigger[]s will yeild the best multiplayer quality/performance.
01/14/2010 (7:55 pm)
Having the limits for speeds in the datablock and having the player C++ code do the logic for changing speeds ensures properly client side prediction.By changing it on the server and waiting for ghosting or a NetEvent to send the new speed to the client you'll have a few ticks where the client is out of sync to the server and you'll get a correction packet.
Depending on how drastic your speed changes are and how good your connection is this can be ok or it can be really bad. Making a sprint mode a feature of the Player C++ and toggled via one of the Move.trigger[]s will yeild the best multiplayer quality/performance.
#7
Coding the basics is easy, but I think what I'm trying to understand is, should I create a new pose or similar? I'm looking in both source and TS for what you suggest Tom. When you mentioned toggle a move trigger I assumed pose.
Could you elaborate a little more, Tom?
01/14/2010 (9:27 pm)
I don't mean to hijack, but I've got a question or 2 regarding the comments.Coding the basics is easy, but I think what I'm trying to understand is, should I create a new pose or similar? I'm looking in both source and TS for what you suggest Tom. When you mentioned toggle a move trigger I assumed pose.
Could you elaborate a little more, Tom?
#8
01/14/2010 (9:59 pm)
Not sure if this is at all helpful in the move change speed thang, but I'm using a half/walk speed source update posted here by Ivan.
#9
01/14/2010 (10:19 pm)
I can see that working (but same can be said about my code change). Not saying that it won't work, I'm not 100% sure where I'd add this, but I'll play with it.
#10
What I then did was remove the code in the player class for "mSprinting"
Then changed my post code a little
I guess what was happening before was that every tick (32ms ?), the updatemove function has to get the boolean from the network stream.
01/15/2010 (12:48 am)
Ok, so I got the sprint code to work smooth! I took Tom's advice (I think) and created a new trigger.// Torque Script
// default.bind.cs
function doSprint()
{
$mvTriggerCount5++;
}
moveMap.bind(keyboard, r, doSprint);What I then did was remove the code in the player class for "mSprinting"
Then changed my post code a little
// FROM THIS
else // StandPose
{
if( !mSprinting )
moveSpeed = getMax(mDataBlock->maxForwardSpeed * move->y,
mDataBlock->maxSideSpeed * mFabs(move->x));
else
moveSpeed = getMax(mDataBlock->sprintSpeed * move->y,
mDataBlock->maxSideSpeed * mFabs(move->x));
}
// TO THIS
else // StandPose
{
if( move->trigger[5] ) // will this even work?! ... actually yes
moveSpeed = getMax(mDataBlock->sprintSpeed * move->y,
mDataBlock->maxSideSpeed * mFabs(move->x));
else
moveSpeed = getMax(mDataBlock->maxForwardSpeed * move->y,
mDataBlock->maxSideSpeed * mFabs(move->x));
}I guess what was happening before was that every tick (32ms ?), the updatemove function has to get the boolean from the network stream.
#11
You have to edit scripts/client/default.bind.cs
And that's done. No need to change engine's code to do such things. I think that Ctrl key is not easy to use in such way. I prefere shift.
Edit :
To change Player Max Speed, you have to change in art/datablocks/player.cs the following lines :
01/15/2010 (5:03 am)
Here is a solution to change dynamically the speed by pressing the shift or Ctrl key like Oblivion does : You have to edit scripts/client/default.bind.cs
// UPDATE THIS LINE
$movementSpeed = 0.2; // m/s => No ! 1 = Max Player Speed definied in its dataBlock... AND you can't go over 1.
// ADD THESE LINES
$runMoreSpeed = 0.8;
$lShiftDownState = 0;
$lForwardState = 0;
// ADD THIS FUNCTION
function setForwardSpeed()
{
if ($lForwardState==1)
{
$mvForwardAction = $movementSpeed + $lShiftDownState*$runMoreSpeed;
} else
{
$mvForwardAction = 0;
}
}
// UPDATE THIS FUNCTION
function moveforward(%val)
{
// $mvForwardAction = %val * $movementSpeed;
$lForwardState = %val;
setForwardSpeed();
}
// ADD THIS FUNCTION
function moveforwardRun(%val)
{
$lShiftDownState = %val;
setForwardSpeed();
}
// ADD this line depending on your fingers size :
//moveMap.bind( keyboard, lshift, moveforwardRun );
moveMap.bind( keyboard, lcontrol, moveforwardRun );And that's done. No need to change engine's code to do such things. I think that Ctrl key is not easy to use in such way. I prefere shift.
Edit :
To change Player Max Speed, you have to change in art/datablocks/player.cs the following lines :
runForce = 48 * 900; // 48*90; // AND maxForwardSpeed = 80; // 8;:)
#12
Otherwise, you'll need to dig in a bit and scale run energy drain by the value of the forward move. This way you can have normal running drain less than the regenRate, while full running (sprinting) drains more than the regen, causing you to lose "stamina."
For an easy solution to "stamina" drain in any walk/run/sprint type setup, you could just use the variable moveSpeed as your run energy drain instead of the datablock value (moveSpeed is a variable in Player::updateMove). Technically, moveSpeed is too large (~8 when forward in standpose), so divide it by something appropriate for a scale you like. By adjusting the energy regen and the scale of moveSpeed, you can come up with a balance where energy regens while walking or crouching, stays neutral while running normally, and drains when sprinting. Make both of these values smaller but keep their relative scales in order to get slower stamina regen and drain.
Run energy drain happens around line 1879, "mEnergy -= mDataBlock->runEnergyDrain;"
01/16/2010 (5:51 am)
The only problem with either of these setups is that you have no restrictions on your sprinting. If that's what you want, problem solved.Otherwise, you'll need to dig in a bit and scale run energy drain by the value of the forward move. This way you can have normal running drain less than the regenRate, while full running (sprinting) drains more than the regen, causing you to lose "stamina."
For an easy solution to "stamina" drain in any walk/run/sprint type setup, you could just use the variable moveSpeed as your run energy drain instead of the datablock value (moveSpeed is a variable in Player::updateMove). Technically, moveSpeed is too large (~8 when forward in standpose), so divide it by something appropriate for a scale you like. By adjusting the energy regen and the scale of moveSpeed, you can come up with a balance where energy regens while walking or crouching, stays neutral while running normally, and drains when sprinting. Make both of these values smaller but keep their relative scales in order to get slower stamina regen and drain.
Run energy drain happens around line 1879, "mEnergy -= mDataBlock->runEnergyDrain;"
#13
01/16/2010 (6:52 am)
You also simply have to update art/datablocks/player.cs the following line : runEnergyDrain = 5; //0;No engine's code update :)
#14
Will that actually interrupt the current move state on the fly?
eg: If I'm holding down forwards (walking) and I press Shift to start sprinting ... will I go back to walking when I release Shift? Or would I have to release and then repress forwards to realter my speed/move type?
01/16/2010 (12:32 pm)
@VincentWill that actually interrupt the current move state on the fly?
eg: If I'm holding down forwards (walking) and I press Shift to start sprinting ... will I go back to walking when I release Shift? Or would I have to release and then repress forwards to realter my speed/move type?
#15
Yes it's totally dynamic. When You press forward Key, start to walk... Then 1 second later you press shift (while pressing forward key), it runs. Three second later, you release shift (forward key always down), so you come back to walk speed. If you maintain shift key down, but not forward key, you will stop. You can also hold Shift, then forward key to run immediately. No need to repress. It's totally the same gameplay that many rpg like oblivion.
More, your running animation will also slow down ($mvForwardAction acts on animation speed). If you want to change animation you can simply call different playthread in setForwardSpeed function.
I Will perhaps release this simple source as ressource. Many users asked these last years about such system. And today I wasn't able to find a working solution on TorquePowered site.
01/16/2010 (1:39 pm)
@Steve,Yes it's totally dynamic. When You press forward Key, start to walk... Then 1 second later you press shift (while pressing forward key), it runs. Three second later, you release shift (forward key always down), so you come back to walk speed. If you maintain shift key down, but not forward key, you will stop. You can also hold Shift, then forward key to run immediately. No need to repress. It's totally the same gameplay that many rpg like oblivion.
More, your running animation will also slow down ($mvForwardAction acts on animation speed). If you want to change animation you can simply call different playthread in setForwardSpeed function.
I Will perhaps release this simple source as ressource. Many users asked these last years about such system. And today I wasn't able to find a working solution on TorquePowered site.
#16
01/16/2010 (3:40 pm)
@Vincent - I think I've done everything exactly as you said but it doesn't work for me at all. There's no change in speed whatsoever...I don't get it!
#17
@Scot: Vincent's code looks completely functional, the basic concept of using a 0-1 value for move->y ($mvForwardAction becomes move->y in updateMove) is exactly how gamepad and joystick controls would handle the ability to walk or run based on how far you move the stick. Are you sure you actually replaced the existing moveforward function instead of adding a second one? Or, is it possible you have lcontrol (or whatever key you picked) bound to another function later in the default.bind.cs?
01/16/2010 (4:32 pm)
@Vincent: Yeah, though that only works if you're okay with run and sprint and crouch all draining the same amount, because the code simply drains runEnergyDrain every tick regardless of move speed, which was why I suggested scaling it. If you want to stick totally to script, isn't the energy regen rate definable by a script function (setRegenRate, something like that)? You could set a static runEnergyDrain and change the regenRate to create the desired effect (scale the regenRate to an inverse of the move speed). I'm not trying to argue against your method, it's honestly the cleanest way to handle sprinting, just thought some people may want a full stamina model with it.@Scot: Vincent's code looks completely functional, the basic concept of using a 0-1 value for move->y ($mvForwardAction becomes move->y in updateMove) is exactly how gamepad and joystick controls would handle the ability to walk or run based on how far you move the stick. Are you sure you actually replaced the existing moveforward function instead of adding a second one? Or, is it possible you have lcontrol (or whatever key you picked) bound to another function later in the default.bind.cs?
#18
Let's check some points :
try to put in moveforward and moveforwardRun functions
0 When a key is down (w/z for forward movement or shift for shift key),
1 When a key is Up
Also, try to seek in your code $movementSpeed var. and check if it is not initialised with any value anywhere else.
Finally, if it always not work, try to put a trace in setForwardSpeed, at the end of the function.
You should obtain "Speed : 0.2" when walking, and "Speed : 1" when running.
EDIT : Also check you have a clean T3D engine (direct from download). Especially with no changes previously done in this post. For my solution, you do not have to change engine's scource code.
end of EDIT
@Henry :
Yes, If I post a ressource, I will take your arguments in care. Stamina is a part of the final solution. Animations, GUI liking, smooth speed increase/decrease and perhaps some others are also part of a ressource.
01/16/2010 (6:16 pm)
@Scot : Let's check some points :
try to put in moveforward and moveforwardRun functions
echo("Key Forward/Shift (depending on function)"@%val);You should obtain the following results in console : 0 When a key is down (w/z for forward movement or shift for shift key),
1 When a key is Up
Also, try to seek in your code $movementSpeed var. and check if it is not initialised with any value anywhere else.
Finally, if it always not work, try to put a trace in setForwardSpeed, at the end of the function.
echo("Speed : "@$movementSpeed);You should obtain "Speed : 0.2" when walking, and "Speed : 1" when running.
EDIT : Also check you have a clean T3D engine (direct from download). Especially with no changes previously done in this post. For my solution, you do not have to change engine's scource code.
end of EDIT
@Henry :
Yes, If I post a ressource, I will take your arguments in care. Stamina is a part of the final solution. Animations, GUI liking, smooth speed increase/decrease and perhaps some others are also part of a ressource.
#19
Thank you for posting your example, Vincent!
It also works in AFX2, where AFX has additional "forwards" by inserting the four ADDs in Vincent's code example but doing this with the UPDATE:
06/30/2011 (6:54 am)
This works just great for me in a B3/AFX+UAISK build. Thank you for posting your example, Vincent!
It also works in AFX2, where AFX has additional "forwards" by inserting the four ADDs in Vincent's code example but doing this with the UPDATE:
function moveforward()
{
if ($move_fwd_1 || $move_fwd_2 || $move_fwd_3)
{
setForwardSpeed(); // <-- Added per VB
$mvForwardAction = $movementSpeed;
} else
$mvForwardAction = 0;
}
//
function moveforward_1(%val)
{
$move_fwd_1 = %val;
$lForwardState = $move_fwd_1; // <-- Added per VB
moveforward();
}
//
#20
Vincent, have you posted your resource yet? I now have my speed, walk, and sprint animations playing on demand, but it would be nice to check against your documentation to make sure I haven't missed something.
07/16/2011 (7:10 am)
Thank you, Vincent! This was exactly what I've been searching a long time for--and thank you, Netwyrm, for posting and making this thread jump to the top of the Forum!Vincent, have you posted your resource yet? I now have my speed, walk, and sprint animations playing on demand, but it would be nice to check against your documentation to make sure I haven't missed something.
Torque 3D Owner Ted Southard