Purple Martians
Technical Code Descriptions
Timers
Overview
Menu timer
Second timer
Frames per second timer
Game speed regulation
Speed adjust during netgame
Overview
Three timers are used in the game:
ALLEGRO_TIMER * fps_timer; // used to control the speed of the game
ALLEGRO_TIMER * mnu_timer; // used to control the speed of the menu
ALLEGRO_TIMER * sec_timer; // used to count the actual frames per second
int initial_setup(void)
{
// create timers
fps_timer = al_create_timer(1 / (float) frame_speed );
sec_timer = al_create_timer(1);
mnu_timer = al_create_timer(.01);
// register timer event source
al_register_event_source(event_queue, al_get_timer_event_source(mnu_timer));
// start timers
al_start_timer(fps_timer);
al_start_timer(sec_timer);
al_start_timer(mnu_timer);
...
Menu timer
The game menu runs a loop that processes both the menu and the logo animation.
The function 'proc_controller()' is called in this loop and will block until it gets a timer event.
'mnu_timer' generates events every 0.01s, which means the menu will run at 100 frames per second.
int proc_controllers()
{
int menu_timer_block = 1;
while (menu_timer_block)
{
while (!al_is_event_queue_empty(event_queue))
{
ALLEGRO_EVENT ev;
if (al_get_next_event(event_queue, &ev))
{
if (ev.type == ALLEGRO_EVENT_TIMER) menu_timer_block = 0;
// process other events........
}
}
if (game_exit) // if not in a game, must be in a menu loop
{
//.....
}
else // game is in progress
{
menu_timer_block = 0;
}
}
}
Second timer
Used to count the actual frames per second and the number of skipped frames in the last second.
The algorithm makes use of the following global variables:
int frame_num;
int last_fps_frame_num = 0;
int actual_fps;
int players1[active_local_player].frames_skipped
int last_frames_skipped = 0;
int frames_skipped_last_second;
When 'sec_timer' gets to 1, frames are counted and tallies are reset.
void proc_frame_delay(void)
{
if (al_get_timer_count(sec_timer) > 0)
{
al_set_timer_count(sec_timer, 0); // reset_second_timer
actual_fps = frame_num - last_fps_frame_num;
last_fps_frame_num = frame_num;
frames_skipped_last_second = (players1[active_local_player].frames_skipped - last_frames_skipped);
last_frames_skipped = players1[active_local_player].frames_skipped;
}
...
Frames per second timer
Used to control the speed of the game.
The global variable: 'int frame_speed' sets the target frame rate of the game in frames per second.
fps_timer is set as a reciprocal of frame_speed like this:
al_set_timer_speed(fps_timer, 1/(float)frame_speed);
Game speed regulation
The game speed is regulated by the function: 'proc_frame_delay()'
void proc_frame_delay(void)
{
if (speed_testing) // draw every frame no matter how fast or slow it is
{
draw_frame = 1;
al_set_timer_count(fps_timer, frame_num);
}
else
{
if (frame_num <= al_get_timer_count(fps_timer)) // skip drawing frame
{
draw_frame = 0;
players1[active_local_player].frames_skipped++;
}
else draw_frame = 1;
while (frame_num > al_get_timer_count(fps_timer)); // delay if too far ahead so timer catches up
}
}
When the game is running normally, proc_frame_delay() will wait until fps_timer catches up to the current frame.
while (frame_num > al_get_timer_count(fps_timer));
When the game is lagging, proc_frame_delay() will skip drawing for the current frame.
if (frame_num <= al_get_timer_count(fps_timer))
{
draw_frame = 0;
players1[active_local_player].frames_skipped++;
}
In speed test mode:
- every frame is forced to be drawn
- fps_timer count is set to the current frame
- waiting for the fps_timer is disabled, the game runs as fast or slow as it is able
if (speed_testing) // draw every frame no matter how fast or slow it is
{
draw_frame = 1;
al_set_timer_count(fps_timer, frame_num);
}
Speed test mode is only used for testing and debugging.
- to see how fast certain hardware can run
- to see how fast certain screen modes and window sizes can perform
- to test how different drawing methods compare
To enable it when the game is running type 'ston', to disable type 'stoff'.
Speed adjust during netgame
During a netgame the server runs at 40 fps and is the master timing source.
The clients all make small adjustments to their speeds to try to maintain the proper timing relation to the server.
The value 'server_lead_frames' is a global int and can be set in the config file, but should be left at its default value of 1.
This means that the clients try to stay 1 frame behind the server.
The packet 'sdat' (server_data) is sent from the server to a client when the server has data for that client,
or if there is no data, an empty 'sdat' is sent periodically for sync purposes.
The 'sdat' packet is time stamped with the frame_num of the server.
The client compares this to its own frame_num and makes speed adjustments like this:
if(PacketRead("sdat"))
{
int sdat_frame_num = PacketGet4ByteInt();
players1[p].server_sync = sdat_frame_num - frame_num;
int fps_chase = frame_speed + players1[p].server_sync - server_lead_frames;
al_set_timer_speed(fps_timer, ( 1 / (float) fps_chase));
}