Jittering quakestyle movement when circle strafing.

Started by
18 comments, last by Erik Labree 23 years, 9 months ago
I know what Im talking about, If you are cheking an array of keys, and see'ing if any of them are TRUE, YOU CANNOT USE ELSE's or it will only evaluate 1 movement a frame, K
But you not so just ignore me

Edited by - Esap1 on June 26, 2000 6:55:23 PM
Advertisement
Erik , if, let's say, a player is holding down the 'left' key, are you sure that event fires exactly once per frame? If your game loop takes, say, 10 ms and the event were to fire every 9 ms, then you'd get a "jerk" about every 9 frames.

I think checking the key states, like Esap1 mentioned, is a better way to do input for a game.

Edited by - Eric on June 27, 2000 2:48:20 AM
Esap1 & Eric,

The piece of code you see is placed in a callback wich is called for every key each frame so every event gets a chance to get handled. So it basically does the same as 'checking an array of keys without elses' It's only a different implementation wich has some advantages as pointed out in the article by Chris Hargrove.
I apoligize, you guys couldn't make that up from the code in the original post. Sorry.
Let me put it differently have you guys got it working smoothly with an 'array of keys' ? If so i'll rewrite the code to do so too.

Regards Erik

Edited by - Erik Labree on June 27, 2000 7:06:04 AM

Edited by - Erik Labree on June 27, 2000 7:07:29 AM
Not really experienced with this sort of stuff, but I''ll throw in my views anyway...

From what I see, you have a loop like:
   Check for ''Up / Down / Strafe'' movement; Move the camera appropriately.   Check for ''Mouse Look'' movement; Move the camera appripriately. 


Right? If you just turn around, or simply head forward, you''re moving the camera once. The scene is updated, and you''ve got your smooth screen movement.

Ok, so with each loop you circle-strafe instead, you''re moving the camera twice. Once for the Strafe, and once for the Mouse Look... You''re jittering the camera a bit then, aren''t you? Within a few moments, you''re repositioning the camera.

The solution would, of course, (if I''m right) be:
   Check for ''Up / Down / Strave movement; Record what should be the camera movement.   Check for ''Mouse Look'' movement; Record what should be the camera movement.   Move the camera using the variables we''ve recorded. 


With any luck, this might help, or at least put you on track... Hope I''ve been useful. =)

ScriptKeeper.
scriptkeeper@www.com
ScriptKeeper,

That''s already what is happening in this code. See previous posts.

Thanks Erik
Hi, I would try making a seperate case that''s handled for circle strafe. Then when this happens smoothly rotate your guy around
a point a few feet in front of his body. Try something like this


you.z = focus_position.z +
(distance_to_focus * cos(angle_to_rotate) );
you.x = focus_position.x +
(distance_to_focus * sin(angle) );


I
author of the Helping Phriendly Book

Erik, try this :

float dx ;
static float old_dx = 0 ;

float dalpha ;
static float old_dalpha = 0 ;

dx = ( ( float ) mouseDeltaX + old_dx ) / 2.0 ;
old_dx = dx ; //or old_dx=mouseDeltaX, choose what looks better
to you
//dx is smoother, mouseDeltaX gives better mouse
response

dalpha = ( dx / MOUSE_COMPENSATION + old_dalpha ) / 2.0 ;
old_dalpha = dalpha ;

//strafe( STRAFE )
//rotate( dalpha )

...or if it''s still not smooth enough try this :


float dx ;
static float old_dx = 0, old2_dx = 0 ;

float dalpha ;
static float old_dalpha = 0, old2_dalpha = 0 ;

dx = ( ( float ) mouseDeltaX + old_dx + old2_dx ) / 3.0 ;
old2_dx = old_dx ;
old_dx = mouseDeltaX ; //better, because more you''re averaging,
poorer response you get
//mouseDeltaX instead of dx will
compensate that a bit

dalpha = ( dx / MOUSE_COMPENSATION + old_dalpha +
old2_dalpha ) / 3.0 ;
old2_dalpha = old_dalpha ;
old_dalpha = dalpha ;

//strafe( STRAFE )
//rotate( dalpha )



Instead of :

//strafe( STRAFE )
//rotate( dalpha )

...you could also :

//rotate( dalpha / 2 )
//strafe( STRAFE )
//rotate( dalpha / 2 )

(rotate/2, strafe, rotate/2) is something in between
of (rotate,strafe) and (strafe,rotate).

Try to draw these 3 cases on paper with dalpha = 45 degrees.
You will see what I mean.
It''s some kind of aproximation of rotating and moving at
the same time.

Let me know if this helped !

Bojan Urosevic (aka Coyote)
coyotelb@yahoo.com

Bojan,

Interesting ideas. Especially case 3. Work is bit hectic so i''ll have to wait till the weekend to try''m out. I''ll let you know how it went.

Thanks Erik.
A little mathematical supplemental about moving and rotating
at the same time. (by Coyote)

Note: All of this is almost academic, because it is hard to
notice differences in a fast-paced real-time motion.
Although, it''s interesting.

!! ALMOST EVERY VARIABLE IS FLOAT, ANGLES ARE IN RADIANS !!

To be honest, (rotate/2, strafe, rotate/2) (read my previous post)
was taken from a small sketch on paper. It seemed much more accurate
position after rotating and moving then (rotate, strafe) or
(strafe, rotate), especially for big angles.

If you try to draw several (r,s) for some large angle, you will
see that points that this motion produces are on displaced circle.
Also, when strafing left and rotating right (Erik called this
circular strafe) point of view moves around a circle, and it shoudn''t.

Several (r/2,s,r/2), on the other hand, makes the point of view
stationary, and removes displacement of motion circle.

I will demonstrate 2 ways of finding accurate position after strafing
and rotating at the same time.

First way is, let''s say, programmatic. It''s dividing the strafe and angle
into N parts, and then doing it N times:

//angle is amount of turn, relative to the heading
//for the simplicity:
// strafe is amount of move in the heading direction (forward)
// let everything happens in xy coordinate system
// let the heading be 0 and let that means the x-axis direction
// let the old position is at origin (0,0)

new_x = new_y = 0 ;
for( i = 1 ; i < N ; i++ ) {
new_x = new_x + ( strafe / N ) * cos( i * angle / N ) ;
new_y = new_y + ( strafe / N ) * sin( i * angle / N ) ;
}

Note that this rotates than moves because of i*angle/N.
Other way is (i-1)*angle/N which yields move followed by a rotation.
If N is great both converges to the same.

And to what does this remind you to ?
Yes, when N->infinity, it can be (mathematically) written as:

new_x = strafe * integral(0,1)cos(angle*x)dx
new_y = strafe * integral(0,1)sin(angle*x)dx

(note that x under the integral have no corespondence to position of
any kind, it''s simply integration variable, and angle is a constant
in that integration)

When we evaulate this integrals we get the following formulae:

new_x = strafe * sin( angle ) / angle ;
new_y = strafe * ( 1 - cos( angle ) ) / angle ;

This is accurate position after moving and rotating at the same time.
One more thing. What if angle of desired relative rotation is 0 ?
Well it''s pretty easy to show that:

lim(angle->0)sin(angle)/angle = 1
lim(angle->0)(1-cos(angle))/angle = 0

(it''s 0/0 - use the l''Hospitale rule)

So, if abs(angle) is less than, say, 0.0001 we do not calculate the
formulae but instead we put :

new_x = strafe ;
new_y = 0 ;

Note that if angle is 0 and we are heading to positive x direction this
is exactly where we wanna be !

Point (new_x,new_y) is on the line through origin and that line makes
angle/2 angle with the x-axis. It''s easy to show that. With some simple
trigonometry you will be able to show that :

new_y/new_x = tan(angle/2) ;

( 1-cos(a)=2sin^2(a/2), sin(a)=2sin(a/2)cos(a/2) )

Note that (rotate/2,strafe,rotate/2) gives a point also on that line !

For small angles it''s quite a good approximation, but let see if we can
do it better.

Instead of moving along angle/2 line for strafe amount, why wouldn''t we
calculate exact distance that we should pass :

sqrt(new_x^2 + new_y^2) = strafe * 2*sin(angle/2)/angle

( sin^2(a)+cos^2(a)=1, 1-cos(a)=2sin^2(a/2) )
Note that because the sin(angle/2) and angle are of the same sign
this is correct. Beware of sqrt(a^2) !! sqrt(a^2) = /a/, not a !!

Again l''Hospital rule(z) : lim(angle->0)2*sin(angle/2)/angle=1

So we come to the exact position after rotating (for angle) and moving
forward (for strafe) with :

if( fabs( angle ) < 0.0001 ) k = 1 ;
else k = 2 * sin( angle / 2 ) / angle ;

(rotate(angle/2), strafe( k * strafe), rotate(angle/2))

Other way for reaching the sin(a)/a and (1-cos(a))/a is to note that we
want to move around the circle for L=strafe. This leads to r=strafe/a
(L=r*a). And if we draw that circle it''s simple to "read" the formulae
from the picture. (bottom of the circle should be in origin,
center should be somewhere on the positive y-axis (for positive a))

And yes, don''t strafe left and then strafe forward if left and forward
are pressed together ! Combine it to one strafe at 45 degrees angle !
Otherwise your character will move faster diagonally.

!! ANGLES ARE DEGREES !!

float dir[16] = { -1, 270, 90, -1, 180, 225, 135, 180, 0, 315, 45, 0, -1, 270, 90, -1 } ;
//-1 means that no keys are pressed or (left and right) and/or (forward and back) are
//pressed simuntaneously

dir_ind = forward_key_down * 8 + backward_key_down * 4 + left_key_down * 2 + right_key_down ;
if( dir[dir_ind] >= 0 ) {
if( fabs( angle ) < 0.0001 ) k = 1 ;
else k = 2 * sin( ( angle / 2 ) * PI / 180.0 ) / ( angle * PI / 180.0 ) ;
heading += angle / 2 ; //rotating
strafe( heading + dir[dir_ind], k * strafe_velocity ) ;
heading += angle / 2 ;
} else {
heading += angle ;
}

Sorry for such a long post. I hope you enjoyed it.


Bojan Urosevic (aka Coyote)
coyotelb@yahoo.com

P.S.:
If someone would like to convert this into some kind of a tutorial with
pictures and nice formulae, please do so, but don''t forget to credit me !


My game is designed a little differently such that I might be insulated from this, I''ll share the method in case someone finds it interesting.

Its a multiplayer 3d shooter:
-Players input is sampled and from that I derive messages that are placed on a global internal queue. The keybindging are translater to "Player X pressed forward", "Player X Fired"
-Network layer grabs the echo''s of other players and places them on the global queue as well.
-A bunch of objects read the queue for messages that relate to their function, like Firing, Movement etc. The world representation of the players are modified the same for all players.

The reason I think telling you this might help is that in my game all input is treated equally and processed independantly each frame. So strafing while turning will just produce two independant events that will both modify movement.

As a side note this method is extremely network friendly.
HTH

gimp
Chris Brodie

This topic is closed to new replies.

Advertisement