diff --git a/.vscode/settings.json b/.vscode/settings.json index 997c8bf..fc2cb77 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -29,6 +29,8 @@ "sokol_time.h": "c", "enet.h": "c", "types.h": "c", - "cmath": "c" + "cmath": "c", + "math.h": "c", + "corecrt_math.h": "c" } } \ No newline at end of file diff --git a/flight.10x b/flight.10x new file mode 100644 index 0000000..46eb060 --- /dev/null +++ b/flight.10x @@ -0,0 +1,46 @@ + + + + *.c,*.cc,*.cpp,*.c++,*.cp,*.cxx,*.h,*.hh,*.hpp,*.h++,*.hp,*.hxx,*.inl,*.cs,*.rs,*.java,*.jav,*.js,*.jsc,*.jsx,*.json,*.cls,*.py,*.rpy,*.php,*.php3,*.phl,*.phtml,*.rhtml,*.tpl,*.phps,*.lua,*.html,*.html5,*.htm,*.xml,*.xaml,*.css,*.ssi,*.haml,*.yaml,*.bat,*.wbf,*.wbt,*.txt,*.cmake,*.make,*.makefile,*.mak,*.mk,*.sh,*.bash,*.csv,*.asp,*.pl,*.mac,*.ws,*.vbs,*.perl,*.src,*.rss,*.inc,*.f,*.go,*.prl,*.plx,*.rb,*.lsp,*.lpx,*.ps1,*.command,*.cbl,*.cob,*.qs,*.wxs,*.ph,*.msc,*.glsl,*.hlsl,*.fx,*.vert,*.tesc,*.tese,*.geom,*.frag,*.comp,*.pssl,*.scons, + + true + true + false + false + build_debug.bat + + + + + flight.exe + devenv.exe \Run $(Workspacefilename) + + + + Debug + Release + + + x64 + Win32 + + + + + + Debug:x64 + + + + + Debug + + + + x64 + + + + + + diff --git a/gamestate.c b/gamestate.c index c160a3d..0f2dc6e 100644 --- a/gamestate.c +++ b/gamestate.c @@ -3,9 +3,9 @@ #include // malloc #include -void __assert(bool cond, const char * file, int line, const char * cond_string) +void __assert(bool cond, const char *file, int line, const char *cond_string) { - if(!cond) + if (!cond) { fprintf(stderr, "%s:%d | Assertion %s failed\n", file, line, cond_string); } @@ -38,32 +38,68 @@ static void integrate_acceleration(struct Body *body, float dt) } } -static void modify_interval(struct Body *from, float *from_interval, V2 center, V2 axis) +struct ProcessBody { - float halfbox = BOX_SIZE/2.0f; - V2 points[4] = { - V2add(from->position, V2rotate((V2){.x = halfbox, .y = -halfbox}, from->rotation)), // upper right - V2add(from->position, V2rotate((V2){.x = halfbox, .y = halfbox}, from->rotation)), // bottom right - V2add(from->position, V2rotate((V2){.x = -halfbox, .y = halfbox}, from->rotation)), // lower left - V2add(from->position, V2rotate((V2){.x = -halfbox, .y = -halfbox}, from->rotation)), // upper left - }; - for (int point_i = 0; point_i < 4; point_i++) - { - float value = V2projectvalue(V2sub(points[point_i], center), axis); - if (value > from_interval[1]) - { - from_interval[1] = value; - } - if (value < from_interval[0]) + V2 vertices[4]; + struct Body *body; +}; + +struct ProcessBody make_process_body(struct Body *from) +{ + float halfbox = BOX_SIZE / 2.0f; + struct ProcessBody to_return = { - from_interval[0] = value; - } + .vertices = { + // important that the first one is the upper right, used to deduce rotation from vertex position + // @Robust instead of array of vertices have type? like struct with upper_right upper_left etc + V2add(from->position, V2rotate((V2){.x = halfbox, .y = -halfbox}, from->rotation)), // upper right + V2add(from->position, V2rotate((V2){.x = halfbox, .y = halfbox}, from->rotation)), // bottom right + V2add(from->position, V2rotate((V2){.x = -halfbox, .y = halfbox}, from->rotation)), // lower left + V2add(from->position, V2rotate((V2){.x = -halfbox, .y = -halfbox}, from->rotation)), // upper left + }, + .body = from, + }; + return to_return; +} + +static void project(struct ProcessBody *from, V2 axis, float *min, float *max) +{ + float DotP = V2dot(axis, from->vertices[0]); + + // Set the minimum and maximum values to the projection of the first vertex + *min = DotP; + *max = DotP; + + for (int I = 1; I < 4; I++) + { + // Project the rest of the vertices onto the axis and extend + // the interval to the left/right if necessary + DotP = V2dot(axis, from->vertices[I]); + + *min = fmin(DotP, *min); + *max = fmax(DotP, *max); } } +static float interval_distance(float min_a, float max_a, float min_b, float max_b) +{ + if (min_a < min_b) + return min_b - max_a; + else + return min_a - max_b; +} + +static void move_vertices(V2 *vertices, int num, V2 shift) +{ + for (int i = 0; i < num; i++) + { + vertices[i] = V2add(vertices[i], shift); + } +} void process(struct GameState *gs, float dt) { + // process input int num_bodies = gs->num_boxes; for (int i = 0; i < MAX_PLAYERS; i++) { @@ -76,7 +112,8 @@ void process(struct GameState *gs, float dt) } // @Robust do this without malloc - struct Body **bodies = malloc(sizeof *bodies * num_bodies); + + struct ProcessBody *bodies = malloc(sizeof *bodies * num_bodies); int cur_body_index = 0; for (int i = 0; i < MAX_PLAYERS; i++) @@ -84,24 +121,21 @@ void process(struct GameState *gs, float dt) struct Player *p = &gs->players[i]; if (!p->connected) continue; - bodies[cur_body_index] = &p->body; + integrate_acceleration(&p->body, dt); + bodies[cur_body_index] = make_process_body(&p->body); cur_body_index++; } for (int i = 0; i < gs->num_boxes; i++) { - bodies[cur_body_index] = &gs->boxes[i].body; + integrate_acceleration(&gs->boxes[i].body, dt); + bodies[cur_body_index] = make_process_body(&gs->boxes[i].body); cur_body_index++; } assert(cur_body_index == num_bodies); - for (int i = 0; i < num_bodies; i++) - { - struct Body *body = bodies[i]; - integrate_acceleration(body, dt); - } - + // Collision // @Robust handle when bodies are overlapping (even perfectly) for (int i = 0; i < num_bodies; i++) { @@ -109,35 +143,125 @@ void process(struct GameState *gs, float dt) { if (ii == i) continue; - struct Body *from = bodies[i]; - struct Body *to = bodies[ii]; - - V2 axis = V2normalize(V2sub(to->position, from->position)); - V2 center = V2scale(V2add(to->position, from->position), 0.5f); - - dbg_line(from->position, to->position); - - dbg_rect(center); - - float from_interval[2] = {1000.0f, -1000.0f}; - float to_interval[2] = {1000.0f, -1000.0f}; - modify_interval(from, from_interval, center, axis); - modify_interval(to, to_interval, center, axis); - assert(from_interval[0] < from_interval[1]); - assert(to_interval[0] < to_interval[1]); - - // @BeforeShip debug compile time flag in preprocessor - - if (from_interval[1] > to_interval[0]) // intersecting + struct ProcessBody *from = &bodies[i]; + struct ProcessBody *to = &bodies[ii]; + dbg_line(from->body->position, to->body->position); + + float MinDistance = 10000.0f; + + struct Edge + { + struct ProcessBody *parent; + V2 *from; + V2 *to; + }; + + struct ProcessBody *bodies[2] = {from, to}; + bool was_collision = false; + V2 normal = {0}; + struct Edge edge = {0}; + for (int body_i = 0; body_i < 2; body_i++) + { + struct ProcessBody *body = bodies[body_i]; + for (int edge_from_i = 0; edge_from_i < 3; edge_from_i++) + { + int edge_to_i = edge_from_i + 1; + V2 *edge_from = &body->vertices[edge_from_i]; + V2 *edge_to = &body->vertices[edge_to_i]; + + // normal vector of edge + V2 axis = (V2){ + .x = edge_from->y - edge_to->y, + .y = edge_to->x - edge_from->x, + }; + axis = V2normalize(axis); + + float min_from, min_to, max_from, max_to = 0.0f; + project(from, axis, &min_from, &max_from); + project(to, axis, &min_to, &max_to); + + float distance = interval_distance(min_from, min_to, max_from, max_to); + + if (distance > 0.0f) + break; + else if (fabsf(distance) < MinDistance) + { + MinDistance = fabsf(distance); + was_collision = true; + normal = axis; + edge = (struct Edge){ + .parent = &body, + .from = edge_from, + .to = edge_to, + }; + } + } + } + float depth = MinDistance; + if (was_collision) { float intersection_depth = from_interval[1] - to_interval[0]; - - from->position = V2add(from->position, V2scale(axis, intersection_depth*-0.5f)); - to->position = V2add(to->position, V2scale(axis, intersection_depth*0.5f)); + move_vertices(from->vertices, 4, V2scale(axis, intersection_depth * -0.5f)); + move_vertices(to->vertices, 4, V2scale(axis, intersection_depth * 0.5f)); } } } + // Wall + if (true) + { + for (int i = 0; i < num_bodies; i++) + { + for (int v_i = 0; v_i < 4; v_i++) + { + V2 *vert = &bodies[i].vertices[v_i]; + if (vert->x > 2.0f) + { + vert->x = 2.0f; + } + } + } + } + + // Correct for differences in vertex position + const int edge_update_iters = 3; + for (int iter = 0; iter < edge_update_iters; iter++) + { + for (int i = 0; i < num_bodies; i++) + { + for (int v_i = 0; v_i < 3; v_i++) + { + int other_v_i = v_i + 1; + V2 *from = &bodies[i].vertices[v_i]; + V2 *to = &bodies[i].vertices[other_v_i]; + + V2 line = V2sub(*to, *from); + float len = V2length(line); + float diff = len - BOX_SIZE; + + line = V2normalize(line); + + *from = V2add(*from, V2scale(line, diff * 0.5f)); + *to = V2sub(*to, V2scale(line, diff * 0.5f)); + } + } + } + + // Reupdate the positions of the bodies based on how the vertices changed + for (int i = 0; i < num_bodies; i++) + { + float upper_right_angle = V2angle(V2sub(bodies[i].vertices[0], bodies[i].body->position)); + bodies[i].body->rotation = upper_right_angle - (PI / 4.0f); + + V2 avg = {0}; + for (int v_i = 0; v_i < 4; v_i++) + { + avg = V2add(avg, bodies[i].vertices[v_i]); + } + avg = V2scale(avg, 1.0f / 4.0f); + bodies[i].body->position = avg; + } + free(bodies); } \ No newline at end of file diff --git a/main.c b/main.c index 0692f49..a58e1c4 100644 --- a/main.c +++ b/main.c @@ -158,6 +158,7 @@ static void frame(void) ENetPacket *packet = enet_packet_create((void *)&curmsg, sizeof(curmsg), ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT); enet_peer_send(peer, 0, packet); + // @BeforeShip client side prediction and rollback to previous server authoritative state, then replay inputs process(&gs, (float)sapp_frame_duration()); } diff --git a/types.h b/types.h index 5203468..c349095 100644 --- a/types.h +++ b/types.h @@ -89,6 +89,8 @@ void dbg_rect(V2 center); // all the math is static so that it can be defined in each compilation unit its included in +#define PI 3.14159f + static V2 V2add(V2 a, V2 b) { return (V2){ @@ -139,6 +141,12 @@ static V2 V2rotate(V2 vec, float theta) }; } +// also known as atan2 +static float V2angle(V2 vec) +{ + return atan2f(vec.y, vec.x); +} + static V2 V2sub(V2 a, V2 b) { return (V2){