Forcing player to look at an object
by Kirk Haynes · in Torque Game Engine · 09/16/2006 (7:48 pm) · 11 replies
I'd like to know how to modify the Player class to have the player look at a specific point in a network friendly manner.
When the player enter's a trigger, the object the player controls Player::mControlObject is set in script to a 3D cursor: %obj.setControlObject( GameBoard.cursor);
While the Player is in the trigger area, the Player filters the yaw and pitch (mouse control) and passes them to mControlObject, the 3D cursor, then keeps the y and x (forward/backward and sidestep left/right) so the player can leave the trigger area by moving forward/backward/sidstep left/sidestep right.
I'd like to have the player looking at the center of the 3D game the 3D cursor is over. I have the point and can set the player to look at it, but I get a constant jitter between the player looking directly ahead, and the player looking at the center of the gameboard interface. Any ideas on what I'm doing incorrectly?
When the player enter's a trigger, the object the player controls Player::mControlObject is set in script to a 3D cursor: %obj.setControlObject( GameBoard.cursor);
While the Player is in the trigger area, the Player filters the yaw and pitch (mouse control) and passes them to mControlObject, the 3D cursor, then keeps the y and x (forward/backward and sidestep left/right) so the player can leave the trigger area by moving forward/backward/sidstep left/sidestep right.
I'd like to have the player looking at the center of the 3D game the 3D cursor is over. I have the point and can set the player to look at it, but I get a constant jitter between the player looking directly ahead, and the player looking at the center of the gameboard interface. Any ideas on what I'm doing incorrectly?
#2
Have you already looked at this?
09/16/2006 (9:42 pm)
www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=4077Have you already looked at this?
#3
09/16/2006 (10:04 pm)
I had not, but now I have. The code is similar, and there are several posts about this producing a 'jerking' motion on the camera. I think I may have to intercept the move on the client side before it's sent to the server, but I don't know.
#4
10/05/2006 (10:27 am)
Any suggestions?
#5
10/05/2006 (11:05 am)
Generally when you experince jerking issues like that, there is a conflict between the server and client ghost. I had an issue like this for a while when I was setting dynamic clamps on my players view rotation. It seems like you are only letting this code act on the server object. Control objects use client side predicition to allow smooth movement (while still ultimately being controlled by the server), so their physics need to be able to run on the client side as well. So I thiiiiiink you would need to remove the isServerObject check and make sure your MatchCursor data is synced between the server and client.
#6
I have removed the dependency on the cursor. Instead I created a mLookAtPoint Point3F member for the player and set it when the player registers to play the game (steps into the trigger area), then update the client with that point.
I have also removed the isServerObject() and it does behave slightly better, however there is still jerking. So it must have to do with something that's different between the client and server. with respect to some state data that I'm not seeing.
I need a list of the code in the Player class that would have to be set to make the player on the server turn to look at a 3D point, then sync it up with the client.
10/05/2006 (12:06 pm)
Thank you for the response. I have removed the dependency on the cursor. Instead I created a mLookAtPoint Point3F member for the player and set it when the player registers to play the game (steps into the trigger area), then update the client with that point.
I have also removed the isServerObject() and it does behave slightly better, however there is still jerking. So it must have to do with something that's different between the client and server. with respect to some state data that I'm not seeing.
I need a list of the code in the Player class that would have to be set to make the player on the server turn to look at a 3D point, then sync it up with the client.
#7
10/05/2006 (1:17 pm)
For something that isn't being directly controlled by player input, you're going to need to code in interpolation as well. I was a little confused since you have control over this point, but the point still needs to have the lookat interpolated to.
#8
This causes random jitter. Sometime's there's no jitter, sometimes there is. When there's jitter I'll break into the client and server and see what's different between.
10/06/2006 (8:04 am)
This is the current code.This causes random jitter. Sometime's there's no jitter, sometimes there is. When there's jitter I'll break into the client and server and see what's different between.
//if ( isServerObject() )
{
ShapeBase * cursor = dynamic_cast<GameCursor *>( &*mControlObject);
if ( !cursor )
cursor = dynamic_cast<StaticShape *>( &*mControlObject);
if ( cursor )
{
MatrixF mat;
getEyeTransform( & mat);
Point3F eyePosition = mat.getPosition();
Point3F rotation = getRotation();
Point3F aimLocation = mLookAtPoint;
F32 xDiff = (aimLocation.x - eyePosition.x);
F32 yDiff = (aimLocation.y - eyePosition.y);
if ( ! isZero( xDiff) || ! isZero( yDiff) )
{
// First do Yaw
// use the cur yaw between -Pi and Pi
F32 curYaw = rotation.z;
while (curYaw > M_2PI)
curYaw -= M_2PI;
while (curYaw < -M_2PI)
curYaw += M_2PI;
// find the yaw offset
F32 newYaw = mAtan( xDiff, yDiff );
F32 yawDiff = newYaw - curYaw;
// make it between 0 and 2PI
if( yawDiff < 0.0f )
yawDiff += M_2PI;
else if( yawDiff >= M_2PI )
yawDiff -= M_2PI;
// now make sure we take the short way around the circle
if( yawDiff > M_PI )
yawDiff -= M_2PI;
else if( yawDiff < -M_PI )
yawDiff += M_2PI;
pMove.yaw = yawDiff;
F32 vertDist = aimLocation.z - eyePosition.z;
F32 horzDist = mSqrt( xDiff * xDiff + yDiff * yDiff);
F32 newPitch = mAtan( horzDist, vertDist) - ( M_PI / 2.0f );
if ( mFabs( newPitch) > 0.01 )
{
Point3F headRotation = getHeadRotation();
pMove.pitch = newPitch - headRotation.x;
}
}
}
}
#9
client
- delta {move={...} dt=0.00000000 pos={...} ...} BigPlayer::StateDelta
- move {px=16 py=16 pz=16 ...} Move
px 16 int
py 16 int
pz 16 int
pyaw 0 unsigned int
ppitch 0 unsigned int
proll 0 unsigned int
x 0.00000000 float
y 0.00000000 float
z 0.00000000 float
yaw 0.00074178376 float
pitch 0.12683995 float
roll 0.00000000 float
id 0 unsigned int
sendCount 0 unsigned int
freeLook false bool
- trigger 0x0b44b605 bool [6]
[0] false bool
[1] false bool
[2] false bool
[3] false bool
[4] false bool
[5] false bool
dt 0.00000000 float
+ pos {x=409.20267 y=324.91602 z=219.83800 } Point3F
+ rot {x=0.00000000 y=0.00000000 z=5.9339237 } Point3F
+ head {x=0.13208850 y=0.00000000 z=0.00000000 } Point3F
+ posVec {x=0.00000000 y=0.00000000 z=0.00000000 } Point3F
+ rotVec {x=0.00000000 y=0.00000000 z=-0.00074195862 } Point3F
+ headVec {x=-4.5299530e-005 y=0.00000000 z=0.00000000 } Point3F
warpTicks 0 int
+ warpOffset {x=0.00000000 y=0.00000000 z=0.00000000 } Point3F
+ rotOffset {x=0.00000000 y=0.00000000 z=0.00000000 } Point3F
mPredictionCount -2 int
+ mHead {x=0.13208850 y=0.00000000 z=0.00000000 } Point3F
+ mRot {x=0.00000000 y=0.00000000 z=5.9339237 } Point3F
+ mVelocity {x=0.00000000 y=0.00000000 z=-1.3586646e-024 } Point3F
+ mAnchorPoint {x=0.00000000 y=0.00000000 z=100.00000 } Point3F
server
- delta {move={...} dt=1.0000000 pos={...} ...} BigPlayer::StateDelta
- move {px=16 py=16 pz=16 ...} Move
px 16 int
py 16 int
pz 16 int
pyaw 0 unsigned int
ppitch 0 unsigned int
proll 0 unsigned int
x 0.00000000 float
y 0.00000000 float
z 0.00000000 float
yaw -1.7484555e-007 float
pitch 0.13208850 float
roll 0.00000000 float
id 0 unsigned int
sendCount 0 unsigned int
freeLook false bool
- trigger 0x0b4422b5 bool [6]
[0] false bool
[1] false bool
[2] false bool
[3] false bool
[4] false bool
[5] false bool
dt 1.0000000 float
+ pos {x=0.00000000 y=0.00000000 z=100.00000 } Point3F
+ rot {x=0.00000000 y=0.00000000 z=5.9339237 } Point3F
+ head {x=0.13208850 y=0.00000000 z=0.00000000 } Point3F
+ posVec {x=409.20267 y=324.91602 z=219.83800 } Point3F
+ rotVec {x=0.00000000 y=0.00000000 z=0.00000000 } Point3F
+ headVec {x=0.00000000 y=0.00000000 z=0.00000000 } Point3F
warpTicks 0 int
+ warpOffset {x=0.00000000 y=0.00000000 z=0.00000000 } Point3F
+ rotOffset {x=0.00000000 y=0.00000000 z=0.00000000 } Point3F
mPredictionCount 30 int
+ mHead {x=0.13208850 y=0.00000000 z=0.00000000 } Point3F
+ mRot {x=0.00000000 y=0.00000000 z=5.9339237 } Point3F
+ mVelocity {x=0.00000000 y=0.00000000 z=-3.7663969e-025 } Point3F
+ mAnchorPoint {x=0.00000000 y=0.00000000 z=100.00000 } Point3F
10/06/2006 (8:38 am)
A capture of client and server while there is a little jitter.client
- delta {move={...} dt=0.00000000 pos={...} ...} BigPlayer::StateDelta
- move {px=16 py=16 pz=16 ...} Move
px 16 int
py 16 int
pz 16 int
pyaw 0 unsigned int
ppitch 0 unsigned int
proll 0 unsigned int
x 0.00000000 float
y 0.00000000 float
z 0.00000000 float
yaw 0.00074178376 float
pitch 0.12683995 float
roll 0.00000000 float
id 0 unsigned int
sendCount 0 unsigned int
freeLook false bool
- trigger 0x0b44b605 bool [6]
[0] false bool
[1] false bool
[2] false bool
[3] false bool
[4] false bool
[5] false bool
dt 0.00000000 float
+ pos {x=409.20267 y=324.91602 z=219.83800 } Point3F
+ rot {x=0.00000000 y=0.00000000 z=5.9339237 } Point3F
+ head {x=0.13208850 y=0.00000000 z=0.00000000 } Point3F
+ posVec {x=0.00000000 y=0.00000000 z=0.00000000 } Point3F
+ rotVec {x=0.00000000 y=0.00000000 z=-0.00074195862 } Point3F
+ headVec {x=-4.5299530e-005 y=0.00000000 z=0.00000000 } Point3F
warpTicks 0 int
+ warpOffset {x=0.00000000 y=0.00000000 z=0.00000000 } Point3F
+ rotOffset {x=0.00000000 y=0.00000000 z=0.00000000 } Point3F
mPredictionCount -2 int
+ mHead {x=0.13208850 y=0.00000000 z=0.00000000 } Point3F
+ mRot {x=0.00000000 y=0.00000000 z=5.9339237 } Point3F
+ mVelocity {x=0.00000000 y=0.00000000 z=-1.3586646e-024 } Point3F
+ mAnchorPoint {x=0.00000000 y=0.00000000 z=100.00000 } Point3F
server
- delta {move={...} dt=1.0000000 pos={...} ...} BigPlayer::StateDelta
- move {px=16 py=16 pz=16 ...} Move
px 16 int
py 16 int
pz 16 int
pyaw 0 unsigned int
ppitch 0 unsigned int
proll 0 unsigned int
x 0.00000000 float
y 0.00000000 float
z 0.00000000 float
yaw -1.7484555e-007 float
pitch 0.13208850 float
roll 0.00000000 float
id 0 unsigned int
sendCount 0 unsigned int
freeLook false bool
- trigger 0x0b4422b5 bool [6]
[0] false bool
[1] false bool
[2] false bool
[3] false bool
[4] false bool
[5] false bool
dt 1.0000000 float
+ pos {x=0.00000000 y=0.00000000 z=100.00000 } Point3F
+ rot {x=0.00000000 y=0.00000000 z=5.9339237 } Point3F
+ head {x=0.13208850 y=0.00000000 z=0.00000000 } Point3F
+ posVec {x=409.20267 y=324.91602 z=219.83800 } Point3F
+ rotVec {x=0.00000000 y=0.00000000 z=0.00000000 } Point3F
+ headVec {x=0.00000000 y=0.00000000 z=0.00000000 } Point3F
warpTicks 0 int
+ warpOffset {x=0.00000000 y=0.00000000 z=0.00000000 } Point3F
+ rotOffset {x=0.00000000 y=0.00000000 z=0.00000000 } Point3F
mPredictionCount 30 int
+ mHead {x=0.13208850 y=0.00000000 z=0.00000000 } Point3F
+ mRot {x=0.00000000 y=0.00000000 z=5.9339237 } Point3F
+ mVelocity {x=0.00000000 y=0.00000000 z=-3.7663969e-025 } Point3F
+ mAnchorPoint {x=0.00000000 y=0.00000000 z=100.00000 } Point3F
#10
10/06/2006 (8:56 am)
There is a significant difference between the rotation values there. The code is probably not run the same on both client and server because they are out of sync.
#11
10/06/2006 (8:59 am)
So how do I sync them? What do I add to the code Posted: Oct 06, 2006 11:04 to sync them?
Torque Owner Kirk Haynes
void BigPlayer::processTick(const Move* move) { PROFILE_START( BigPlayer_ProcessTick); Move aiMove; if (!move && isServerObject() && getAIMove(&aiMove)) move = &aiMove; Move pMove,cMove; if (mControlObject) { if (!move) mControlObject->processTick(0); else { pMove = NullMove; cMove = *move; if (isMounted()) { // Filter Jump trigger if mounted pMove.trigger[2] = move->trigger[2]; cMove.trigger[2] = false; } else { pMove.x = move->x; pMove.y = move->y; pMove.trigger[2] = move->trigger[2]; } if (move->freeLook) { // Filter yaw/picth/roll when freelooking. pMove.yaw = move->yaw; pMove.pitch = move->pitch; pMove.roll = move->roll; pMove.freeLook = true; cMove.freeLook = false; cMove.yaw = cMove.pitch = cMove.roll = 0; } mControlObject->processTick( (mDamageState == Enabled) ? & cMove : & NullMove); // look at the center of mCursorBox; if ( isServerObject() ) { MatchCursor * cursor = dynamic_cast<MatchCursor *>( &*mControlObject); if ( cursor ) { MatrixF eye; getEyeTransform( & eye); Point3F eyePosition = eye.getPosition(); Point3F rotation = getRotation(); Box3F cursorBox = cursor->CursorBox(); Point3F result; cursorBox.getCenter( &result); result.x = -result.x; // transform to world space eye = cursor->getTransform(); eye.setPosition( Point3F( 0, 0, 0)); Point3F aimLocation; eye.mulP( result, & aimLocation); aimLocation += cursor->GameBoard()->getPosition(); F32 xDiff = (aimLocation.x - eyePosition.x); F32 yDiff = (aimLocation.y - eyePosition.y); if ( ! isZero( xDiff) || ! isZero( yDiff) ) { // First do Yaw // use the cur yaw between -Pi and Pi F32 curYaw = rotation.z; while (curYaw > M_2PI) curYaw -= M_2PI; while (curYaw < -M_2PI) curYaw += M_2PI; // find the yaw offset F32 newYaw = mAtan( xDiff, yDiff ); F32 yawDiff = newYaw - curYaw; // make it between 0 and 2PI if( yawDiff < 0.0f ) yawDiff += M_2PI; else if( yawDiff >= M_2PI ) yawDiff -= M_2PI; // now make sure we take the short way around the circle if( yawDiff > M_PI ) yawDiff -= M_2PI; else if( yawDiff < -M_PI ) yawDiff += M_2PI; pMove.yaw = yawDiff; // This should be adjusted to run from the // eye point to the object's center position. Though this // works well enough for now. F32 vertDist = aimLocation.z - eyePosition.z; F32 horzDist = mSqrt( xDiff * xDiff + yDiff * yDiff); F32 newPitch = mAtan( horzDist, vertDist) - ( M_PI / 2.0f ); if ( mFabs( newPitch) > 0.01 ) { Point3F headRotation = getHeadRotation(); pMove.pitch = newPitch - headRotation.x; } } } } move = &pMove; } } Parent::processTick(move); if ( delta.warpTicks > 0 ) { delta.warpTicks--; // Set new pos. getTransform().getColumn( 3, &delta.pos); delta.pos += delta.warpOffset; delta.rot += delta.rotOffset; setPosition(delta.pos,delta.rot); setRenderPosition(delta.pos,delta.rot); updateDeathOffsets(); updateLookAnimation(); // Backstepping delta.posVec.x = -delta.warpOffset.x; delta.posVec.y = -delta.warpOffset.y; delta.posVec.z = -delta.warpOffset.z; delta.rotVec.x = -delta.rotOffset.x; delta.rotVec.y = -delta.rotOffset.y; delta.rotVec.z = -delta.rotOffset.z; } else { if (!move) { if (isGhost()) { if (mPredictionCount-- <= 0) { PROFILE_END(); return; } move = &delta.move; } else move = &NullMove; } if (!isGhost()) updateAnimation(TickSec); PROFILE_START( BigPlayer_PhysicsSection); if(isServerObject() || (didRenderLastRender() || getControllingClient())) { updateWorkingCollisionSet(); updateState(); updateMove(move); updateLookAnimation(); updateDeathOffsets(); updatePos(); } PROFILE_END(); if (!isGhost()) { updateActionThread(); updateAnimationTree(true); } } PROFILE_END(); }