Tutorials

Tutorials for getting started with programming on Linux

Shoot Astroid

Simple Astroids clone

Published
Liked the article? Share it!

We add in hit detection for the astroids coming in contact with the player bullets. To start off, if a hit is detected we simply toggle the astroid off for debug purposes.

main.c
#include <epoxy/gl.h> #include <epoxy/glx.h> #include <gtk/gtk.h> #include <math.h> #include "DashGL/dashgl.h" static void on_realize(GtkGLArea *area); static void on_render(GtkGLArea *area, GdkGLContext *context); static gboolean on_idle(gpointer data); static gboolean on_keydown(GtkWidget *widget, GdkEventKey *event); static gboolean on_keyup(GtkWidget *widget, GdkEventKey *event); #define NUM_BULLETS 10 #define WIDTH 640.0f #define HEIGHT 480.0f #define NUM_ROCKS 3 GLuint program; GLuint vao; GLint attribute_texcoord, attribute_coord2d; GLint uniform_mytexture, uniform_mvp; struct bullet { vec3 pos; GLfloat rot; gboolean active; int ticks; }; struct { GLuint vbo[2]; GLuint tex[2]; vec3 pos; mat4 mvp; gboolean left; gboolean right; gboolean up; gboolean space; GLfloat rot; GLfloat dx, dy, max; float bullet_speed; int num_ticks; struct bullet bullets[NUM_BULLETS]; } player; struct rock { vec3 pos; float rot; float dx, dy; int size; gboolean active; }; struct { GLuint vbo; GLuint tex; mat4 mvp; float radius; struct rock rocks[NUM_ROCKS]; } astroid; int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *glArea; gtk_init(&argc, &argv); // Initialize Window window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "Astroids Tutorial"); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 640, 480); gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_UTILITY); g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(window, "key-press-event", G_CALLBACK(on_keydown), NULL); g_signal_connect(window, "key-release-event", G_CALLBACK(on_keyup), NULL); // Initialize GTK GL Area glArea = gtk_gl_area_new(); gtk_widget_set_vexpand(glArea, TRUE); gtk_widget_set_hexpand(glArea, TRUE); g_signal_connect(glArea, "realize", G_CALLBACK(on_realize), NULL); g_signal_connect(glArea, "render", G_CALLBACK(on_render), NULL); gtk_container_add(GTK_CONTAINER(window), glArea); // Show widgets gtk_widget_show_all(window); gtk_main(); return 0; } static void on_realize(GtkGLArea *area) { // Debug Message int i; g_print("on realize\n"); gtk_gl_area_make_current(area); if(gtk_gl_area_get_error(area) != NULL) { fprintf(stderr, "Unknown error\n"); return; } const GLubyte *renderer = glGetString(GL_RENDER); const GLubyte *version = glGetString(GL_VERSION); printf("Renderer: %s\n", renderer); printf("OpenGL version supported %s\n", version); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glGenVertexArrays(1, &vao); glBindVertexArray(vao); GLfloat player_vertices[] = { -24.0, -24.0, 0.0, 0.0, -24.0, 24.0, 0.0, 1.0, 24.0, 24.0, 1.0, 1.0, -24.0, -24.0, 0.0, 0.0, 24.0, 24.0, 1.0, 1.0, 24.0, -24.0, 1.0, 0.0 }; glGenBuffers(1, &player.vbo[0]); glBindBuffer(GL_ARRAY_BUFFER, player.vbo[0]); glBufferData( GL_ARRAY_BUFFER, sizeof(player_vertices), player_vertices, GL_STATIC_DRAW ); GLfloat bullet_vertices[] = { -8.0, -8.0, 0.0, 0.0, -8.0, 8.0, 0.0, 1.0, 8.0, 8.0, 1.0, 1.0, -8.0, -8.0, 0.0, 0.0, 8.0, 8.0, 1.0, 1.0, 8.0, -8.0, 1.0, 0.0 }; glGenBuffers(1, &player.vbo[1]); glBindBuffer(GL_ARRAY_BUFFER, player.vbo[1]); glBufferData( GL_ARRAY_BUFFER, sizeof(bullet_vertices), bullet_vertices, GL_STATIC_DRAW ); player.pos[0] = WIDTH / 2.0f; player.pos[1] = HEIGHT / 2.0f; player.pos[2] = 0.0; player.left = FALSE; player.right = FALSE; player.up = FALSE; player.space = FALSE; mat4_translate(player.pos, player.mvp); player.tex[0] = shader_load_texture("sprites/player.png"); player.tex[1] = shader_load_texture("sprites/bullet.png"); player.dx = 0.0f; player.dy = 0.0f; player.max = 8.0f; player.bullet_speed = 6.0f; player.num_ticks = 70; for(i = 0; i WIDTH - 24.0f) { player.bullets[i].pos[0] -= WIDTH; draw_mirror = TRUE; } else if(player.bullets[i].pos[0] <24.0f) { player.bullets[i].pos[0] += WIDTH; draw_mirror = TRUE; } if(player.bullets[i].pos[1] > HEIGHT - 24.0f) { player.bullets[i].pos[1] -= HEIGHT; draw_mirror = TRUE; } else if(player.bullets[i].pos[1] <24.0f) { player.bullets[i].pos[1] += HEIGHT; draw_mirror = TRUE; } if(!draw_mirror) { continue; } mat4_translate(player.bullets[i].pos, pos); mat4_rotate_z(player.bullets[i].rot, rot); mat4_multiply(pos, rot, player.mvp); glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, player.mvp); glDrawArrays(GL_TRIANGLES, 0, 6); draw_mirror = FALSE; } // Draw Player Sprite glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, player.tex[0]); glUniform1i(uniform_mytexture, 0); mat4_translate(player.pos, pos); mat4_rotate_z(player.rot, rot); mat4_multiply(pos, rot, player.mvp); glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, player.mvp); glBindBuffer(GL_ARRAY_BUFFER, player.vbo[0]); glVertexAttribPointer( attribute_coord2d, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, 0 ); glVertexAttribPointer( attribute_texcoord, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)(sizeof(float) * 2) ); glDrawArrays(GL_TRIANGLES, 0, 6); if(player.pos[0] > WIDTH - 24.0f) { player.pos[0] -= WIDTH; draw_mirror = TRUE; } else if(player.pos[0] <24.0f) { player.pos[0] += WIDTH; draw_mirror = TRUE; } if(player.pos[1] > HEIGHT - 24.0f) { player.pos[1] -= HEIGHT; draw_mirror = TRUE; } else if(player.pos[1] <24.0f) { player.pos[1] += HEIGHT; draw_mirror = TRUE; } if(draw_mirror) { draw_mirror = FALSE; mat4_translate(player.pos, pos); mat4_rotate_z(player.rot, rot); mat4_multiply(pos, rot, player.mvp); glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, player.mvp); glDrawArrays(GL_TRIANGLES, 0, 6); } // Draw Astroid Sprites glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, astroid.tex); glUniform1i(uniform_mytexture, 0); for(i = 0; i WIDTH - astroid.radius*scale[0]) { astroid.rocks[i].pos[0] -= WIDTH; draw_mirror = TRUE; } else if(astroid.rocks[i].pos[0] HEIGHT - astroid.radius*scale[0]) { astroid.rocks[i].pos[1] -= HEIGHT; draw_mirror = TRUE; } else if(astroid.rocks[i].pos[1] player.max) { player.dx = player.max; } if(player.dy <-player.max) { player.dy = -player.max; } else if(player.dy > player.max) { player.dy = player.max; } player.pos[0] += player.dx; player.pos[1] += player.dy; for(i = 0; i for(k = 0; k astroid.radius * scale) { continue; } astroid.rocks[k].active = FALSE; player.bullets[i].active = FALSE; break; } } for(i = 0; i keyval) { case GDK_KEY_Left: player.left = TRUE; break; case GDK_KEY_Right: player.right = TRUE; break; case GDK_KEY_Up: player.up = TRUE; break; case GDK_KEY_space: if(!player.space) { int i; for(i = 0; i keyval) { case GDK_KEY_Left: player.left = FALSE; break; case GDK_KEY_Right: player.right = FALSE; break; case GDK_KEY_Up: player.up = FALSE; break; case GDK_KEY_space: player.space = FALSE; break; } }

Compile

$ gcc -c -o DashGL/dashgl.o DashGL/dashgl.c -lepoxy -lpng -lm
$ gcc `pkg-config --cflags gtk+-3.0` main.c DashGL/dashgl.o `pkg-config --libs gtk+-3.0` \
-lepoxy -lm -lpng

Run

$ ./a.out