void game_tick( GameThreadSockets & gsockets, GameState & gs, SharedRenderState & srs, unsigned int now ) { Ogre::Quaternion o; uint8_t b; zstr_send( gsockets.zmq_input_req, "mouse_state" ); char * mouse_state = zstr_recv( gsockets.zmq_input_req ); parse_mouse_state( mouse_state, o, b ); free( mouse_state ); uint8_t w, a, s, d, spc, alt; zstr_send( gsockets.zmq_input_req, "kb_state" ); char * kb_state = zstr_recv( gsockets.zmq_input_req ); parse_kb_state( kb_state, w, a, s, d, spc, alt ); free( kb_state ); // yaw 0 looks towards -Z float yaw = o.getYaw().valueRadians(); Ogre::Vector3 xp( cosf( yaw ), 0.0, -sinf( yaw ) ); Ogre::Vector3 yp( -sinf( yaw ), 0.0, -cosf( yaw ) ); float speed = 10.0f; if ( w ) { srs.position += speed * yp; } if ( s ) { srs.position -= speed * yp; } if ( a ) { srs.position -= speed * xp; } if ( d ) { srs.position += speed * xp; } if ( spc ) { srs.position[1] += speed; } if ( alt ) { srs.position[1] -= speed; } }
void game_tick( unsigned int now, GameState & gs, SharedRenderState & rs ) { // get the latest mouse buttons state and orientation zstr_send( gs.zmq_input_req, "mouse_state" ); char * mouse_state = zstr_recv( gs.zmq_input_req ); Uint8 buttons; Ogre::Quaternion orientation; parse_mouse_state( mouse_state, orientation, buttons ); free( mouse_state ); // at 16 ms tick and the last 10 orientations buffered, that's 150ms worth of orientation history gs.orientation_history[ gs.orientation_index ].t = now; gs.orientation_history[ gs.orientation_index ].o = orientation; gs.orientation_index = ( gs.orientation_index + 1 ) % ORIENTATION_LOG; // oldest orientation unsigned int q1_index = gs.orientation_index; // NOTE: the problem with using the successive orientations to infer an angular speed, // is that if the orientation is changing fast enough, this code will 'flip' the speed around // e.g. this doesn't work, need to use the XY mouse data to track angular speed // NOTE: uncomment the following line to use the full history, notice the 'flip' happens at much lower speed q1_index = ( q1_index + ORIENTATION_LOG - 2 ) % ORIENTATION_LOG; Ogre::Quaternion q1 = gs.orientation_history[ q1_index ].o; Uint32 q1_t = gs.orientation_history[ q1_index ].t; Ogre::Quaternion omega = 2.0f * ( orientation - q1 ) * q1.UnitInverse() * ( 1000.0f / (float)( now - q1_t ) ); omega.ToAngleAxis( gs.smoothed_angular_velocity, gs.smoothed_angular ); // printf( "%f %f %f - %f\n", gs.smoothed_angular.x, gs.smoothed_angular.y, gs.smoothed_angular.z, gs.smoothed_angular_velocity.valueDegrees() ); rs.smoothed_angular = gs.smoothed_angular; if ( ( buttons & SDL_BUTTON( 1 ) ) != 0 ) { if ( !gs.mouse_pressed ) { gs.mouse_pressed = true; // changing the control scheme: the player is now driving the orientation of the head directly with the mouse // tell the input logic to reset the orientation to match the current orientation of the head zstr_sendf( gs.zmq_input_req, "mouse_reset %f %f %f %f", rs.orientation.w, rs.orientation.x, rs.orientation.y, rs.orientation.z ); zstr_recv( gs.zmq_input_req ); // wait for ack from input // IF RENDER TICK HAPPENS HERE: render will not know that it should grab the orientation directly from the mouse, // but the orientation coming from game should still be ok? zstr_sendf( gs.zmq_render_socket, "# %s", "1" ); // IF RENDER TICK HAPPENS HERE (before a new gamestate): // the now reset input orientation will combine with the old game state, that's bad } } else { if ( gs.mouse_pressed ) { gs.mouse_pressed = false; // changing the control scheme: the head will free spin and slow down for a bit, then it will resume bouncing around // the player looses mouse control, the game grabs latest orientation and angular velocity // the input thread was authoritative on orientation until now, so accept that as our starting orientation rs.orientation = orientation; gs.rotation_speed = gs.smoothed_angular_velocity; gs.rotation = gs.smoothed_angular; zstr_sendf( gs.zmq_render_socket, "# %s", "0" ); // IF RENDER TICK HAPPENS HERE (before a new gamestate): render will pull the head orientation from the game state rather than input, but game state won't have the fixed orientation yet } } if ( rs.position.x > gs.bounce || rs.position.x < -gs.bounce ) { gs.direction.x *= -1.0f; } if ( rs.position.y > gs.bounce || rs.position.y < -gs.bounce ) { gs.direction.y *= -1.0f; } Ogre::Vector2 delta = gs.speed * ( (float)GAME_DELAY / 1000.0f ) * gs.direction; if ( !gs.mouse_pressed ) { if ( gs.rotation_speed.valueDegrees() == 0.0f ) { rs.position.x += delta.x; rs.position.y += delta.y; } // printf( "game tick position: %f %f\n", rs.position.x, rs.position.y ); // update the orientation of the head on a free roll // gs.rotation is unit length // gs.rotation_speed is in degrees/seconds // NOTE: sinf/cosf really needed there? gs.rotation_speed *= 0.97f; if ( gs.rotation_speed.valueDegrees() < 20.f ) { gs.rotation_speed = 0.0f; } float factor = sinf( 0.5f * Ogre::Degree( gs.rotation_speed * GAME_TICK_FLOAT ).valueRadians() ); Ogre::Quaternion rotation_tick( cosf( 0.5f * Ogre::Degree( gs.rotation_speed * GAME_TICK_FLOAT ).valueRadians() ), factor * gs.rotation.x, factor * gs.rotation.y, factor * gs.rotation.z ); rs.orientation = rotation_tick * rs.orientation; } else { // keep updating the orientation in the render state, even while the render thread is ignoring it: // when the game thread resumes control of the head orientation, it will interpolate from one of these states, // so we keep updating the orientation to avoid a short glitch at the discontinuity rs.orientation = orientation; } }