Tutorials

Tutorials for getting started with programming on Linux

Enemies Shoot Back

Gtk Invaders

Published
Liked the article? Share it!

Next we will add player hit detection, similar to the method we used to detect enemy hits. In this case though, we make the hit detection radius much smaller, so that we only detect a small part of the player’s ship. This adds the effect of “dodging”, or allowing bullets to pass over the wings.

To start, we simply want to debug the conditions when the player is hit, so we can adjust accordingly. We will simply output “player hit detected” to the console when detected.

main.c
#include <stdlib.h> #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); static void detect_player_hit(vec3 point); #define WIDTH 640.0f #define HEIGHT 480.0f GLuint program; GLuint vao; GLint attribute_texcoord, attribute_coord2d; GLint uniform_mytexture, uniform_mvp; struct { GLuint vbo; GLuint tex; mat4 mvp; } background; struct { GLuint vbo; GLuint tex; vec3 pos; mat4 mvp; gboolean left; gboolean right; int num_bullets; vec3 *bullets; gboolean *active; gboolean space; } player; struct { GLuint vbo; GLuint tex; mat4 mvp; GLfloat dy; } bullet; struct { GLuint vbo; GLuint tex; mat4 mvp; vec3 *pos; int num_enemies; float dx, dy; gboolean *active; } enemy; struct { int num_bullets; gboolean *active; vec3 *pos; float dy; GLuint vbo; GLuint tex; mat4 mvp; } bullets; 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), "Invaders 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 g_print("on realize 0 "); gtk_gl_area_make_current(area); if(gtk_gl_area_get_error(area) != NULL) { fprintf(stderr, "Unknown error "); return; } const GLubyte *renderer = glGetString(GL_RENDER); const GLubyte *version = glGetString(GL_VERSION); g_print("Renderer: %s ", renderer); g_print("OpenGL version supported %s ", version); glClearColor(1.0f, 1.0f, 1.0f, 1.0f); glGenVertexArrays(1, &vao); glBindVertexArray(vao); // Background Sprite GLfloat background_vertices[] = { 0.0, 0.0, 0.0, 0.0, 0.0, HEIGHT, 0.0, 1.0, WIDTH, HEIGHT, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, WIDTH, HEIGHT, 1.0, 1.0, WIDTH, 0.0, 1.0, 0.0 }; glGenBuffers(1, &background.vbo); glBindBuffer(GL_ARRAY_BUFFER, background.vbo); glBufferData( GL_ARRAY_BUFFER, sizeof(background_vertices), background_vertices, GL_STATIC_DRAW ); mat4_identity(background.mvp); background.tex = shader_load_texture("sprites/background.png"); // Player Sprite GLfloat player_vertices[] = { -22.0, -22.0, 0.0, 0.0, -22.0, 22.0, 0.0, 1.0, 22.0, 22.0, 1.0, 1.0, -22.0, -22.0, 0.0, 0.0, 22.0, 22.0, 1.0, 1.0, 22.0, -22.0, 1.0, 0.0 }; glGenBuffers(1, &player.vbo); glBindBuffer(GL_ARRAY_BUFFER, player.vbo); glBufferData( GL_ARRAY_BUFFER, sizeof(player_vertices), player_vertices, GL_STATIC_DRAW ); player.pos[0] = WIDTH / 2; player.pos[1] = 30.0f; player.pos[2] = 0.0; player.left = FALSE; player.right = FALSE; mat4_translate(player.pos, player.mvp); player.tex = shader_load_texture("sprites/player.png"); player.space = FALSE; player.num_bullets = 3; player.bullets = malloc(player.num_bullets * sizeof(vec3)); player.active = malloc(player.num_bullets * sizeof(gboolean)); int i; for(i = 0; i WIDTH) { player.pos[0] = WIDTH; } int i, k; for(i = 0; i detect_player_hit(enemy.pos[i]); if(enemy.pos[i][0] > WIDTH || enemy.pos[i][0] <0) { do_switch = TRUE; } if(rand() % 5000 <4950) { continue; } for(k = 0; k 32.0f) { continue; } enemy.dx *= 1.05; player.active[i] = FALSE; enemy.active[k] = FALSE; break; } } for(i = 0; i detect_player_hit(bullets.pos[i]); if(bullets.pos[i][1] <-20.0f) { bullets.active[i] = FALSE; } } gtk_widget_queue_draw(GTK_WIDGET(data)); return TRUE; } static gboolean on_keydown(GtkWidget *widget, GdkEventKey *event) { switch(event->keyval) { case GDK_KEY_Left: player.left = TRUE; break; case GDK_KEY_Right: player.right = 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_space: player.space = FALSE; break; } } static void detect_player_hit(vec3 point) { float x_dif, y_dif; x_dif = player.pos[0] - point[0]; y_dif = player.pos[1] - point[1]; if(hypotf(x_dif, y_dif) > 10.0f) { return; } g_print("player hit detected!! "); }

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