Adding new positions and moves; ie swim, crouch, crawl, prone
by Erik Madison · 06/23/2003 (1:33 pm) · 166 comments
This is loosely based on the only 2 forum threads I found dealing with swimming. Neither of them worked well enough for my needs, so I began this as a dirty rewrite. I also solved quite a few of my problems by reading and rereading Daniel Nielsens tutorials and resources.
I'm not real adept at showing anyone how or why I do something, so I apologize if this resource isn't as clear as it could be. I will try and work out any problems you may have.
Only 2 files are changed for the meat of the system, along with 2 scripts for a keybinding and callback access.
FILE: Which file to edit
FILE: Player.cc
FILE: player.h
FILE: default.bind.cs
FILE: commands.cs
I'm not real adept at showing anyone how or why I do something, so I apologize if this resource isn't as clear as it could be. I will try and work out any problems you may have.
Only 2 files are changed for the meat of the system, along with 2 scripts for a keybinding and callback access.
FILE: Which file to edit
[i]Which function to work in[/i] surrounding original code [b]New code[/b] surrounding original code
FILE: Player.cc
[i]PlayerData::ActionAnimationDef PlayerData::ActionAnimationList[NumTableActionAnims] =
{
[/i]
{ "back", { 0,-1,0 } }, // BackBackwardAnim
{ "side", { -1,0,0 } }, // SideLeftAnim,
// These are set explicitly based on player actions
[b]
// 6 new animations for the extreme basics
{ "swimroot" },
{ "swim" },
{ "crouchroot" },
{ "crouchforward" },
{ "crawlroot" },
{ "crawlforward" },[/b]
{ "fall" }, // FallAnim
{ "jump" }, // JumpAnim
[i]PlayerData::PlayerData()
{
[/i]
minJumpSpeed = 500;
maxJumpSpeed = 2 * minJumpSpeed;
[b]
swimForce = 75;
swimEnergyDrain = 0;
minSwimEnergy = 0;
[/b]
horizMaxSpeed = 80;
horizResistSpeed = 38;
[i]
bool PlayerData::preload(bool server, char errorBuffer[256])
{[/i]
if (minJumpEnergy < jumpEnergyDrain)
minJumpEnergy = jumpEnergyDrain;
[b]
if (minSwimEnergy < swimEnergyDrain)
minSwimEnergy = swimEnergyDrain;
[/b]
// Validate some of the data
if (recoverDelay > (1 << RecoverDelayBits) - 1) {
[i]
void PlayerData::initPersistFields()
{
[/i]
addField("jumpSurfaceAngle", TypeF32, Offset(jumpSurfaceAngle, PlayerData));
addField("jumpDelay", TypeS32, Offset(jumpDelay, PlayerData));
[b]
addField("swimForce", TypeF32, Offset(swimForce, PlayerData));
addField("swimEnergyDrain", TypeF32, Offset(swimEnergyDrain, PlayerData));
addField("minSwimEnergy", TypeF32, Offset(minSwimEnergy, PlayerData));
[/b]
addField("boundingBox", TypePoint3F, Offset(boxSize, PlayerData));
addField("boxHeadPercentage", TypeF32, Offset(boxHeadPercentage, PlayerData));
[i]
void PlayerData::packData(BitStream* stream)
{[/i]
stream->write(jumpSurfaceAngle);
stream->writeInt(jumpDelay,JumpDelayBits);
[b]
stream->write(swimForce);
stream->write(swimEnergyDrain);
stream->write(minSwimEnergy);
[/b]
stream->write(horizMaxSpeed);
stream->write(horizResistSpeed);
[i]
void PlayerData::unpackData(BitStream* stream)
{[/i]
stream->read(&jumpSurfaceAngle);
jumpDelay = stream->readInt(JumpDelayBits);
[b]
stream->read(&swimForce);
stream->read(&swimEnergyDrain);
stream->read(&minSwimEnergy);
[/b]
stream->read(&horizMaxSpeed);
stream->read(&horizResistSpeed);
[i]
Player::Player()
{[/i]
mFalling = false;
[b] mSwimming = false;
mPlayerPosition = 1; // 1=stand, 2=crouch, 3=prone
[/b] mContactTimer = 0;
[i]
bool Player::onAdd()
{[/i]
mState = NullState;
setState(state);
[b]
setPlayerPosition(1); // 1=stand, 2=crouch, 3=prone
[/b]
if (serverAnim.action != PlayerData::NullAnimation) {
[i]
void Player::updateMove(const Move* move)
{[/i]
}
else
mJumpSurfaceLastContact++;
[b]
// Acceleration from Swimming
// I don't understand physics, nor 3d math. Forgive me if this
// looks horrid. It seems to work fairly well though, so I'll
// be using it for now.
if (!isMounted() && canSwim())
{
mSwimming = true; // Not actually used, but perhaps good to have
VectorF pv;
mEnergy -= mDataBlock->swimEnergyDrain;
Point3F headRotation = getHeadRotation();
pv = moveVec;
pv *= moveSpeed;
F32 impulse = (5000 / mMass) * TickSec; // times player.strength as well ?
VectorF horiz = moveVec;
horiz.normalize();
horiz *= 0.9;
acc += horiz * impulse;
VectorF swimAcc = pv - (mVelocity + acc);
F32 swimSpeed = swimAcc.len();
F32 maxSwimAcc = (mDataBlock->swimForce / mMass) * TickSec;
if (swimSpeed > maxSwimAcc) {
swimAcc *= maxSwimAcc / swimSpeed;
// Con::errorf("Swimming too fast!");
}
if (swimSpeed > moveSpeed)
swimSpeed = moveSpeed;
acc += swimAcc;
acc.z = (-headRotation.x * (mDataBlock->swimForce / mMass) * 0.25f);
} else {
mSwimming = false;
}
[/b]
// Add in force from physical zones...
acc += (mAppliedForce / mMass) * TickSec;
[i] Further down[/i]
if (!isGhost()) {
// Vehicle Dismount
if(move->trigger[2] && isMounted())
Con::executef(mDataBlock,2,"doDismount",scriptThis());
if(!inLiquid && mWaterCoverage != 0.0f) {
Con::executef(mDataBlock,4,"onEnterLiquid",scriptThis(), Con::getFloatArg(mWaterCoverage), Con::getIntArg(mLiquidType));
[b] setPlayerPosition(3);[/b]
inLiquid = true;
}
else if(inLiquid && mWaterCoverage <= 0.0f) {
Con::executef(mDataBlock,3,"onLeaveLiquid",scriptThis(), Con::getIntArg(mLiquidType));
[b] setPlayerPosition(1);[/b]
inLiquid = false;
}
} else {
if(!inLiquid && mWaterCoverage >= 1.0f) {
[b] setPlayerPosition(3);[/b]
inLiquid = true;
}
else if(inLiquid && mWaterCoverage < 0.5f) {
if(getVelocity().len() >= mDataBlock->exitSplashSoundVel && !isMounted())
alxPlay(mDataBlock->sound[PlayerData::ExitWater], &getTransform());
[b] setPlayerPosition(1);[/b]
inLiquid = false;
}
}
[i]
bool Player::canJump()
{[/i]
// Added position check, you cant jump while crouch, crawl
return mState == MoveState && mDamageState == Enabled && !isMounted() && !mJumpDelay && mEnergy >= mDataBlock->minJumpEnergy && mJumpSurfaceLastContact < JumpSkipContactsMax[b] && getPlayerPosition() == 1[/b];
}
[b]
bool Player::canSwim()
{
return mState == MoveState && mDamageState == Enabled && !isMounted() && mEnergy >= mDataBlock->minSwimEnergy && mWaterCoverage >= 0.8f;
}
[/b]
[b]
void Player::setPlayerPosition(S32 position)
{
F32 len_x, len_y, len_z;
if (position != mPlayerPosition) {
if (isProperlyAdded()) {
// Special case, this one bumps us up to the next position
if (position == 0) {
position = mPlayerPosition + 1;
if (position > 3)
position = 1;
}
switch (position) {
case 1: // Stand, walk
{
len_x = 1;
len_y = 1;
len_z = 2.3;
break;
}
case 2: // Crouch, sit
{
len_x = 1;
len_y = 1;
len_z = 1.1;
break;
}
case 3: // Crawl, prone
{
len_x = 1;
len_y = 2.3;
len_z = 0.7;
break;
}
}
mObjBox.max.x = len_x * 0.5;
mObjBox.max.y = len_y * 0.5;
mObjBox.max.z = len_z;
mObjBox.min.x = -mObjBox.max.x;
mObjBox.min.y = -mObjBox.max.y;
mObjBox.min.z = 0;
}
mPlayerPosition = position;
}
if (isServerObject())
setMaskBits(MoveMask);
}
[/b]
// This function needs a lot more work. It works well enough for
// basic testing/playing around purposes. Also, without quite a
// few more animations available, a more robust job here would be
// for naught.
[i]
void Player::pickActionAnimation()
{[/i]
bool forward = true;
U32 action = PlayerData::RootAnim;
[b]
if (getPlayerPosition() == 1) {
////////// enclosing original code in my own stuff
///////// End of original code
}
// Swim code, Position code
else if (getPlayerPosition() == 2) {
action = (mVelocity.len() < 0.5) ? PlayerData::CrouchRootAnim : PlayerData::CrouchForwardAnim;
}
else if (getPlayerPosition() == 3) {
action = (mVelocity.len() < 0.5) ? PlayerData::CrawlRootAnim : PlayerData::CrawlForwardAnim;
if (mWaterCoverage != 0.0f) {
action = (mVelocity.len() < 0.5) ? PlayerData::SwimRootAnim : PlayerData::SwimAnim;
}
}
[/b]
setActionThread(action,forward,false,false);
}
[i]
bool Player::updatePos(const F32 travelTime)
{[/i]
setPosition(start,mRot);
setMaskBits(MoveMask);
updateContainer();
[b]
// this prevents the rocket launch effect when a player in water surfaces
if (mBuoyancy != 0 && mWaterCoverage <= 0.5f)
{
mVelocity.z += mBuoyancy * mGravity * mGravityMod * TickSec;
}
[/b]
if (!isGhost()) {
[i]
U32 Player::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
{[/i]
[b]
if (stream->writeFlag(mask & MoveMask))
stream->writeInt(getPlayerPosition(),5);
[/b]
if (stream->writeFlag((mask & ImpactMask) && !(mask & InitialUpdateMask)))
stream->writeInt(mImpactSound, PlayerData::ImpactBits);
[i] Further down......[/i]
stream->writeFlag(mFalling);
[b]
stream->writeFlag(mSwimming);
[/b]
stream->writeInt(mState,NumStateBits);
[i]
void Player::unpackUpdate(NetConnection *con, BitStream *stream)
{[/i]
[b]
if (stream->readFlag()) {
S32 pos = stream->readInt(5);
setPlayerPosition(pos);
}[/b]
if (stream->readFlag())
mImpactSound = stream->readInt(PlayerData::ImpactBits);
[i] Further down......[/i]
mFalling = stream->readFlag();
[b]
mSwimming = stream->readFlag(); // fafhrd, swim code
[/b]
ActionState actionState = (ActionState)stream->readInt(NumStateBits);
[b][i]NEW: Updated 2/1/04 to coincide with HEAD's change to consolemethod style commands.
Change the below code (or ignore if this is your first time installing this) to what follows [/b][/i]
[i]
static S32 cgetPlayerPosition(SimObject *ptr, S32, const char **)
{
Player* obj = static_cast<Player*>(ptr);
return obj->getPlayerPosition();
}
static void csetPlayerPosition(SimObject *ptr, S32, const char **argv)
{
Player* obj = static_cast<Player*>(ptr);
obj->setPlayerPosition(dAtof(argv[2]));
}
[/i]
void Player::consoleInit()
Con::addCommand("Player", "getPlayerPosition", cgetPlayerPosition, "obj.getPlayerPosition()", 2, 2);
Con::addCommand("Player", "setPlayerPosition", csetPlayerPosition, "obj.setPlayerPosition(pos)", 3, 3);
[b][i] CHANGE to this code[/b][/i]
[b]
ConsoleMethod( Player, getPlayerPosition, S32, 2, 2, "(1=stand, 2=crouch, 3=crawl)")
{
return object->getPlayerPosition();
}
ConsoleMethod( Player, setPlayerPosition, bool, 3, 3, "(1=stand, 2=crouch, 3=crawl)")
{
object->setPlayerPosition(dAtof(argv[2]));
}
[/b]FILE: player.h
[i]
struct PlayerData: public ShapeBaseData {
[/i]
F32 jumpSurfaceAngle; // Angle from vertical in degrees
S32 jumpDelay; // Delay time in ticks
[b]
F32 swimForce;
F32 swimEnergyDrain;
F32 minSwimEnergy;
[/b]
F32 boxHeadPercentage;
F32 boxTorsoPercentage;
[i] Further down......[/i]
BackBackwardAnim,
SideLeftAnim,
// These are set explicitly based on player actions
[b] SwimRootAnim,
SwimAnim,
CrouchRootAnim,
CrouchForwardAnim,
CrawlRootAnim,
CrawlForwardAnim,[/b]
FallAnim,
JumpAnim,
[i]
class Player: public ShapeBase
{[/i]
ActionState mState;
bool mFalling; // Falling in mid-air
[b] bool mSwimming;
S32 mPlayerPosition; // 1=stand, 2=crouch, 3=prone [/b]
S32 mJumpDelay; // Delay till next jump
[i] Further down......[/i]
F32 getJumpForceModifier() { return mJumpForceModifier; }
[b]
void setPlayerPosition(S32 pos);
S32 getPlayerPosition() { return mPlayerPosition; }
[/b]
protected:
void setState(ActionState state, U32 ticks=0);
void updateState();
[i] Further down......[/i]
bool canJump();
[b] bool canSwim();[/b]
bool haveContact() {return !mContactTimer;}FILE: default.bind.cs
[b]
moveMap.bind( keyboard, c, changePlayerPosition );
function changePlayerPosition(%val)
{
if (%val)
// calling it with a 0 will bump us up to the next position
CommandtoServer('SetPlayerPos', 0);
}
[/b]FILE: commands.cs
[b]
function serverCmdSetPlayerPos(%client,%pos)
{
if (isObject(%client.player))
%client.player.setPlayerPosition(%pos);
}
function serverCmdGetPlayerPos(%client)
{
if (isObject(%client.player))
return %client.player.getPosition();
else
return 0;
}
[/b]About the author
Recent Blogs
• Dynamic GUI• Mob Look
• Faster Polysoup
• DreamGames and Titas
• Working with pickActionAnimation()
#62
or is THIS code correct:
thanks!
01/23/2006 (4:01 pm)
so is this code correct:ConsoleMethod( Player, setPlayerPosition, void, 3, 3, "(1=stand, 2=crouch, 3=crawl)")
{
object->setPlayerPosition(dAtof(argv[2]));
}or is THIS code correct:
ConsoleMethod( Player, setPlayerPosition, bool, 3, 3, "(1=stand, 2=crouch, 3=crawl)")
{
return object->setPlayerPosition(dAtof(argv[2]));
}thanks!
#63
01/23/2006 (4:10 pm)
I used the one with the void return and everything worked fine for me!
#64
Can't find any TGE models/packs that include swim animations (or crouch/prone), anyone know of any ?
Willing to pay if theres one out there.
01/24/2006 (2:53 am)
Cracking resource ,thanks for sharing it with us. Using it with TGE 1.4.Can't find any TGE models/packs that include swim animations (or crouch/prone), anyone know of any ?
Willing to pay if theres one out there.
#65
I'd really like this little beauty in my game.
@Duncan, perhaps you can share the secret or any extra steps you did to get this into 1.4?
Thanks in advance chaps.
02/06/2006 (4:27 pm)
Anyone help me out here.. I did all of the above.. Compiled, worked fine! Got into the game, hit "c" and nothing. Ran into the water.. Nothing special either. What on earth did I do some where wrong to have no changes? I'm working on a nice clean install of 1.4.. Compiling with vsc++ 2005 express. Any clues anyone?I'd really like this little beauty in my game.
@Duncan, perhaps you can share the secret or any extra steps you did to get this into 1.4?
Thanks in advance chaps.
#66
02/06/2006 (7:42 pm)
First, are you sure your player's model comes with animations for crouch/prone/swim? Second, when you go INTO the water, you should float until you look down and then move down.. in normal behavior, you'd always be walking on the bottom.
#67
Once that worked, i changed the swim movement code to suit my needs.
02/07/2006 (1:34 am)
The only extra steps i took was to create some crouch / prone and swim animations .Once that worked, i changed the swim movement code to suit my needs.
#68
02/07/2006 (8:25 am)
Ah, animations. Anyone happen to have extra animations for the orc bone structure? Models really arn't my thing! Thanks guys.
#69
http://www.grafosyakuza.com/wc/temp/swimming%2Bcrouch.rar
http://www.grafosyakuza.com/wc/temp/prone.rar
02/14/2006 (6:10 pm)
Orc model anim...http://www.grafosyakuza.com/wc/temp/swimming%2Bcrouch.rar
http://www.grafosyakuza.com/wc/temp/prone.rar
#70
Slight correction on the first url:
http://jovechiere.no-ip.com/wc/temp/swimming%2Bcrouch.rar
02/15/2006 (12:44 am)
Many thanks for posting the orc anims.Slight correction on the first url:
http://jovechiere.no-ip.com/wc/temp/swimming%2Bcrouch.rar
#71
Thanks for saving me hours or more likely days of work.
Cheers again.
02/15/2006 (8:57 am)
Many thanks Jove! You'd not believe the hassle I have gone through to try and get these.. I even started trying to learn how to animate would you believe it!Thanks for saving me hours or more likely days of work.
Cheers again.
#72
I have added:
sequence30 = "./player_swimroot.dsq swimroot";
sequence31 = "./player_swimforward.dsq swim";
sequence32 = "./player_crouchroot.dsq crouchroot";
sequence33 = "./player_crouchforward.dsq crouchforward";
sequence34 = "./player_crouchroot.dsq crawlroot";
sequence35 = "./player_crouchforward.dsq crawlforward";
sequence36 = "./player_proneroot.dsq proneroot";
sequence37 = "./player_proneforward.dsq proneforward";
to the player.cs, I do have the animation files in the directory.
I am using TGE 1.4. Any suggestions?
02/17/2006 (2:17 pm)
I am trying to get this one to work. This didn't seem to support prone position, so I added in the enum for it. My orc is trying to change positions, his position number is changing in the engine, but the animations are not changing.I have added:
sequence30 = "./player_swimroot.dsq swimroot";
sequence31 = "./player_swimforward.dsq swim";
sequence32 = "./player_crouchroot.dsq crouchroot";
sequence33 = "./player_crouchforward.dsq crouchforward";
sequence34 = "./player_crouchroot.dsq crawlroot";
sequence35 = "./player_crouchforward.dsq crawlforward";
sequence36 = "./player_proneroot.dsq proneroot";
sequence37 = "./player_proneforward.dsq proneforward";
to the player.cs, I do have the animation files in the directory.
I am using TGE 1.4. Any suggestions?
#73
02/17/2006 (2:31 pm)
I'd have to look at home to be sure of what it was, but I know you don't call the prone animations by 'proneroot' and 'proneforward'. You have crouchroot and forward twice. Remove sequence 34 and 35 and make the prone ones be 'crawl' not 'prone'
#74
02/17/2006 (2:49 pm)
Did that, just realized fundamentally crawl and prone were the same thing. Technically it should still work ;) No effect though. Several mentions of this problem above, no mentions of what fixes it hehe. Anyone?
#75
from my player.cs file (change numbering, path to the animations and animation file name to suit whatever animations your using), the sequence name at the end of each line works with the above resource.
sequence32 = "./animations/player_crouchroot.dsq crouchroot";
sequence33 = "./animations/player_crouchforward.dsq crouchforward";
sequence34 = "./animations/player_swimroot.dsq swimroot";
sequence35 = "./animations/player_swim.dsq swim";
sequence36 = "./animations/player_proneroot.dsq crawlroot";
sequence37 = "./animations/player_proneforward.dsq crawlforward";
If you've got the animations and player.cs setup correctly and are still not getting any crouch/prone anims working then i'd suggest you check that you've bound the 'c' key correctly as explained above.
02/18/2006 (1:09 am)
I'm using TGE 1.4 with swim, crouch and prone animations.from my player.cs file (change numbering, path to the animations and animation file name to suit whatever animations your using), the sequence name at the end of each line works with the above resource.
sequence32 = "./animations/player_crouchroot.dsq crouchroot";
sequence33 = "./animations/player_crouchforward.dsq crouchforward";
sequence34 = "./animations/player_swimroot.dsq swimroot";
sequence35 = "./animations/player_swim.dsq swim";
sequence36 = "./animations/player_proneroot.dsq crawlroot";
sequence37 = "./animations/player_proneforward.dsq crawlforward";
If you've got the animations and player.cs setup correctly and are still not getting any crouch/prone anims working then i'd suggest you check that you've bound the 'c' key correctly as explained above.
#76
I embedded the SetActionAnimation command inside the //original code in pickActionAnimation. Doh!
In retrospect, I should have just tried again. Sorry to trouble you guys!
Also, I see reference to a common problem with the camera not moving with the position. And that it is a workaround to change the player's scale. Is this done in the datablock, or in code? Is there a better solution that's *not* a workaround?
02/18/2006 (6:33 am)
Thanks Duncan and dishmal, I tried a third time and it worked. Where I got caught:I embedded the SetActionAnimation command inside the //original code in pickActionAnimation. Doh!
In retrospect, I should have just tried again. Sorry to trouble you guys!
Also, I see reference to a common problem with the camera not moving with the position. And that it is a workaround to change the player's scale. Is this done in the datablock, or in code? Is there a better solution that's *not* a workaround?
#77
I try to jump, and I get nothing, however everything else seems to work just fine.
02/28/2006 (5:42 pm)
Hey am I the only one having issues with Jumping after implementing this?I try to jump, and I get nothing, however everything else seems to work just fine.
#78
02/28/2006 (6:36 pm)
Dreamer, jump worked fine for me, I would check the pickActionAnimation changes and see if something minor was missed. There is a bolded change at the end of one line that I didn't see in another area, one of the && flags...
#79
I just have to make sure I'm underwater when I try to jump.
I launch up and out of the water like a rocket!
Ok so this tells me somewhere something that's supposed to be checking for canJump must actually be looking if !canJump or something along those lines.
Oh well Im just glad jumping isn't completely borked.
02/28/2006 (7:49 pm)
Hmmm Ok Jump is NOT completely broken... It does in fact work quite wellI just have to make sure I'm underwater when I try to jump.
I launch up and out of the water like a rocket!
Ok so this tells me somewhere something that's supposed to be checking for canJump must actually be looking if !canJump or something along those lines.
Oh well Im just glad jumping isn't completely borked.
#80
Ok I'm an idiot I had the canJump and canSwim functions duplicated... The code for canSwim was in the canJump function...
Doh!
03/01/2006 (12:36 am)
*edit*Ok I'm an idiot I had the canJump and canSwim functions duplicated... The code for canSwim was in the canJump function...
Doh!

Torque Owner Vishal Lamba
but if i try to crouch they start shooting at me.