Game Development Community

dev|Pro Game Development Curriculum

Do the Twist

by Derk Adams · 11/15/2004 (2:35 pm) · 23 comments

Download Code File

Discussion:
To enable torso twisting, I have extended the controls of the first-person view to include the capabilities of the third-person view. This is a combination process and it is absolute, i.e. both the first-person view and the third-person view function identically and all characters have this ability.

The amount of twisting is determined by the look angle player datablock values. These values are in radians, so 180 degrees equals 3.1415. MinLookAngle and MaxLookAngle control the amount of vertical bending and maxFreelookAngle controls the amount of horizontal twisting.

Comments:
If you want a character to not have torso twist, simply set their maxFreelookAngle to 0.
For animating your character, have the "headside" animation be the torso twist extremes, and the "look" animation be the up-down extremes. Note: the "look" animation expects an 80 degree angle up and an 80 degree angle down for the extremes, even if you don't use the full range.

Key:
I use the same indication of modifications as a patch file. A line beginning with a "-" is to be removed and a line beginning with a "+" is to be added. Be sure to remove the "+" when you actually put the line into your code. A few lines around the changes are shown to give context to the changes. The triple dot "..." is showing that information has been removed for brevity purposes.

Development Environment:
November 2004
Head 1.2.2
Win32
Single Player

Implementation:
EngineFile: moveManager.h
struct Move
{
...
   S32 px, py, pz;
-   U32 pyaw, ppitch, proll;
+   U32 pyaw, ppitch, proll, ptwist;
   F32 x, y, z;          // float -1 to 1
-   F32 yaw, pitch, roll; // 0-2PI
+   F32 yaw, pitch, roll, twist; // 0-2PI
...
}

class MoveManager
{
...
   static F32 mRoll;
+   static F32 mTwist;
...
   static F32 mRollRightSpeed;
+   static F32 mTwistLeftSpeed;
+   static F32 mTwistRightSpeed;
...
};
EngineFile:gameConnectionMoves.cc
...
F32 MoveManager::mRoll = 0;
+F32 MoveManager::mTwist = 0;
...
F32 MoveManager::mRollRightSpeed = 0;
+F32 MoveManager::mTwistLeftSpeed = 0;
+F32 MoveManager::mTwistRightSpeed = 0;
...
const Move NullMove =
{
   16,16,16,
-   0,0,0,
+   0,0,0,0,
   0,0,0,   // x,y,z
-   0,0,0,   // Yaw, pitch, roll
+   0,0,0,0,   // Yaw, pitch, roll, twist
   0,0,

   false,
   false,false,false,false,false,false
};
...
void MoveManager::init()
{
...
   Con::addVariable("mvRoll", TypeF32, &mRoll);
+   Con::addVariable("mvTwist", TypeF32, &mTwist);
   Con::addVariable("mvPitchUpSpeed", TypeF32, &mPitchUpSpeed);
...
   Con::addVariable("mvRollRightSpeed", TypeF32, &mRollRightSpeed);
+   Con::addVariable("mvTwistLeftSpeed", TypeF32, &mTwistLeftSpeed);
+   Con::addVariable("mvTwistRightSpeed", TypeF32, &mTwistRightSpeed);
...
}

void Move::unclamp()
{
   yaw = IANG2FANG(pyaw);
   pitch = IANG2FANG(ppitch);
   roll = IANG2FANG(proll);
+   twist = IANG2FANG(ptwist);

   x = (px - 16) / F32(16);
   y = (py - 16) / F32(16);
   z = (pz - 16) / F32(16);
}

void Move::clamp()
{
   // angles are all 16 bit.
   pyaw = FANG2IANG(yaw);
   ppitch = FANG2IANG(pitch);
   proll = FANG2IANG(roll);
+   ptwist = FANG2IANG(twist);

   px = clampRangeClamp(x);
   py = clampRangeClamp(y);
   pz = clampRangeClamp(z);
   unclamp();
}

void Move::pack(BitStream *stream)
{
   if(stream->writeFlag(pyaw != 0))
      stream->writeInt(pyaw, 16);
   if(stream->writeFlag(ppitch != 0))
      stream->writeInt(ppitch, 16);
   if(stream->writeFlag(proll != 0))
      stream->writeInt(proll, 16);
+   if(stream->writeFlag(ptwist != 0))
+      stream->writeInt(ptwist, 16);

   stream->writeInt(px, 6);
   stream->writeInt(py, 6);
   stream->writeInt(pz, 6);
   stream->writeFlag(freeLook);

   for(U32 i = 0; i < MaxTriggerKeys; i++)
      stream->writeFlag(trigger[i]);
}

void Move::unpack(BitStream *stream)
{
   if(stream->readFlag())
      pyaw = stream->readInt(16);
   else
      pyaw = 0;
   if(stream->readFlag())
      ppitch = stream->readInt(16);
   else
      ppitch = 0;
   if(stream->readFlag())
      proll = stream->readInt(16);
   else
      proll = 0;
+   if(stream->readFlag())
+      ptwist = stream->readInt(16);
+   else
+      ptwist = 0;
   px = stream->readInt(6);
   py = stream->readInt(6);
   pz = stream->readInt(6);

   unclamp();
   freeLook = stream->readFlag();

   for(U32 i = 0; i < MaxTriggerKeys; i++)
      trigger[i] = stream->readFlag();
}

bool GameConnection::getNextMove(Move &curMove)
{
...
   F32 pitchAdd = MoveManager::mPitchUpSpeed - MoveManager::mPitchDownSpeed;
   F32 yawAdd = MoveManager::mYawLeftSpeed - MoveManager::mYawRightSpeed;
   F32 rollAdd = MoveManager::mRollRightSpeed - MoveManager::mRollLeftSpeed;
+   F32 twistAdd = MoveManager::mTwistLeftSpeed - MoveManager::mTwistRightSpeed;

   curMove.pitch = MoveManager::mPitch + pitchAdd;
   curMove.yaw = MoveManager::mYaw + yawAdd;
   curMove.roll = MoveManager::mRoll + rollAdd;
+   curMove.twist = MoveManager::mTwist + twistAdd;
   
   MoveManager::mPitch = 0;
   MoveManager::mYaw = 0;
   MoveManager::mRoll = 0;
+   MoveManager::mTwist = 0;
...
}
EngineFile: player.cc
void Player::updateMove(const Move* move)
{
...
      F32 p = move->pitch;
      if (p > M_PI) p -= M_2PI;
      mHead.x = mClampF(mHead.x + p,mDataBlock->minLookAngle,
                        mDataBlock->maxLookAngle);

+  F32 t = move->twist;
+  if (t > M_PI) t -= M_2PI;
+  mHead.z = mClampF(mHead.z + t,-mDataBlock->maxFreelookAngle,mDataBlock->maxFreelookAngle);
...
}

void Player::getMuzzleTransform(U32 imageSlot,MatrixF* mat)
{
...
   if (mActionAnimation.action < PlayerData::NumTableActionAnims) {
-      MatrixF xmat;
+      MatrixF pmat,xmat,zmat;
      xmat.set(EulerF(mHead.x, 0, 0));
+      zmat.set(EulerF(0, 0, mHead.z));
+      pmat.mul(zmat,xmat);
+      mat->mul(getTransform(), pmat);
-      mat->mul(getTransform(), xmat);
      F32 *sp = nmat,*dp = *mat;
      dp[3] = sp[3]; dp[7] = sp[7]; dp[11] = sp[11];
   }
...
}
ScriptFile: /client/scripts/default.bind.cs
function twist(%val)
{
   $mvTwist += getMouseAdjustAmount(%val);
}
moveMap.bind( mouse, xaxis, twist );

function twistLeft( %val )
{
   $mvTwistRightSpeed = %val ? $Pref::Input::KeyboardTurnSpeed : 0;
}
moveMap.bind(keyboard, "comma", twistLeft);

function twistRight( %val )
{
   $mvTwistLeftSpeed = %val ? $Pref::Input::KeyboardTurnSpeed : 0;
}
moveMap.bind(keyboard, "period", twistRight);
ScriptFile: /client/config.cs
moveMap.bind(mouse0, "xaxis", twist);
moveMap.bind(keyboard, "comma", twistLeft);
moveMap.bind(keyboard, "period", twistRight);
Page«First 1 2 Next»
#21
08/05/2008 (11:23 am)
has anyone tried this with tgea 1.7.1?
#22
01/30/2009 (8:17 pm)
Problem with tge 1.4...I can twist a few degrees to the left or right, but my view immediately bounces back to center, so I can't really look around. I can twist as I should in the free look mode in 3rd person, but it won't twist right if I'm not holding z...Any ideas?
#23
02/11/2011 (3:23 pm)
Trying to use this resource with T3D (1.1 beta 3), but running into the same problems Bryce described above. I really can't figure out what's causing it, though I'll keep looking. I'll be working at it until I (hopefully) figure it out, and if/when I do, I shall post the solution - but any advice/pointers would be appreciated!

cheers

edit: Getting somewhere! The problem lies in the section under
GameConnection* con = getControllingClient();
..in Player.cpp (around line 2019).
Will try to refine it if possible/necessary.
Page«First 1 2 Next»