Skip to content

Instantly share code, notes, and snippets.

@Lightnet
Last active April 25, 2025 17:37
Show Gist options
  • Save Lightnet/43928aa3957280add0f25873d5bc26ea to your computer and use it in GitHub Desktop.
Save Lightnet/43928aa3957280add0f25873d5bc26ea to your computer and use it in GitHub Desktop.
refine physics collision but wall lag too many collisions. raylib.
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define NOGDI
#define NOUSER
#define MMNOSOUND
#include "raylib.h"
#include "raymath.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
// Terrain generation parameters
int t_width = 8;
int t_height = 8;
float t_scale = 30.0f;
float t_magnitude = 0.5f;
float t_offset = 0.02f;
float gravity = 8.0f; // Feet/s²
// Physics simulation parameters
// float simulationRate = 250.0f;
// float stepSize = 1.0f / 250.0f; // 0.004s
// float simulationRate = 60.0f;
// float stepSize = 1.0f / 60.0f;
// Simple pseudo-noise function
float simple_noise(float x, float z) {
int xi = (int)(x * 1000.0f);
int zi = (int)(z * 1000.0f);
int n = xi * 57 + zi * 131;
n = (n << 13) ^ n;
return (1.0f - ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f) * 0.5f;
}
typedef struct {
Vector3 size;
Vector3 position;
Color color;
} ObjBlock;
typedef struct {
Vector3 size;
float radius;
float height;
Vector3 position;
Color color;
float maxSpeed;
Vector3 input;
Vector3 velocity;
float jumpGround;
bool onGround;
} ObjPlayer;
typedef struct {
ObjBlock* blocks;
int count;
int capacity;
} ObjWorld;
typedef struct {
ObjBlock** blocks;
int count;
int capacity;
} Candidates;
typedef struct {
ObjBlock** blocks;
int count;
int capacity;
} DebugCubes;
typedef struct {
ObjBlock* block;
Vector3 contactPoint;
Vector3 normal;
float overlap;
bool onGround;
} Collision;
typedef struct {
Collision* collisions;
int count;
int capacity;
} Collisions;
typedef struct {
Collision* collisions;
int count;
int capacity;
} DebugCollisions;
// Initialize dynamic array
void init_dynamic_array(void** array, int* count, int* capacity, int initial_capacity, size_t element_size) {
*array = malloc(initial_capacity * element_size);
if (!*array) {
fprintf(stderr, "Failed to allocate memory\n");
exit(1);
}
*count = 0;
*capacity = initial_capacity;
}
// Free dynamic array
void free_dynamic_array(void** array, int* count, int* capacity) {
free(*array);
*array = NULL;
*count = 0;
*capacity = 0;
}
// Append to dynamic array
void append_to_dynamic_array(void** array, int* count, int* capacity, void* element, size_t element_size) {
if (*count >= *capacity) {
int new_capacity = *capacity == 0 ? 4 : *capacity * 2;
void* new_array = realloc(*array, new_capacity * element_size);
if (!new_array) {
fprintf(stderr, "Failed to reallocate memory\n");
exit(1);
}
*array = new_array;
*capacity = new_capacity;
}
memcpy((char*)*array + (*count) * element_size, element, element_size);
(*count)++;
}
// Initialize world
void init_world(ObjWorld* world, int initial_capacity) {
init_dynamic_array((void**)&world->blocks, &world->count, &world->capacity, initial_capacity, sizeof(ObjBlock));
}
// Free world
void free_world(ObjWorld* world) {
free_dynamic_array((void**)&world->blocks, &world->count, &world->capacity);
}
// Append block
void append_block(ObjWorld* world, ObjBlock block) {
append_to_dynamic_array((void**)&world->blocks, &world->count, &world->capacity, &block, sizeof(ObjBlock));
}
// Generate world
void generate_world(ObjWorld* world, int size) {
init_world(world, size * size + 4 * size * 3); // Account for ground + walls
// Generate flat terrain at y=1.0
for (int x = 0; x < size; x++) {
for (int z = 0; z < size; z++) {
float height = 1.0f; // Fixed ground height
Color color = (Color){100, 150, 50, 255}; // Greenish ground
ObjBlock block = {
.size = {1.0f, 1.0f, 1.0f},
.position = {(float)x, height, (float)z},
.color = color
};
append_block(world, block);
}
}
// Generate walls along edges (x=0, x=size-1, z=0, z=size-1)
int wall_height = 3; // 3 blocks high
Color wall_color = (Color){128, 128, 128, 255}; // Gray walls
// Walls at x=0 and x=size-1
for (int z = 0; z < size; z++) {
for (int y = 1; y <= wall_height; y++) {
// Wall at x=0
ObjBlock block1 = {
.size = {1.0f, 1.0f, 1.0f},
.position = {0.0f, (float)y, (float)z},
.color = wall_color
};
append_block(world, block1);
// Wall at x=size-1
ObjBlock block2 = {
.size = {1.0f, 1.0f, 1.0f},
.position = {(float)(size - 1), (float)y, (float)z},
.color = wall_color
};
append_block(world, block2);
}
}
// Walls at z=0 and z=size-1 (excluding corners to avoid duplicates)
for (int x = 1; x < size - 1; x++) {
for (int y = 1; y <= wall_height; y++) {
// Wall at z=0
ObjBlock block1 = {
.size = {1.0f, 1.0f, 1.0f},
.position = {(float)x, (float)y, 0.0f},
.color = wall_color
};
append_block(world, block1);
// Wall at z=size-1
ObjBlock block2 = {
.size = {1.0f, 1.0f, 1.0f},
.position = {(float)x, (float)y, (float)(size - 1)},
.color = wall_color
};
append_block(world, block2);
}
}
}
// Get block at (x, y, z) with range check for Y
ObjBlock* get_block(ObjWorld* world, int x, float y, int z) {
ObjBlock* highest_block = NULL;
float highest_top = -FLT_MAX;
for (int i = 0; i < world->count; i++) {
ObjBlock* block = &world->blocks[i];
float block_x = block->position.x;
float block_z = block->position.z;
if (fabsf(block_x - (float)x) <= 0.5f && fabsf(block_z - (float)z) <= 0.5f) {
float block_top = block->position.y + block->size.y * 0.5f;
if (block_top <= y + 0.5f && block_top > highest_top) { // Relaxed y check
highest_top = block_top;
highest_block = block;
}
}
}
if (highest_block) {
printf("get_block: Found block at (%.1f, %.1f, %.1f), Top: %.1f, Queried y: %.2f, x=%d, z=%d\n",
highest_block->position.x, highest_block->position.y, highest_block->position.z, highest_top, y, x, z);
} else {
printf("get_block: No block found at x=%d, z=%d, y=%.2f\n", x, z, y);
}
return highest_block;
}
// Initialize candidates
void init_candidates(Candidates* candidates, int initial_capacity) {
init_dynamic_array((void**)&candidates->blocks, &candidates->count, &candidates->capacity, initial_capacity, sizeof(ObjBlock*));
}
// Free candidates
void free_candidates(Candidates* candidates) {
free_dynamic_array((void**)&candidates->blocks, &candidates->count, &candidates->capacity);
}
// Append candidate
void append_candidate(Candidates* candidates, ObjBlock* block) {
append_to_dynamic_array((void**)&candidates->blocks, &candidates->count, &candidates->capacity, &block, sizeof(ObjBlock*));
}
// Initialize debug cubes
void init_debug_cubes(DebugCubes* debug_cubes, int initial_capacity) {
init_dynamic_array((void**)&debug_cubes->blocks, &debug_cubes->count, &debug_cubes->capacity, initial_capacity, sizeof(ObjBlock*));
}
// Free debug cubes
void free_debug_cubes(DebugCubes* debug_cubes) {
free_dynamic_array((void**)&debug_cubes->blocks, &debug_cubes->count, &debug_cubes->capacity);
}
// Append debug cube
void append_debug_cube(DebugCubes* debug_cubes, ObjBlock* block) {
append_to_dynamic_array((void**)&debug_cubes->blocks, &debug_cubes->count, &debug_cubes->capacity, &block, sizeof(ObjBlock*));
}
// Initialize collisions
void init_collisions(Collisions* collisions, int initial_capacity) {
init_dynamic_array((void**)&collisions->collisions, &collisions->count, &collisions->capacity, initial_capacity, sizeof(Collision));
}
// Free collisions
void free_collisions(Collisions* collisions) {
free_dynamic_array((void**)&collisions->collisions, &collisions->count, &collisions->capacity);
}
// Append collision
void append_collision(Collisions* collisions, Collision collision) {
append_to_dynamic_array((void**)&collisions->collisions, &collisions->count, &collisions->capacity, &collision, sizeof(Collision));
}
// Initialize debug collisions
void init_debug_collisions(DebugCollisions* debug_collisions, int initial_capacity) {
init_dynamic_array((void**)&debug_collisions->collisions, &debug_collisions->count, &debug_collisions->capacity, initial_capacity, sizeof(Collision));
}
// Free debug collisions
void free_debug_collisions(DebugCollisions* debug_collisions) {
free_dynamic_array((void**)&debug_collisions->collisions, &debug_collisions->count, &debug_collisions->capacity);
}
// Append debug collision
void append_debug_collision(DebugCollisions* debug_collisions, Collision collision) {
append_to_dynamic_array((void**)&debug_collisions->collisions, &debug_collisions->count, &debug_collisions->capacity, &collision, sizeof(Collision));
}
// Check if point is in player's bounding cylinder
bool point_in_player_bounding_cylinder(Vector3 point, ObjPlayer* player) {
float cylinder_center_y = player->position.y - player->height / 2.0f;
float dx = point.x - player->position.x;
float dy = point.y - cylinder_center_y;
float dz = point.z - player->position.z;
float r_sq = dx * dx + dz * dz;
return fabsf(dy) < player->height / 2.0f && r_sq < player->radius * player->radius;
}
// Compare function for sorting collisions by overlap
int compare_collisions(const void* a, const void* b) {
const Collision* ca = (const Collision*)a;
const Collision* cb = (const Collision*)b;
return (ca->overlap < cb->overlap) ? -1 : (ca->overlap > cb->overlap) ? 1 : 0;
}
// Helper function to project velocity for sliding
Vector3 slide_velocity(Vector3 velocity, Vector3 normal) {
float dot = Vector3DotProduct(velocity, normal);
if (dot < 0.0f) {
Vector3 normal_component = Vector3Scale(normal, dot);
return Vector3Subtract(velocity, normal_component);
}
return velocity;
}
// Resolve collisions
void resolve_collisions(ObjWorld* world, Collisions* collisions, ObjPlayer* player) {
if (collisions->count > 1) {
qsort(collisions->collisions, collisions->count, sizeof(Collision), compare_collisions);
}
bool ground_handled = false;
// First pass: Handle ground collision
for (int i = 0; i < collisions->count; i++) {
Collision* collision = &collisions->collisions[i];
if (!point_in_player_bounding_cylinder(collision->contactPoint, player)) {
continue;
}
if (collision->overlap < 0.01f || Vector3Length(collision->normal) < 0.1f) {
continue;
}
// Handle ground collision
if (collision->onGround && collision->normal.y > 0.5f && !ground_handled) {
player->onGround = true;
player->velocity.y = 0.0f;
float block_top = collision->block->position.y + (collision->block->size.y * 0.5f);
float target_y = block_top + (player->height * 0.5f);
if (player->position.y < target_y) {
player->position.y = target_y;
}
ground_handled = true;
printf("Resolved ground: Block at (%.1f, %.1f, %.1f), Player y=%.2f, Overlap: %.2f\n",
collision->block->position.x, collision->block->position.y, collision->block->position.z,
player->position.y, collision->overlap);
continue;
}
// Handle wall collision
if (!collision->onGround && collision->overlap >= 0.01f) {
Vector3 delta_position = Vector3Scale(collision->normal, collision->overlap);
player->position = Vector3Add(player->position, delta_position);
float dot = Vector3DotProduct(player->velocity, collision->normal);
if (dot < -0.01f) {
player->velocity = slide_velocity(player->velocity, collision->normal);
if (fabsf(collision->normal.x) > 0.5f) {
player->velocity.x = 0.0f;
}
if (fabsf(collision->normal.z) > 0.5f) {
player->velocity.z = 0.0f;
}
}
printf("Wall collision: Normal (%.1f, %.1f, %.1f), Overlap: %.2f, Vel: (%.2f, %.2f, %.2f), Pos: (%.2f, %.2f, %.2f)\n",
collision->normal.x, collision->normal.y, collision->normal.z,
collision->overlap,
player->velocity.x, player->velocity.y, player->velocity.z,
player->position.x, player->position.y, player->position.z);
}
}
// Fallback: Check for ground proximity if no ground collision
if (!ground_handled) {
float player_bottom = player->position.y - (player->height * 0.5f);
ObjBlock* block_below = get_block(world, (int)player->position.x, player_bottom, (int)player->position.z); // Fixed: pass world
if (block_below) {
float block_top = block_below->position.y + (block_below->size.y * 0.5f);
if (fabsf(player_bottom - block_top) < 0.2f && player->velocity.y <= 0.0f) {
player->onGround = true;
player->velocity.y = 0.0f;
player->position.y = block_top + (player->height * 0.5f);
printf("Fallback ground correction: Block at (%.1f, %.1f, %.1f), Player y=%.2f\n",
block_below->position.x, block_below->position.y, block_below->position.z,
player->position.y);
} else if (player->velocity.y > 0.0f || player->position.y > block_top + 0.2f) {
player->onGround = false;
printf("Reset onGround: Vel.y=%.2f, Pos.y=%.2f, Block top=%.2f\n",
player->velocity.y, player->position.y, block_top);
}
} else {
printf("Fallback: No block below at x=%d, z=%d, player_bottom=%.2f\n",
(int)player->position.x, (int)player->position.z, player_bottom);
}
}
printf("Post-resolve: Pos (%.2f, %.2f, %.2f), Vel (%.2f, %.2f, %.2f), onGround=%d\n",
player->position.x, player->position.y, player->position.z,
player->velocity.x, player->velocity.y, player->velocity.z, player->onGround);
}
// Narrow-phase collision detection
Collisions narrow_phase(Candidates* candidates, ObjPlayer* player) {
Collisions collisions;
init_collisions(&collisions, candidates->count ? candidates->count : 1);
for (int i = 0; i < candidates->count; i++) {
ObjBlock* block = candidates->blocks[i];
Vector3 cylinder_center = {player->position.x, player->position.y - player->height / 2.0f, player->position.z};
Vector3 closest_point = {
fmaxf(block->position.x - 0.5f, fminf(player->position.x, block->position.x + 0.5f)),
fmaxf(block->position.y - 0.5f, fminf(cylinder_center.y, block->position.y + 0.5f)),
fmaxf(block->position.z - 0.5f, fminf(player->position.z, block->position.z + 0.5f))
};
float dx = closest_point.x - player->position.x;
float dy = closest_point.y - cylinder_center.y;
float dz = closest_point.z - player->position.z;
float block_top = block->position.y + (block->size.y * 0.5f);
float player_bottom = player->position.y - (player->height * 0.5f);
if (point_in_player_bounding_cylinder(closest_point, player)) {
float overlap_y = (player->height / 2.0f) - fabsf(dy);
float overlap_xz = player->radius - sqrtf(dx * dx + dz * dz);
Vector3 normal;
float overlap;
bool on_ground = false;
// Ground detection: Increased threshold
float ground_threshold = 0.3f; // Relaxed to handle fast falls
if (player_bottom <= block_top + ground_threshold && player_bottom >= block_top - ground_threshold && player->velocity.y <= 0.0f) {
normal = (Vector3){0.0f, 1.0f, 0.0f};
overlap = block_top - player_bottom + 0.01f;
if (overlap < 0.0f) overlap = 0.01f; // Ensure positive overlap
on_ground = true;
// player->onGround = true; // Set here for immediate effect
} else if (overlap_xz > 0.01f) {
// Wall collision
Vector3 delta = {player->position.x - block->position.x, 0.0f, player->position.z - block->position.z};
float abs_dx = fabsf(delta.x);
float abs_dz = fabsf(delta.z);
if (abs_dx > abs_dz + 0.3f) {
normal = (Vector3){delta.x > 0.0f ? 1.0f : -1.0f, 0.0f, 0.0f};
} else {
normal = (Vector3){0.0f, 0.0f, delta.z > 0.0f ? 1.0f : -1.0f};
}
overlap = overlap_xz;
} else {
continue; // Skip invalid collisions
}
Collision collision = {
.block = block,
.contactPoint = closest_point,
.normal = normal,
.overlap = overlap,
.onGround = on_ground
};
append_collision(&collisions, collision);
printf("Narrow-phase collision: Block (%.1f, %.1f, %.1f), Normal (%.1f, %.1f, %.1f), Overlap: %.2f, onGround=%d\n",
block->position.x, block->position.y, block->position.z,
normal.x, normal.y, normal.z, overlap, on_ground);
}
}
printf("Narrow-phase: %d collisions\n", collisions.count);
return collisions;
}
// Visualize contact point
void add_contact_pointer_helper(Vector3 contact_point) {
DrawSphere(contact_point, 0.1f, YELLOW);
}
Candidates broad_phase(ObjWorld* world, ObjPlayer* player) {
Candidates candidates;
init_candidates(&candidates, 16);
// Bounding box with larger downward extension
BoundingBox player_box = {
.min = {player->position.x - player->radius - 0.1f, player->position.y - player->height / 2.0f - 0.5f, player->position.z - player->radius - 0.1f},
.max = {player->position.x + player->radius + 0.1f, player->position.y + player->height / 2.0f + 0.1f, player->position.z + player->radius + 0.1f}
};
printf("Player box: Min (%.2f, %.2f, %.2f), Max (%.2f, %.2f, %.2f)\n",
player_box.min.x, player_box.min.y, player_box.min.z,
player_box.max.x, player_box.max.y, player_box.max.z);
for (int i = 0; i < world->count; i++) {
ObjBlock* block = &world->blocks[i];
BoundingBox block_box = {
.min = Vector3Subtract(block->position, Vector3Scale(block->size, 0.5f)),
.max = Vector3Add(block->position, Vector3Scale(block->size, 0.5f))
};
if (CheckCollisionBoxes(player_box, block_box)) {
append_candidate(&candidates, block);
printf("Broad-phase candidate: (%.1f, %.1f, %.1f), Player y=%.2f\n",
block->position.x, block->position.y, block->position.z, player->position.y);
}
}
printf("Broad-phase: %d candidates\n", candidates.count);
return candidates;
}
// Update player input
void update_input_player(float dt, ObjPlayer* player) {
player->input = (Vector3){0.0f, 0.0f, 0.0f};
if (IsKeyDown(KEY_W)) player->input.z -= 1.0f;
if (IsKeyDown(KEY_S)) player->input.z += 1.0f;
if (IsKeyDown(KEY_A)) player->input.x -= 1.0f;
if (IsKeyDown(KEY_D)) player->input.x += 1.0f;
printf("Input: (%.1f, %.1f, %.1f)\n", player->input.x, player->input.y, player->input.z);
if (IsKeyPressed(KEY_SPACE)) {
if (player->onGround) {
player->velocity.y = 6.0f;
player->onGround = false;
printf("Jump triggered! Pos: (%.2f, %.2f, %.2f), Vel.y=%.2f\n",
player->position.x, player->position.y, player->position.z, player->velocity.y);
} else {
printf("Jump failed: Not on ground (onGround=%d), Pos: (%.2f, %.2f, %.2f)\n",
player->onGround, player->position.x, player->position.y, player->position.z);
}
}
float mag = Vector3Length(player->input);
if (mag > 1.0f) {
player->input = Vector3Scale(player->input, 1.0f / mag);
}
Vector3 target_velocity = {player->input.x * player->maxSpeed, player->velocity.y, player->input.z * player->maxSpeed};
float lerp_factor = 1.0f - powf(0.001f, dt);
player->velocity.x = Lerp(player->velocity.x, target_velocity.x, lerp_factor);
player->velocity.z = Lerp(player->velocity.z, target_velocity.z, lerp_factor);
printf("Post-input velocity: (%.2f, %.2f, %.2f)\n", player->velocity.x, player->velocity.y, player->velocity.z);
}
// Physics update
void physics_update(ObjWorld* world, ObjPlayer* player, float dt) {
static float accumulatedTime = 0.0f;
float simulationRate = 60.0f;
float stepSize = 1.0f / simulationRate;
float maxStepSize = 0.0167f;
static Vector3 last_position = {0};
static Vector3 last_velocity = {0};
static bool skip_collision = false;
accumulatedTime += dt;
printf("Physics update: dt=%.4f, accumulatedTime=%.4f, stepSize=%.4f\n", dt, accumulatedTime, stepSize);
int steps = 0;
while (accumulatedTime >= stepSize) {
float currentStep = fminf(stepSize, maxStepSize);
printf("Physics step %d: Pos (%.2f, %.2f, %.2f), Vel (%.2f, %.2f, %.2f), onGround=%d\n",
steps, player->position.x, player->position.y, player->position.z,
player->velocity.x, player->velocity.y, player->velocity.z, player->onGround);
update_input_player(currentStep, player);
if (!player->onGround) {
player->velocity.y -= gravity * currentStep;
if (player->velocity.y < -10.0f) {
player->velocity.y = -10.0f;
printf("Capped velocity.y: %.2f\n", player->velocity.y);
}
printf("Applied gravity: Vel.y=%.2f\n", player->velocity.y);
}
player->position.x += player->velocity.x * currentStep;
player->position.y += player->velocity.y * currentStep;
player->position.z += player->velocity.z * currentStep;
printf("Updated position: Pos (%.2f, %.2f, %.2f)\n",
player->position.x, player->position.y, player->position.z);
if (Vector3Distance(player->position, last_position) < 0.01f &&
Vector3Distance(player->velocity, last_velocity) < 0.01f &&
Vector3Length(player->input) < 0.01f) {
skip_collision = true;
printf("Skipping collision: Stationary\n");
} else {
skip_collision = false;
last_position = player->position;
last_velocity = player->velocity;
}
if (!skip_collision) {
Candidates candidates = broad_phase(world, player);
Collisions collisions = narrow_phase(&candidates, player);
resolve_collisions(world, &collisions, player);
free_collisions(&collisions);
free_candidates(&candidates);
}
accumulatedTime -= stepSize;
steps++;
}
if (accumulatedTime > 0.0f && accumulatedTime < stepSize && dt > 0.0f) {
static float totalTime = 0.0f;
totalTime += dt;
if (totalTime >= stepSize) {
printf("Forced physics step: totalTime=%.4f\n", totalTime);
float currentStep = fminf(stepSize, maxStepSize);
update_input_player(currentStep, player);
if (!player->onGround) {
player->velocity.y -= gravity * currentStep;
if (player->velocity.y < -10.0f) player->velocity.y = -10.0f;
}
player->position.x += player->velocity.x * currentStep;
player->position.y += player->velocity.y * currentStep;
player->position.z += player->velocity.z * currentStep;
if (Vector3Distance(player->position, last_position) < 0.01f &&
Vector3Distance(player->velocity, last_velocity) < 0.01f &&
Vector3Length(player->input) < 0.01f) {
skip_collision = true;
printf("Skipping collision (forced step): Stationary\n");
} else {
skip_collision = false;
last_position = player->position;
last_velocity = player->velocity;
}
if (!skip_collision) {
Candidates candidates = broad_phase(world, player);
Collisions collisions = narrow_phase(&candidates, player);
resolve_collisions(world, &collisions, player);
free_collisions(&collisions);
free_candidates(&candidates);
}
totalTime -= stepSize;
accumulatedTime = 0.0f;
}
}
}
// Render the world
void render_world(ObjWorld* world, ObjPlayer* player, DebugCubes* debug_cubes, DebugCollisions* debug_collisions) {
for (int i = 0; i < world->count; i++) {
ObjBlock* block = &world->blocks[i];
DrawCubeV(block->position, block->size, block->color);
DrawCubeWiresV(block->position, block->size, BLACK);
}
// DrawCubeV(player->position, player->size, player->color);
DrawCubeWiresV(player->position, player->size, BLACK);
for (int i = 0; i < debug_cubes->count; i++) {
ObjBlock* block = debug_cubes->blocks[i];
DrawCubeWiresV(block->position, block->size, YELLOW);
}
for (int i = 0; i < debug_collisions->count; i++) {
Collision* collision = &debug_collisions->collisions[i];
add_contact_pointer_helper(collision->contactPoint);
Vector3 normal_end = Vector3Add(collision->contactPoint, Vector3Scale(collision->normal, 1.0f));
DrawLine3D(collision->contactPoint, normal_end, RED);
}
}
int main(void) {
const int screenWidth = 800;
const int screenHeight = 600;
InitWindow(screenWidth, screenHeight, "Collision Demo");
SetTargetFPS(60);
DisableCursor();
Camera3D camera = { 0 };
camera.up = (Vector3){0.0f, 1.0f, 0.0f};
camera.fovy = 45.0f;
camera.projection = CAMERA_PERSPECTIVE;
ObjPlayer player = {
.maxSpeed = 3.0f,
.size = {1.0f, 1.85f, 1.0f},
.height = 1.85f,
.radius = 0.4f,
.position = {4.5f, 3.85f, 4.5f}, // Centered in 8x8 world
.color = {0, 255, 0, 255},
.velocity = {0.0f, 0.0f, 0.0f},
.input = {0.0f, 0.0f, 0.0f},
.jumpGround = 2.0f,
.onGround = true
};
ObjWorld world;
DebugCubes debug_cubes;
DebugCollisions debug_collisions;
int world_size = t_width;
generate_world(&world, world_size);
init_debug_cubes(&debug_cubes, 4);
init_debug_collisions(&debug_collisions, 4);
printf("World contains %d blocks:\n", world.count);
for (int i = 0; i < world.count; i++) {
ObjBlock* block = &world.blocks[i];
printf("Block %d: Position (%.1f, %.1f, %.1f), Color (%d, %d, %d, %d)\n",
i, block->position.x, block->position.y, block->position.z,
block->color.r, block->color.g, block->color.b, block->color.a);
}
float accumulator = 0.0f;
while (!WindowShouldClose()) {
if (IsKeyPressed(KEY_R)) {
player.position.x = 0.5f; // Slightly off wall to avoid x=0 collision
player.position.y = 2.425f; // Ground top (1.5) + player->height/2 (0.925)
player.position.z = 4.0f;
player.velocity = (Vector3){0.0f, 0.0f, 0.0f};
player.onGround = true;
printf("Reset to ground: Pos (%.2f, %.2f, %.2f)\n",
player.position.x, player.position.y, player.position.z);
}
if (IsKeyPressed(KEY_T)) {
player.position.x = 0.5f; // Slightly off wall
player.position.y = 2.425f; // Ground top (1.5) + player->height/2 (0.925)
player.position.z = 4.0f;
player.velocity = (Vector3){0.0f, 0.0f, 0.0f};
player.onGround = true;
printf("Reset to ground: Pos (%.2f, %.2f, %.2f)\n",
player.position.x, player.position.y, player.position.z);
}
float dt = GetFrameTime();
// accumulator += dt;
// // Clamp accumulator to prevent spiral-of-death
// if (accumulator > 0.2f) accumulator = 0.2f;
// while (accumulator >= stepSize) {
// physics_update(&world, &player, stepSize);
// accumulator -= stepSize;
// }
physics_update(&world, &player, dt);
camera.position = Vector3Add(player.position, (Vector3){0.0f, 3.0f, 5.0f});
camera.target = player.position;
BeginDrawing();
ClearBackground(RAYWHITE);
BeginMode3D(camera);
render_world(&world, &player, &debug_cubes, &debug_collisions);
DrawGrid(world_size, 1.0f);
EndMode3D();
DrawFPS(10, 10);
float player_base_y = player.position.y - player.height * 0.5f;
ObjBlock* block_below = get_block(&world, (int)player.position.x, player_base_y, (int)player.position.z); // Fixed: pass &world
float block_height = block_below ? block_below->position.y : -1.0f;
DrawText(TextFormat("OnGround: %d, Collisions: %d, BlockY: %.1f, Vel: (%.1f, %.1f, %.1f), Acc: %.4f",
player.onGround, debug_collisions.count, block_height,
player.velocity.x, player.velocity.y, player.velocity.z, accumulator),
10, 30, 20, BLACK);
EndDrawing();
}
free_world(&world);
free_debug_cubes(&debug_cubes);
free_debug_collisions(&debug_collisions);
CloseWindow();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment