I got an assignment in my class to develop a simple game in C
with the Allegro5
library.
In class we developed tetris together. The approach was to implement a while(true)
loop within our main.c
with a switch case
to listen for events. We registered a timer
for a our refresh_rate
to update and redraw the game like so:
#define WIDTH 1440
#define HEIGHT 810
#define REFRESH_RATE (1.0 / 60)
#define OBSTACLE_CREATE_RATE (1.0)
void init_assets(void) {
timer = assert_not_null(al_create_timer(REFRESH_RATE), "timer");
queue = assert_not_null(al_create_event_queue(), "event queue");
disp = assert_not_null(al_create_display(WIDTH, HEIGHT), "display");
font = assert_not_null(al_create_builtin_font(), "built-in font");
}
static void main_event_loop(void) {
ALLEGRO_EVENT event;
al_start_timer(timer);
while (true) {
al_wait_for_event(queue, &event);
switch(event.type) {
case ALLEGRO_EVENT_TIMER:
game_update(event.timer);
redraw = true;
break;
case ALLEGRO_EVENT_MOUSE_AXES: mouse_move(event.mouse); break;
case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN: mouse_down(event.mouse); break;
case ALLEGRO_EVENT_MOUSE_BUTTON_UP: mouse_up(event.mouse); break;
case ALLEGRO_EVENT_KEY_DOWN: key_down(event.keyboard); break;
case ALLEGRO_EVENT_KEY_UP: key_up(event.keyboard); break;
case ALLEGRO_EVENT_KEY_CHAR: key_char(event.keyboard); break;
case ALLEGRO_EVENT_DISPLAY_CLOSE: done = true; break;
}
if (done) break;
if (redraw && al_is_event_queue_empty(queue)) {
game_redraw();
redraw = false;
}
}
}
I wanted to implement Flappy Bird
and I am really struggling with either Input Lag or Stuttering because my event queue gets filled up:
The following produces stuttering because the queue is not always empty in 1/60 of a second:
if (redraw && al_is_event_queue_empty(queue)) {
game_redraw();
redraw = false;
}
Changing it like so makes the game unresponsive because input is not processed in time because an older event is still being processed and clogs up the queue.
if (redraw) {
game_redraw();
redraw = false;
}
I suspect the redrawing of my display is what clogs up the queue. I do not do a lot of calculation; only that the 1 bird drops and a maximum of 10 obstacles are beeing shifted to the left. I have not even implemented any sort of collision detection. The redrawing is done by a bunch of al_draw_filled_rectangle(x, y, x + BLOCK_WIDTH, y + BLOCK_HEIGHT, color);
calls.
In hindsight it would have been better to implement the bird and obstacles as images and built a hitbox around them but what I do at the moment is basically split the window (1440 * 810) in rows and columns and draw 5 x 5 rectangles like so:
void game_redraw(void) {
al_clear_to_color(BACKGROUND);
bird_draw();
queue_obstacles_draw(obstacles);
al_flip_display();
}
So the function al_draw_filled_rectangle(x, y, x + BLOCK_WIDTH, y + BLOCK_HEIGHT, color);
gets called approximately 200 times for the bird and a maximum of 10 * 3000 = 30.000 times for the obstacles 60 times a second. So this might be too much for my PC to handle, but when inspecting the resources via Task Manager the GPU is at roughly 40% and the CPU at 65% (in Visual Studio it sits at 40-45%).
My GPU: NVIDIA GeForce GTX 1060 6GB
My CPU: Intel Core i5-4460 CPU@ 3.20 GHz
Now the assignment is due within the next week and I will most likely submit it with stuttering but out of curiosity I would love to know what I am doing wrong here.
Am I drawing my display incorrectly?
Are there ways to better utilize GPU/CPU?
What is best practise to make the game more fluid?
How does a FPS Shooter like CS:GO accomplish frame rates between 100-200 on my machine while I am struggling to draw a "few" rectangles for a 60 fps framerate?