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()
#162
04/08/2009 (11:45 am)
It looks like this resource was made for TGEA... do you have a vanilla copy of player.cpp and player.h with JUST this resource added to it? Zip that up and add it to the very first post...
#163
04/08/2009 (1:00 pm)
Nicolai I can already tell from your error list what is wrong. In TGEA 1.8.1 all references to min and max were changed to minExtents and maxExtents. As for the float to double conversion issue wherever there is a number in those areas like 1.0 change it to indicate it is a float as in 1.0f.
#164
07/27/2009 (2:56 pm)
Would someone be so kind as to upload the crouch and swim animations that are compatible with the stock Torque bones? All of the links on this resource appear to be outdated. Thanks! 
Torque 3D Owner Nicolai Dutka
player.cpp ..\..\..\..\..\engine\source\T3D\player.cpp(1806) : warning C4305: 'argument' : truncation from 'double' to 'F32' ..\..\..\..\..\engine\source\T3D\player.cpp(2039) : warning C4305: '=' : truncation from 'double' to 'F32' ..\..\..\..\..\engine\source\T3D\player.cpp(2046) : warning C4305: '=' : truncation from 'double' to 'F32' ..\..\..\..\..\engine\source\T3D\player.cpp(2052) : warning C4305: '=' : truncation from 'double' to 'F32' ..\..\..\..\..\engine\source\T3D\player.cpp(2053) : warning C4305: '=' : truncation from 'double' to 'F32' ..\..\..\..\..\engine\source\T3D\player.cpp(2057) : error C2039: 'max' : is not a member of 'Box3F' c:\Torque\TGEA_1_8_1\engine\source\math/mBox.h(22) : see declaration of 'Box3F' ..\..\..\..\..\engine\source\T3D\player.cpp(2057) : error C2228: left of '.x' must have class/struct/union ..\..\..\..\..\engine\source\T3D\player.cpp(2058) : error C2039: 'max' : is not a member of 'Box3F' c:\Torque\TGEA_1_8_1\engine\source\math/mBox.h(22) : see declaration of 'Box3F' ..\..\..\..\..\engine\source\T3D\player.cpp(2058) : error C2228: left of '.y' must have class/struct/union ..\..\..\..\..\engine\source\T3D\player.cpp(2059) : error C2039: 'max' : is not a member of 'Box3F' c:\Torque\TGEA_1_8_1\engine\source\math/mBox.h(22) : see declaration of 'Box3F' ..\..\..\..\..\engine\source\T3D\player.cpp(2059) : error C2228: left of '.z' must have class/struct/union ..\..\..\..\..\engine\source\T3D\player.cpp(2060) : error C2039: 'min' : is not a member of 'Box3F' c:\Torque\TGEA_1_8_1\engine\source\math/mBox.h(22) : see declaration of 'Box3F' ..\..\..\..\..\engine\source\T3D\player.cpp(2060) : error C2228: left of '.x' must have class/struct/union ..\..\..\..\..\engine\source\T3D\player.cpp(2060) : error C2039: 'max' : is not a member of 'Box3F' c:\Torque\TGEA_1_8_1\engine\source\math/mBox.h(22) : see declaration of 'Box3F' ..\..\..\..\..\engine\source\T3D\player.cpp(2060) : error C2228: left of '.x' must have class/struct/union ..\..\..\..\..\engine\source\T3D\player.cpp(2061) : error C2039: 'min' : is not a member of 'Box3F' c:\Torque\TGEA_1_8_1\engine\source\math/mBox.h(22) : see declaration of 'Box3F' ..\..\..\..\..\engine\source\T3D\player.cpp(2061) : error C2228: left of '.y' must have class/struct/union ..\..\..\..\..\engine\source\T3D\player.cpp(2061) : error C2039: 'max' : is not a member of 'Box3F' c:\Torque\TGEA_1_8_1\engine\source\math/mBox.h(22) : see declaration of 'Box3F' ..\..\..\..\..\engine\source\T3D\player.cpp(2061) : error C2228: left of '.y' must have class/struct/union ..\..\..\..\..\engine\source\T3D\player.cpp(2062) : error C2039: 'min' : is not a member of 'Box3F' c:\Torque\TGEA_1_8_1\engine\source\math/mBox.h(22) : see declaration of 'Box3F' ..\..\..\..\..\engine\source\T3D\player.cpp(2062) : error C2228: left of '.z' must have class/struct/unionAny help would be much appreciated. ;)