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()
#22
With some wrenching I got this in!
Works wonderful!
Needed some animation help, but this resource saved me alot of time.
Works on the latest HEAD.
This resource has been assimilated!
Ari
07/14/2004 (8:52 pm)
Thanks Erik,With some wrenching I got this in!
Works wonderful!
Needed some animation help, but this resource saved me alot of time.
Works on the latest HEAD.
This resource has been assimilated!
Ari
#23
c:\torque edit\engine\game\player.cc(3841) : error C4716: 'cPlayersetPlayerPosition' : must return a value
My attempts to fix it have failed. Any help would be appreciated.
Thanks,
-Warspawn
08/14/2004 (7:59 pm)
Well, im a noob. Trying to get this one in, everything went smooth until that last part about the Feb update. I wasn't quite clear on what I was supposed to ignore if it was the first install. Anyway, I put it all in, and I'm getting this error:c:\torque edit\engine\game\player.cc(3841) : error C4716: 'cPlayersetPlayerPosition' : must return a value
My attempts to fix it have failed. Any help would be appreciated.
Thanks,
-Warspawn
#24
I have the same error...
f:\torque\engine\game\player.cc(3872): error C4716: 'cPlayersetPlayerPosition' : must return a value
Did you already solve this situation?
Thanks,
Patrick
09/03/2004 (9:25 am)
Hi Warspawn...I have the same error...
f:\torque\engine\game\player.cc(3872): error C4716: 'cPlayersetPlayerPosition' : must return a value
Did you already solve this situation?
Thanks,
Patrick
#25
i dont get any specifics on the error, it just has a fatal error and closes
09/13/2004 (1:13 pm)
my game crashes right after i load the map after adding thisi dont get any specifics on the error, it just has a fatal error and closes
#26
I believe the correct sintax for the console method setPlayerPosition is:
ConsoleMethod( Player, setPlayerPosition, void, 3, 3, "(1=stand, 2=crouch, 3=crawl)") {
object->setPlayerPosition(dAtof(argv[2]));
}
... since Player::setPlayerPosition is void.
10/22/2004 (7:40 pm)
Ben & Patrick,I believe the correct sintax for the console method setPlayerPosition is:
ConsoleMethod( Player, setPlayerPosition, void, 3, 3, "(1=stand, 2=crouch, 3=crawl)") {
object->setPlayerPosition(dAtof(argv[2]));
}
... since Player::setPlayerPosition is void.
#27
Window title:
"Fatal: (d:\torque\engine\math\mplane.h @ 233)"
Text in Error window:
"Error, no plane possible!"
P. S.
i
10/24/2004 (3:23 pm)
My game (fresh loaded demo "starter.fps") crashes also after adding the above code in the source code.Window title:
"Fatal: (d:\torque\engine\math\mplane.h @ 233)"
Text in Error window:
"Error, no plane possible!"
P. S.
i
#28
"mw/server/scripts/commands.cs (272): Unknown command setPlayerPosition.
Object (1924) Player -> ShapeBase -> GameBase -> SceneObject -> NetObject -> SimObject"
I made sure in default.bind.cs that the ´ to '.
Whats wrong?
Thanks,
Stephen
12/04/2004 (2:43 pm)
I have added the code in torque and it compiled fine but I'm getting this error"mw/server/scripts/commands.cs (272): Unknown command setPlayerPosition.
Object (1924) Player -> ShapeBase -> GameBase -> SceneObject -> NetObject -> SimObject"
I made sure in default.bind.cs that the ´ to '.
Whats wrong?
Thanks,
Stephen
#29
But now im getting this error when compiling,
"c:\torque\engine\game\player.cc(3904) : error C4716: 'cPlayersetPlayerPosition' : must return a value"
Thanks,
Stephen
12/04/2004 (4:03 pm)
I wonder out that I forgot something, it was the last part of player.cc in the resource.But now im getting this error when compiling,
"c:\torque\engine\game\player.cc(3904) : error C4716: 'cPlayersetPlayerPosition' : must return a value"
Thanks,
Stephen
#31
I checked the models bounds in the world inspector, (mouse over to show its box) and there is no reason why it will not fit through.
Has anyone had similer problems?
or an idea on what may be causing it?
Thanks
01/05/2005 (12:00 am)
I got this to work in 1.3, but for some reason when my model is in crouch or crawl mode it will not fit under a tight spot, eg a low ceiling, the only time the model will pass though in those positions is when the model can do it in stand/run mode.I checked the models bounds in the world inspector, (mouse over to show its box) and there is no reason why it will not fit through.
Has anyone had similer problems?
or an idea on what may be causing it?
Thanks
#32
This is a great resource.
Thank you.
However, the swim animation would not start if I jumped into the water until after I hit the ground. I fixed this with the following code change in player.cc.
01/07/2005 (6:26 am)
Erik,This is a great resource.
Thank you.
However, the swim animation would not start if I jumped into the water until after I hit the ground. I fixed this with the following code change in player.cc.
[i]In Player::updateMove ...[/i]
// If we are running on the ground, then we're not jumping
if (mDataBlock->isJumpAction(mActionAnimation.action))
mActionAnimation.action = PlayerData::NullAnimation;
}
else
mContactTimer++;
[b]
if (mDataBlock->isJumpAction(mActionAnimation.action) && mWaterCoverage > 0.5f)
mActionAnimation.action = PlayerData::NullAnimation;
[/b]
// Acceleration from Jumping
if (move->trigger[2] && !isMounted() && canJump())
#33
01/19/2005 (2:38 am)
Fantastic resource Erik. Have you had chance to develop the code further? I know i'm being cheeky but my modelling and animation are not good, any chance you or someone else could supply a character with these animations? Just a stick figuire would be fine. Grovel, grovel
#34
Next free moment of boredom I get, I'll package up some of those basic animations for you.
02/18/2005 (9:15 am)
For those still attempting to get this working, I finally had a moment to look back over it. At the end of setPlayerPosition(), you need to call OnScaleChanged(), which lets the collision code know that we've changed the players bounding box and a new list needs to be generated. Otherwise, everything is still valid as of this posting. I added it to my copy of TSE for testing, and it works very well. (Can't test swim without water though :))Next free moment of boredom I get, I'll package up some of those basic animations for you.
#35
I implemented your code fix and now my little Orc stops his animation midway "frozen". I am talking about walking/running.
If you move he somewhat to the left, he will not finish the left animation but instead stay at the current point in his current animation.
02/23/2005 (10:14 pm)
@Joseph:I implemented your code fix and now my little Orc stops his animation midway "frozen". I am talking about walking/running.
If you move he somewhat to the left, he will not finish the left animation but instead stay at the current point in his current animation.
#36
Now, I am a newb at this, so I'm going to ask a newbish question:
> Do I need these animations on my computer first?!
because it doesnt say so
im assuming theres an "animation" file i dont have.
and i cant make my own, im barely getting the hang of 3d modelling.
03/01/2005 (10:19 pm)
I tried this and my game (starter.fps) freezes when it says "Loading datablocks"Now, I am a newb at this, so I'm going to ask a newbish question:
> Do I need these animations on my computer first?!
because it doesnt say so
im assuming theres an "animation" file i dont have.
and i cant make my own, im barely getting the hang of 3d modelling.
#37
03/03/2005 (11:52 pm)
You shouldn't need the animations. It is most likely a script file error (but I am too tired to think now).
#38
Maybe it isn't your code that caused my problem, hehe.
I removed it and I still had the problem, sorry for the false accusation.
03/07/2005 (8:13 pm)
@Joseph:Maybe it isn't your code that caused my problem, hehe.
I removed it and I still had the problem, sorry for the false accusation.
#39
like before. Now, if I press the first time the 'C' key our player is moving without any animations, also
when I press the second time the 'C' key. The third time, all is like before, and the player animations
are there. So I think I have the three states of crouching. But now, how do I have to name the
extra animations and where I have to put the sequences in the player.cs file ???
Also I think there is a problem when the player enters a liquid area. I have some fog over our water
and when the player enters the fog area without reaching the water, he loses his gravity.
Any help will be great .....
03/13/2005 (10:57 am)
I don't know exactly if I got it correct in. I do not get any compiler error and our game is runninglike before. Now, if I press the first time the 'C' key our player is moving without any animations, also
when I press the second time the 'C' key. The third time, all is like before, and the player animations
are there. So I think I have the three states of crouching. But now, how do I have to name the
extra animations and where I have to put the sequences in the player.cs file ???
Also I think there is a problem when the player enters a liquid area. I have some fog over our water
and when the player enters the fog area without reaching the water, he loses his gravity.
Any help will be great .....
#40
03/25/2005 (2:40 pm)
I was trying to implement this resource and that last comment about OnScaleChanged() is giving me some trouble, where is that method defined? 
Grant Lucas