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
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.ccvoid 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.csfunction 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.csmoveMap.bind(mouse0, "xaxis", twist); moveMap.bind(keyboard, "comma", twistLeft); moveMap.bind(keyboard, "period", twistRight);
About the author
Recent Blogs
• Damage Texture with Transparency• Projectile Spread
• Plan for Derk Adams
• Continuous Laser
• Plan for Derk Adams
#2
11/17/2004 (3:29 am)
Very useful! I'll look into this tonight, as well.
#4
12/03/2004 (7:11 am)
It is very useful!thanks.
#5
any other mac folks try that?
12/03/2004 (8:16 am)
When I implement this on the mac, my player jumps constantly and fires all his weapons.any other mac folks try that?
#6
Since these code changes have nothing to do with the jump or firing code, my guess is that it is a bug that came from before implementing this. Try removing the twist code and see if it still happens. Of course, it could always be a missing semicolon :)
Thanks.
12/03/2004 (10:09 am)
Edward,Since these code changes have nothing to do with the jump or firing code, my guess is that it is a bug that came from before implementing this. Try removing the twist code and see if it still happens. Of course, it could always be a missing semicolon :)
Thanks.
#7
"mhead.z = mClampF(mHead.z + t,-mDataBlock->maxFreelookAngle,mDataBlock->maxFreelookAngle);" should be
"mHead.z = mClampF(mHead.z + t,-mDataBlock->maxFreelookAngle,mDataBlock->maxFreelookAngle);"
Typo change the h to H
-Stephen
03/17/2005 (6:22 pm)
I'm having a bit of troblem getting this to work in 1.3. One thing is that this "mhead.z = mClampF(mHead.z + t,-mDataBlock->maxFreelookAngle,mDataBlock->maxFreelookAngle);" should be
"mHead.z = mClampF(mHead.z + t,-mDataBlock->maxFreelookAngle,mDataBlock->maxFreelookAngle);"
Typo change the h to H
-Stephen
#8
03/23/2005 (10:05 am)
Great resource.
#9
ie: i want my character to be standing still and while in 3rd view, have him look left/right and NOT move his lower half.
im not planning on using this for a mecha but for bipeds.
I'd like the ability for my character to look left and right without fully rotating the charcter.
Thanks again!
anyone know where i can find more about creating custom animations? links?
I want to integrate this with crouching, walking, running, and jumping/jetting.
07/09/2005 (4:43 am)
would this code allow me to have a still person look around?ie: i want my character to be standing still and while in 3rd view, have him look left/right and NOT move his lower half.
im not planning on using this for a mecha but for bipeds.
I'd like the ability for my character to look left and right without fully rotating the charcter.
Thanks again!
anyone know where i can find more about creating custom animations? links?
I want to integrate this with crouching, walking, running, and jumping/jetting.
#10
If you only want your character to look left and right, use the code in the default engine. You just need to have the correct animations (look and headside) in your character. The Orc already has them.
A search in the forums will get you the other resources for which you are looking.
Thanks.
07/09/2005 (8:43 pm)
Alaric,If you only want your character to look left and right, use the code in the default engine. You just need to have the correct animations (look and headside) in your character. The Orc already has them.
A search in the forums will get you the other resources for which you are looking.
Thanks.
#11
after
(after setting "yaw"):
Am I lost somewhere in code?
And... in GetMuzzleTransform:
01/06/2006 (3:45 pm)
hmm.. how this should work, if in player.cc in void Player::updateMove(const Move* move)after
mHead.x = mClampF(mHead.x + p,mDataBlock->minLookAngle,
mDataBlock->maxLookAngle);we add:F32 t = move->twist; if (t > M_PI) t -= M_2PI; mHead.z = mClampF(mHead.z + t,-mDataBlock->maxFreelookAngle,mDataBlock->maxFreelookAngle);but just few lines after we have:
(after setting "yaw"):
F32 y = move->yaw;
if (y > M_PI) y -= M_2PI;
GameConnection* con = getControllingClient();
if (move->freeLook && ((isMounted() && getMountNode() == 0) || (con && !con->isFirstPerson())))
{
mHead.z = mClampF(mHead.z + y,
-mDataBlock->maxFreelookAngle,
mDataBlock->maxFreelookAngle);
}
else
{
mRot.z += y;
// Rotate the head back to the front, center horizontal
// as well if we're controlling another object.
mHead.z *= 0.5;
if (mControlObject)
mHead.x *= 0.5;
}where mHead.z is anyway overwritten?Am I lost somewhere in code?
And... in GetMuzzleTransform:
- mat->mul(getTransform(), pmat); + mat->mul(getTransform(), xmat);But in code it IS xmat ... shouldn't it be
- mat->mul(getTransform(), xmat); + mat->mul(getTransform(), pmat);?
#12
You understand the code enough to realize that you have to remove any other lines that change mHead.z.
Good catch with the error in GetMuzzleTransform. I have corrected it above and in the file.
Thanks.
02/23/2006 (11:13 am)
bank,You understand the code enough to realize that you have to remove any other lines that change mHead.z.
Good catch with the error in GetMuzzleTransform. I have corrected it above and in the file.
Thanks.
#13
How do I get the torso twist to be animated? You mentioned briefly to have the "headside" and "look" animation. I do have these defined in the player and are able to play them standalone, but not when I'm twisting with the ., keys.
Thanks.
07/10/2006 (11:12 pm)
Hi Derk,How do I get the torso twist to be animated? You mentioned briefly to have the "headside" and "look" animation. I do have these defined in the player and are able to play them standalone, but not when I'm twisting with the ., keys.
Thanks.
#14
Make sure you have selected them to be "blend" animations. Otherwise, they won't "blend" with the root and run animations.
Thanks.
07/12/2006 (1:12 pm)
Ron,Make sure you have selected them to be "blend" animations. Otherwise, they won't "blend" with the root and run animations.
Thanks.
#15
I made an adjustment to this resource(thanks for the original, by the way, Derk :D) so that when you hit the threshold of the freelook, you'll start pivoting, like Halo 2, and other games like that.
It's pretty quick and easy, you just find the twist code in updateMove(), as seen here:
and change it so that it looks like:
And yes, you do over write the yaw portion of that code. All other original changes in the resource apply.
Now you have twisting so your lower half stays put untill you look too far(defined by the maxFreeLook) and then you start pivoting!
Hope that helps!
01/01/2008 (5:15 am)
While this may not be part of the intended implementation, I found it needed for my FPS, and I think other people have been wondering about this as well.I made an adjustment to this resource(thanks for the original, by the way, Derk :D) so that when you hit the threshold of the freelook, you'll start pivoting, like Halo 2, and other games like that.
It's pretty quick and easy, you just find the twist code in updateMove(), as seen here:
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);
and change it so that it looks like:
F32 p = move->pitch;
if (p > M_PI) p -= M_2PI;
mHead.x = mClampF(mHead.x + p,mDataBlock->minLookAngle,
mDataBlock->maxLookAngle);
F32 y = move->yaw;
F32 t = move->twist;
if (t > M_PI)
t -= M_2PI;
if((mHead.z + t) > mDataBlock->maxFreelookAngle)
{
y += (mHead.z + t) - mDataBlock->maxFreelookAngle;
}
else if((mHead.z + t) < -mDataBlock->maxFreelookAngle)
{
y += mDataBlock->maxFreelookAngle + (mHead.z + t);
}
mHead.z = mClampF(mHead.z + t,-mDataBlock->maxFreelookAngle,mDataBlock->maxFreelookAngle);
//if we've overstepped on the yaw...
if (y > M_PI)
y -= M_2PI;And yes, you do over write the yaw portion of that code. All other original changes in the resource apply.
Now you have twisting so your lower half stays put untill you look too far(defined by the maxFreeLook) and then you start pivoting!
Hope that helps!
#16
But I finally knew that it is very useful resource. :-)
01/11/2008 (6:32 am)
Hmm.. a word 'twist' make it a bit wiried at first seeing only the title though.But I finally knew that it is very useful resource. :-)
#17
I was wondering, does anyone have an idea how one might go about controling a bot using this? AIPlayer has all the goodness that we added into the Player class, so there is twist ability encoded (right?). But the only example given to apply twist was with the $mvTwist globals (which will control the player's twist - to my understanding). Anyone have any insight to offer? Thanks.
02/25/2008 (1:17 am)
Great resource.I was wondering, does anyone have an idea how one might go about controling a bot using this? AIPlayer has all the goodness that we added into the Player class, so there is twist ability encoded (right?). But the only example given to apply twist was with the $mvTwist globals (which will control the player's twist - to my understanding). Anyone have any insight to offer? Thanks.
#18
EDIT: Actually, not the only problem. The looking and twisting is fine, but when you run, you move in the direction you're facing, not twisting. Which is cool for a mecha game, but extremely annoying for an FPS. I'll see what I can do... I'd say that after adjusting mRot.z to be 0...2PI, you check to see if the mRot vector is different to the mHead vector. If so, and we're moving, change mRot to be centered to mHead.
02/29/2008 (1:30 pm)
Very nice resource, and thank you Jeff for the modifications! The only problem is that the look animation is not a blend by default (I think), so until I create my own character I can't see the animation. Grr.EDIT: Actually, not the only problem. The looking and twisting is fine, but when you run, you move in the direction you're facing, not twisting. Which is cool for a mecha game, but extremely annoying for an FPS. I'll see what I can do... I'd say that after adjusting mRot.z to be 0...2PI, you check to see if the mRot vector is different to the mHead vector. If so, and we're moving, change mRot to be centered to mHead.
#19
My additions go after the following part in the code (in Player::updateMove):
Just add
Hmm... there seems to be something wrong with this code, though I can't figure out what. It seems to overcorrect the rotation, so you end up facing way off where you were looking.
This is fixed, so you now face the right direction, but for some reason there's a bit of wobble before you get on the right track.
03/08/2008 (1:17 am)
Okay, problem solved. The following will turn your character's torso to face the same way as his head whenever you're moving.My additions go after the following part in the code (in Player::updateMove):
GameConnection* con = getControllingClient();
if (move->freeLook && ((isMounted() && getMountNode() == 0) ||
(con && !con->isFirstPerson())))
{
...
}
else
{
...
}Just add
//Dan's twist improvements ->
//If we're moving, centre legs to twist
if(((move->x != 0.0f) || (move->y != 0.0f))
&& (mState == MoveState)
&& (mDamageState == Enabled))
{
//Body rot in +-180deg
F32 torso = mRot.z - M_PI_F;
//Head rot in absolute terms
F32 head = mHead.z;
//Scale by speed factor
head *= 0.25;
//Add to body, subtract from head
mRot.z += head / 2;
mHead.z -= head;
}
//<- DanHmm... there seems to be something wrong with this code, though I can't figure out what. It seems to overcorrect the rotation, so you end up facing way off where you were looking.
This is fixed, so you now face the right direction, but for some reason there's a bit of wobble before you get on the right track.
#20
07/29/2008 (9:07 am)
Hi i have a problem, in my engine i dont have gameconnectionsMoves.h, i only have gameConnection.h and gameConnectionEvents.h :S, if you can help me.. 
Torque Owner Funky Diver
Helps to understand lots of things for me!
Thanks!