mirror of
https://github.com/kuhyx/testsAndMisc-archive.git
synced 2026-07-04 15:23:06 +02:00
feat: make the game have any sense at al
This commit is contained in:
parent
372326f8ac
commit
74120f2cde
177
C/fps/main.c
177
C/fps/main.c
@ -1,7 +1,8 @@
|
|||||||
// Simple FPS demo using FreeGLUT + legacy OpenGL (compat profile)
|
// Simple FPS demo using FreeGLUT + legacy OpenGL (compat profile)
|
||||||
// - Move: WASD, Shift to sprint, Space to shoot, Esc to quit
|
// - Move: WASD, Shift to sprint, Space to shoot, Esc to quit
|
||||||
// - Look: mouse
|
// - Look: mouse
|
||||||
// - Target: red cube; shoot to score and respawn target
|
// - Targets: red cubes move toward you; shoot them before they reach you
|
||||||
|
// - Game over when a target reaches you; final score shown; press R to restart
|
||||||
|
|
||||||
#include <GL/glut.h>
|
#include <GL/glut.h>
|
||||||
#include <GL/glu.h>
|
#include <GL/glu.h>
|
||||||
@ -10,6 +11,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#ifndef M_PI
|
#ifndef M_PI
|
||||||
#define M_PI 3.14159265358979323846
|
#define M_PI 3.14159265358979323846
|
||||||
@ -47,10 +49,17 @@ static float g_move_speed = 4.0f; // m/s
|
|||||||
static float g_sprint_mul = 1.8f;
|
static float g_sprint_mul = 1.8f;
|
||||||
static float g_mouse_sens = 0.12f; // deg per pixel
|
static float g_mouse_sens = 0.12f; // deg per pixel
|
||||||
|
|
||||||
typedef struct { vec3 pos; float radius; } Target;
|
typedef struct { vec3 pos; float radius; float speed; } Target;
|
||||||
static Target g_target;
|
static const int MAX_TARGETS = 128;
|
||||||
|
static Target g_targets[128];
|
||||||
|
static int g_target_count = 0;
|
||||||
|
static float g_spawn_timer = 0.0f;
|
||||||
|
static float g_spawn_interval = 1.2f; // seconds
|
||||||
static int g_score = 0;
|
static int g_score = 0;
|
||||||
|
|
||||||
|
typedef enum { GAME_RUNNING = 0, GAME_OVER = 1 } GameState;
|
||||||
|
static GameState g_state = GAME_RUNNING;
|
||||||
|
|
||||||
// Bullet visualization
|
// Bullet visualization
|
||||||
static float g_bullet_t = 0.0f; // seconds left to show bullet line
|
static float g_bullet_t = 0.0f; // seconds left to show bullet line
|
||||||
static vec3 g_bullet_origin, g_bullet_dir;
|
static vec3 g_bullet_origin, g_bullet_dir;
|
||||||
@ -75,12 +84,36 @@ static vec3 cam_right()
|
|||||||
return v3_norm(v3_cross(cam_front(), v3(0,1,0)));
|
return v3_norm(v3_cross(cam_front(), v3(0,1,0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void respawn_target()
|
static float frand(float a, float b) { return a + (b-a) * (rand()/(float)RAND_MAX); }
|
||||||
|
|
||||||
|
static void clear_targets() { g_target_count = 0; }
|
||||||
|
|
||||||
|
static void spawn_target()
|
||||||
{
|
{
|
||||||
float x = ((rand()/(float)RAND_MAX) * 24.0f) - 12.0f; // [-12,12]
|
if (g_target_count >= MAX_TARGETS) return;
|
||||||
float z = ((rand()/(float)RAND_MAX) * 24.0f) - 12.0f; // [-12,12]
|
float radius_spawn = frand(22.0f, 34.0f);
|
||||||
g_target.pos = v3(x, 0.5f, z);
|
float ang = frand(0.0f, 2.0f*(float)M_PI);
|
||||||
g_target.radius = 0.6f; // fits a 1.0 cube roughly
|
vec3 p = v3(cosf(ang)*radius_spawn, 0.5f, sinf(ang)*radius_spawn);
|
||||||
|
Target t;
|
||||||
|
t.pos = p;
|
||||||
|
t.radius = 0.6f;
|
||||||
|
t.speed = frand(1.4f, 3.2f);
|
||||||
|
g_targets[g_target_count++] = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reset_game()
|
||||||
|
{
|
||||||
|
g_score = 0;
|
||||||
|
g_spawn_timer = 0.0f;
|
||||||
|
g_spawn_interval = 1.2f;
|
||||||
|
clear_targets();
|
||||||
|
// spawn a few to start
|
||||||
|
for (int i = 0; i < 4; ++i) spawn_target();
|
||||||
|
g_state = GAME_RUNNING;
|
||||||
|
if (g_captured_mouse) {
|
||||||
|
g_ignore_next_passive = true;
|
||||||
|
glutWarpPointer(g_win_w/2, g_win_h/2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ray-sphere intersection: returns t >= 0 for first hit, or -1 if miss
|
// Ray-sphere intersection: returns t >= 0 for first hit, or -1 if miss
|
||||||
@ -97,18 +130,23 @@ static float ray_sphere(vec3 ro, vec3 rd, vec3 c, float r)
|
|||||||
|
|
||||||
static void shoot()
|
static void shoot()
|
||||||
{
|
{
|
||||||
|
if (g_state != GAME_RUNNING) return;
|
||||||
vec3 dir = cam_front();
|
vec3 dir = cam_front();
|
||||||
float t = ray_sphere(g_cam_pos, dir, g_target.pos, g_target.radius);
|
float best_t = 1e9f;
|
||||||
|
int best_i = -1;
|
||||||
|
for (int i = 0; i < g_target_count; ++i) {
|
||||||
|
float t = ray_sphere(g_cam_pos, dir, g_targets[i].pos, g_targets[i].radius);
|
||||||
|
if (t >= 0.0f && t < best_t) { best_t = t; best_i = i; }
|
||||||
|
}
|
||||||
g_bullet_origin = g_cam_pos;
|
g_bullet_origin = g_cam_pos;
|
||||||
g_bullet_dir = dir;
|
g_bullet_dir = dir;
|
||||||
g_bullet_t = 0.08f; // show for ~80ms
|
g_bullet_t = 0.08f; // show for ~80ms
|
||||||
|
|
||||||
if (t >= 0.0f && t < 100.0f) {
|
if (best_i >= 0) {
|
||||||
|
// remove hit target (swap remove)
|
||||||
|
g_targets[best_i] = g_targets[g_target_count-1];
|
||||||
|
g_target_count--;
|
||||||
g_score++;
|
g_score++;
|
||||||
char title[128];
|
|
||||||
snprintf(title, sizeof(title), "FPS Demo | Score: %d", g_score);
|
|
||||||
glutSetWindowTitle(title);
|
|
||||||
respawn_target();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,11 +221,13 @@ static void display()
|
|||||||
// Ground grid
|
// Ground grid
|
||||||
draw_grid(40.0f, 1.0f);
|
draw_grid(40.0f, 1.0f);
|
||||||
|
|
||||||
// Target cube
|
// Target cubes
|
||||||
glPushMatrix();
|
for (int i = 0; i < g_target_count; ++i) {
|
||||||
glTranslatef(g_target.pos.x, g_target.pos.y, g_target.pos.z);
|
glPushMatrix();
|
||||||
draw_cube();
|
glTranslatef(g_targets[i].pos.x, g_targets[i].pos.y, g_targets[i].pos.z);
|
||||||
glPopMatrix();
|
draw_cube();
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
// Bullet line
|
// Bullet line
|
||||||
if (g_bullet_t > 0.0f) {
|
if (g_bullet_t > 0.0f) {
|
||||||
@ -200,8 +240,42 @@ static void display()
|
|||||||
glEnd();
|
glEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crosshair overlay
|
// Crosshair overlay (only during gameplay)
|
||||||
draw_crosshair();
|
if (g_state == GAME_RUNNING) {
|
||||||
|
draw_crosshair();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Game over overlay text
|
||||||
|
if (g_state == GAME_OVER) {
|
||||||
|
// Switch to 2D for text
|
||||||
|
glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity();
|
||||||
|
gluOrtho2D(0, g_win_w, g_win_h, 0);
|
||||||
|
glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity();
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
const char* line1 = "GAME OVER";
|
||||||
|
char line2[64]; snprintf(line2, sizeof(line2), "Score: %d", g_score);
|
||||||
|
const char* line3 = "Press R to restart or Esc to quit";
|
||||||
|
|
||||||
|
void* font = GLUT_BITMAP_HELVETICA_18;
|
||||||
|
int cx = g_win_w/2; int cy = g_win_h/2;
|
||||||
|
glColor3f(1,1,1);
|
||||||
|
// naive center: estimate width by char count * 9 px
|
||||||
|
int w1 = (int)(9 * strlen(line1));
|
||||||
|
int w2 = (int)(9 * strlen(line2));
|
||||||
|
int w3 = (int)(9 * strlen(line3));
|
||||||
|
|
||||||
|
glRasterPos2i(cx - w1/2, cy - 30);
|
||||||
|
for (const char* p=line1; *p; ++p) glutBitmapCharacter(font, *p);
|
||||||
|
glRasterPos2i(cx - w2/2, cy - 8);
|
||||||
|
for (const char* p=line2; *p; ++p) glutBitmapCharacter(font, *p);
|
||||||
|
glRasterPos2i(cx - w3/2, cy + 18);
|
||||||
|
for (const char* p=line3; *p; ++p) glutBitmapCharacter(font, *p);
|
||||||
|
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glMatrixMode(GL_MODELVIEW); glPopMatrix();
|
||||||
|
glMatrixMode(GL_PROJECTION); glPopMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
glutSwapBuffers();
|
glutSwapBuffers();
|
||||||
}
|
}
|
||||||
@ -209,17 +283,44 @@ static void display()
|
|||||||
// -------- Update/Input --------
|
// -------- Update/Input --------
|
||||||
static void update(float dt)
|
static void update(float dt)
|
||||||
{
|
{
|
||||||
float speed = g_move_speed * ((g_keys['\t'] || g_keys['Q'] || g_keys['q']) ? g_sprint_mul : 1.0f); // Tab/Q to sprint
|
if (g_state == GAME_RUNNING) {
|
||||||
vec3 f = cam_front(); f.y = 0.0f; f = v3_norm(f);
|
float speed = g_move_speed * ((g_keys['\t'] || g_keys['Q'] || g_keys['q']) ? g_sprint_mul : 1.0f); // Tab/Q to sprint
|
||||||
vec3 r = cam_right(); r.y = 0.0f; r = v3_norm(r);
|
vec3 f = cam_front(); f.y = 0.0f; f = v3_norm(f);
|
||||||
|
vec3 r = cam_right(); r.y = 0.0f; r = v3_norm(r);
|
||||||
|
|
||||||
if (g_keys['W'] || g_keys['w']) g_cam_pos = v3_add(g_cam_pos, v3_scale(f, speed*dt));
|
if (g_keys['W'] || g_keys['w']) g_cam_pos = v3_add(g_cam_pos, v3_scale(f, speed*dt));
|
||||||
if (g_keys['S'] || g_keys['s']) g_cam_pos = v3_sub(g_cam_pos, v3_scale(f, speed*dt));
|
if (g_keys['S'] || g_keys['s']) g_cam_pos = v3_sub(g_cam_pos, v3_scale(f, speed*dt));
|
||||||
if (g_keys['A'] || g_keys['a']) g_cam_pos = v3_sub(g_cam_pos, v3_scale(r, speed*dt));
|
if (g_keys['A'] || g_keys['a']) g_cam_pos = v3_sub(g_cam_pos, v3_scale(r, speed*dt));
|
||||||
if (g_keys['D'] || g_keys['d']) g_cam_pos = v3_add(g_cam_pos, v3_scale(r, speed*dt));
|
if (g_keys['D'] || g_keys['d']) g_cam_pos = v3_add(g_cam_pos, v3_scale(r, speed*dt));
|
||||||
|
|
||||||
// Keep feet on ground
|
// Keep feet on ground
|
||||||
g_cam_pos.y = 1.6f;
|
g_cam_pos.y = 1.6f;
|
||||||
|
|
||||||
|
// Move targets toward player and check collision
|
||||||
|
for (int i = 0; i < g_target_count; ++i) {
|
||||||
|
vec3 to_player = v3_sub(g_cam_pos, g_targets[i].pos);
|
||||||
|
to_player.y = 0.0f;
|
||||||
|
vec3 dir = v3_norm(to_player);
|
||||||
|
g_targets[i].pos = v3_add(g_targets[i].pos, v3_scale(dir, g_targets[i].speed * dt));
|
||||||
|
g_targets[i].pos.y = 0.5f;
|
||||||
|
|
||||||
|
float dist2 = to_player.x*to_player.x + to_player.z*to_player.z;
|
||||||
|
float reach = (0.6f + g_targets[i].radius);
|
||||||
|
if (dist2 <= reach*reach) {
|
||||||
|
g_state = GAME_OVER;
|
||||||
|
glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn new targets over time (slightly accelerate spawn rate)
|
||||||
|
g_spawn_timer += dt;
|
||||||
|
if (g_spawn_timer >= g_spawn_interval) {
|
||||||
|
g_spawn_timer = 0.0f;
|
||||||
|
spawn_target();
|
||||||
|
g_spawn_interval = fmaxf(0.4f, g_spawn_interval * 0.98f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (g_bullet_t > 0.0f) {
|
if (g_bullet_t > 0.0f) {
|
||||||
g_bullet_t -= dt;
|
g_bullet_t -= dt;
|
||||||
@ -248,7 +349,7 @@ static void reshape(int w, int h)
|
|||||||
gluPerspective(75.0, (double)g_win_w/(double)g_win_h, 0.05, 500.0);
|
gluPerspective(75.0, (double)g_win_w/(double)g_win_h, 0.05, 500.0);
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
|
||||||
if (g_captured_mouse) {
|
if (g_captured_mouse && g_state == GAME_RUNNING) {
|
||||||
g_ignore_next_passive = true;
|
g_ignore_next_passive = true;
|
||||||
glutWarpPointer(g_win_w/2, g_win_h/2);
|
glutWarpPointer(g_win_w/2, g_win_h/2);
|
||||||
}
|
}
|
||||||
@ -265,10 +366,15 @@ static void keyboard_down(unsigned char key, int x, int y)
|
|||||||
} else if (key == 'm' || key == 'M') {
|
} else if (key == 'm' || key == 'M') {
|
||||||
g_captured_mouse = !g_captured_mouse;
|
g_captured_mouse = !g_captured_mouse;
|
||||||
glutSetCursor(g_captured_mouse ? GLUT_CURSOR_NONE : GLUT_CURSOR_LEFT_ARROW);
|
glutSetCursor(g_captured_mouse ? GLUT_CURSOR_NONE : GLUT_CURSOR_LEFT_ARROW);
|
||||||
if (g_captured_mouse) {
|
if (g_captured_mouse && g_state == GAME_RUNNING) {
|
||||||
g_ignore_next_passive = true;
|
g_ignore_next_passive = true;
|
||||||
glutWarpPointer(g_win_w/2, g_win_h/2);
|
glutWarpPointer(g_win_w/2, g_win_h/2);
|
||||||
}
|
}
|
||||||
|
} else if (key == 'r' || key == 'R') {
|
||||||
|
if (g_state == GAME_OVER) {
|
||||||
|
glutSetCursor(g_captured_mouse ? GLUT_CURSOR_NONE : GLUT_CURSOR_LEFT_ARROW);
|
||||||
|
reset_game();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,7 +394,7 @@ static void mouse_button(int button, int state, int x, int y)
|
|||||||
|
|
||||||
static void passive_motion(int x, int y)
|
static void passive_motion(int x, int y)
|
||||||
{
|
{
|
||||||
if (!g_captured_mouse) return;
|
if (!g_captured_mouse || g_state != GAME_RUNNING) return;
|
||||||
|
|
||||||
if (g_ignore_next_passive) { // ignore event caused by warp
|
if (g_ignore_next_passive) { // ignore event caused by warp
|
||||||
g_ignore_next_passive = false;
|
g_ignore_next_passive = false;
|
||||||
@ -320,12 +426,11 @@ int main(int argc, char** argv)
|
|||||||
{
|
{
|
||||||
(void)argv;
|
(void)argv;
|
||||||
srand((unsigned)time(NULL));
|
srand((unsigned)time(NULL));
|
||||||
respawn_target();
|
|
||||||
|
|
||||||
glutInit(&argc, argv);
|
glutInit(&argc, argv);
|
||||||
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
|
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
|
||||||
glutInitWindowSize(g_win_w, g_win_h);
|
glutInitWindowSize(g_win_w, g_win_h);
|
||||||
glutCreateWindow("FPS Demo | Score: 0");
|
glutCreateWindow("FPS Demo");
|
||||||
|
|
||||||
init_gl();
|
init_gl();
|
||||||
|
|
||||||
@ -342,6 +447,8 @@ int main(int argc, char** argv)
|
|||||||
g_ignore_next_passive = true;
|
g_ignore_next_passive = true;
|
||||||
glutWarpPointer(g_win_w/2, g_win_h/2);
|
glutWarpPointer(g_win_w/2, g_win_h/2);
|
||||||
|
|
||||||
|
reset_game();
|
||||||
|
|
||||||
glutMainLoop();
|
glutMainLoop();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user