"Camera space" controls in Quake/HL

Discuss programming topics for the various GPL'd game engine sources.

Moderator: InsideQC Admins

"Camera space" controls in Quake/HL

Postby crystallize » Sat May 19, 2018 2:45 pm

Hello, thanks for an approval. :)

I am trying to implement Mario64/Sonic Adventure-like scheme of camera and controls into Half-Life singleplayer. Not working on it every single day but been trying to do it for over 6 months by now. Back then I started with a server-side camera as a reference point, then I moved to client side camera and now my reference point is just 0, 0, 0 with a camera as an irrelevant top-down observer. I did this because I've been trying to avoid the same weird bug that just kept occurring.

My assumption for a control scheme like this was: as long as the camera isn't moving and it is pointed straight at the player, and as long as player is moving 90 degrees left or right from camera's view direction, player would walk in perfect circles around the camera.

However I was mistaken and the following glitch kept occurring: player would walk not in circles but in a spiral.

The code for this is simple:
Code: Select all
void V_CalcThirdPersonRefdef( struct ref_params_s * pparams )
    // make a variable with shorter name
    VectorCopy ( pparams->simorg, v_sim_org );//player origin

    // ======Make camera calculations======
    VectorAngles ( player->curstate.origin, pparams->cl_viewangles );

    // write back new values into pparams
    pparams->viewangles[0]= 90;// top-down
    // end of block for thirdperson camera

Pretty much a single VectorAngles function.

At that point I was kind of desperate already. I tried to do the same thing in Quake and it turned out to be exactly the same:

Code: Select all
void Chase_UpdateForDrawing (void)
    int     i;
    float   dist;
    vec3_t  forward, up, right;
    vec3_t  crosshair, temp;
    r_refdef.vieworg[0] = 0;
    r_refdef.vieworg[1] = 0;
    r_refdef.vieworg[2] = 999;

    VectorAngles (cl.viewent.origin, cl.viewangles);

    r_refdef.viewangles[PITCH] = 90;
    r_refdef.viewangles[YAW] = 0;

People more or less explained it to me that in Half-Life's client-server architecture pparams->cl_viewangles is sent to server, to be modified by server-side entities like rotating platforms apparently, and then it returns to the client one frame later. So the client is dealing with an outdated cl_viewangles all the time which creates some kind of an error.

But may be this lag is not that severe and the real bug is somewhere else? For example, in my assumption about moving at perfect 90 degrees to the side. Considering how smooth movement doesn't really exist in a game but a sequence of discrete frames only, I can imagine a circle as a really smooth n-gon and try to predict a correction I have to apply to cl_viewangles as an angle between its sides. I need a horda, radius, and frametime: angle=2*asin(horda/2*Radius)

Code: Select all
// V_CalcThirdPersonRefdef
Vector plyawtracker, plyawtrackerold, plyawtrackerdiff;
void V_CalcThirdPersonRefdef( struct ref_params_s * pparams )
   cl_entity_t *ent = GET_LOCAL_PLAYER();
   static float M_RADIAN = 57.2958;
   pparams->vieworg[2] = 999;
   ALERT (at_console, "radius = %.3f ", pparams->simorg.Length());
   Vector onefrmvel = ent->curstate.velocity * pparams->frametime;
   float fl_onefrmvel = onefrmvel.Length();
   float sinusfood = fl_onefrmvel / ( 2 * pparams->simorg.Length() );
   //ALERT (at_console, "sinfood = %.3f\n", sinusfood);
   float yaw_corr_rad = 2 * asin (sinusfood);
   float yaw_corr_deg = yaw_corr_rad * M_RADIAN;

   VectorAngles (pparams->simorg, pparams->cl_viewangles);
   ALERT (at_console, "pl yaw, raw and corrected: %.3f, ", pparams->cl_viewangles[1] );
   if (pparams->cl_viewangles[1]<0)
      yaw_corr_deg = -yaw_corr_deg;
   pparams->cl_viewangles[1] += yaw_corr_deg;
   ALERT (at_console, "%.3f\n", pparams->cl_viewangles[1] );

Still no improvement. Out of curiosity I tried to tweak this line
Code: Select all
float yaw_corr_rad = 2 * asin (sinusfood);

to figure out an actual value that would actually keep the player from running away. It turned out, for 185 unit radius I needed to replace my 2 multiplier with 13.45475 which meant that my calculations were several times off. And it only worked for this exact radius despite radius being already considered in the formula.

Of course I can keep trying to perfect this approach: consider which key, strafe left of strafe right is held down, or I can get rid of curstate.velocity and substract old origin from new one to calculate 1-frame movement, I can also consider CL_Keystate multiplier. But am I even moving in a right direction with all this? I have a history of being too humble to admit that the problem whose solution I am trying to perfect, shouldn't exist at all.
Posts: 1
Joined: Tue May 15, 2018 5:18 pm

Return to Engine Programming

Who is online

Users browsing this forum: No registered users and 2 guests