From 99a65431d8b50e08556bfe9e0a0c53a691809f67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Madar=C3=A1sz?= Date: Mon, 26 Jul 2021 17:53:18 +0200 Subject: [PATCH] physics: handle entity collisions --- code/game/header/profiler.h | 1 + code/game/header/world/blocks.h | 2 +- code/game/header/world/world.h | 12 +++++++ code/game/source/debug_ui.c | 26 ++++++++++++++ code/game/source/entity.c | 12 +++++-- code/game/source/player.c | 14 ++------ code/game/source/profiler.c | 1 + code/game/source/world/world.c | 53 ++++++++++++++++++++++++++-- code/modules/modules/general.h | 25 +++++++------- code/modules/modules/physics.h | 16 ++++----- code/modules/source/physics.c | 61 ++++++++++++++++++++++++--------- 11 files changed, 169 insertions(+), 54 deletions(-) diff --git a/code/game/header/profiler.h b/code/game/header/profiler.h index 930c1a8..1636898 100644 --- a/code/game/header/profiler.h +++ b/code/game/header/profiler.h @@ -10,6 +10,7 @@ typedef enum { PROF_UPDATE_SYSTEMS, PROF_ENTITY_LERP, PROF_ENTITY_REMOVAL, + PROF_INTEGRATE_POS, MAX_PROF, PROF_FORCE_UINT8 = UINT8_MAX diff --git a/code/game/header/world/blocks.h b/code/game/header/world/blocks.h index 40d3dae..227fc39 100644 --- a/code/game/header/world/blocks.h +++ b/code/game/header/world/blocks.h @@ -4,7 +4,7 @@ #define BLOCK_INVALID 0xF typedef enum { - BLOCK_FLAG_COLLISION = (1 << 0) + BLOCK_FLAG_COLLISION = (1 << 1) } block_flags; #include "blocks_info.h" diff --git a/code/game/header/world/world.h b/code/game/header/world/world.h index f50abca..9ef8398 100644 --- a/code/game/header/world/world.h +++ b/code/game/header/world/world.h @@ -30,11 +30,13 @@ typedef struct { uint32_t size; uint16_t chunk_size; uint16_t chunk_amount; + uint8_t **block_mapping; uint16_t dim; uint64_t tracker_update[3]; uint8_t active_layer_id; ecs_world_t *ecs; ecs_query_t *ecs_update; + ecs_entity_t *chunk_mapping; librg_world *tracker; world_pkt_reader_proc *reader_proc; world_pkt_writer_proc *writer_proc; @@ -55,3 +57,13 @@ librg_world * world_tracker(void); uint16_t world_chunk_size(void); uint16_t world_chunk_amount(void); uint16_t world_dim(void); +ecs_entity_t world_chunk_mapping(librg_chunk id); + +typedef struct { + uint32_t id; + uint8_t block_id; + ecs_entity_t chunk_id; + float ox, oy; +} world_block_lookup; + +world_block_lookup world_block_from_realpos(float x, float y); \ No newline at end of file diff --git a/code/game/source/debug_ui.c b/code/game/source/debug_ui.c index b9fe4ff..17bccc9 100644 --- a/code/game/source/debug_ui.c +++ b/code/game/source/debug_ui.c @@ -46,6 +46,11 @@ typedef struct debug_item { uint8_t is_collapsed; } list; + struct { + float val, min, max; + void (*on_change)(float); + } slider; + void (*on_click)(void); }; @@ -79,6 +84,7 @@ static debug_item items[] = { { .kind = DITEM_TEXT, .name = "delta time", .proc = DrawDeltaTime }, { .kind = DITEM_TEXT, .name = "random literal", .text = "hello", .proc = DrawLiteral }, { .kind = DITEM_TEXT, .name = "zoom", .proc = DrawZoom }, + { .kind = DITEM_SLIDER, .name = "slider", .slider = { .min = 0.0f, .max = 1.0f, .val = 0.5f } }, { .kind = DITEM_END }, } } @@ -95,6 +101,7 @@ static debug_item items[] = { { .kind = DITEM_RAW, .val = PROF_UPDATE_SYSTEMS, .proc = DrawProfilerDelta }, { .kind = DITEM_RAW, .val = PROF_ENTITY_LERP, .proc = DrawProfilerDelta }, { .kind = DITEM_RAW, .val = PROF_ENTITY_REMOVAL, .proc = DrawProfilerDelta }, + { .kind = DITEM_RAW, .val = PROF_INTEGRATE_POS, .proc = DrawProfilerDelta }, { .kind = DITEM_END }, }, .is_collapsed = 1 @@ -163,6 +170,25 @@ debug_draw_result debug_draw_list(debug_item *list, float xpos, float ypos, bool ypos = res.y; }break; + case DITEM_SLIDER: { + assert(it->slider.min != it->slider.max); + char const *text = TextFormat("%s: ", it->name); + if (it->name_width == 0) { + it->name_width = UIMeasureText(text, DBG_FONT_SIZE); + } + UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, RAYWHITE); + xpos += it->name_width; + + DrawRectangleLines(xpos, ypos, 100.0f, DBG_FONT_SIZE, RAYWHITE); + + float stick_x = xpos + ((it->slider.val / it->slider.max) * 100.0f) - 5.0f; + DrawRectangle(stick_x, ypos, 10.0f, DBG_FONT_SIZE, RED); + + xpos += 100.0f + 5.0f; + DrawFloat(xpos, ypos, it->slider.val); + ypos += DBG_FONT_SPACING; + }break; + default: { }break; diff --git a/code/game/source/entity.c b/code/game/source/entity.c index 4402aec..2a22108 100644 --- a/code/game/source/entity.c +++ b/code/game/source/entity.c @@ -22,11 +22,19 @@ uint64_t entity_spawn(char *name) { ecs_set(world_ecs(), e, EcsName, {.alloc_value = name }); ecs_set(world_ecs(), e, Velocity, {0}); - ecs_set(world_ecs(), e, Position, {0}); ecs_add(world_ecs(), e, Walking); + Position *pos = ecs_get_mut(world_ecs(), e, Position, NULL); +#if 1 + pos->x=rand() % world_dim(); + pos->y=rand() % world_dim(); +#else + pos->x=35; + pos->y=33; +#endif + librg_entity_track(world_tracker(), e); - librg_entity_chunk_set(world_tracker(), e, librg_chunk_from_realpos(world_tracker(), 0, 0, 0)); + librg_entity_chunk_set(world_tracker(), e, librg_chunk_from_realpos(world_tracker(), pos->x, pos->y, 0)); return (uint64_t)e; } diff --git a/code/game/source/player.c b/code/game/source/player.c index d550ae7..cfe89d6 100644 --- a/code/game/source/player.c +++ b/code/game/source/player.c @@ -21,28 +21,18 @@ uint64_t player_spawn(char *name) { ECS_IMPORT(world_ecs(), General); ECS_IMPORT(world_ecs(), Controllers); ECS_IMPORT(world_ecs(), Net); + ecs_set(world_ecs(), e, EcsName, {.alloc_value = name }); ecs_add(world_ecs(), e, EcsClient); ecs_set(world_ecs(), e, ClientInfo, {0}); ecs_set(world_ecs(), e, Input, {0}); ecs_add(world_ecs(), e, Player); - Position *pos = ecs_get_mut(world_ecs(), e, Position, NULL); -#if 1 - pos->x=rand() % world_dim(); - pos->y=rand() % world_dim(); -#else - pos->x=10; - pos->y=10; -#endif librg_entity_owner_set(world_tracker(), e, (int64_t)e); - librg_entity_radius_set(world_tracker(), e, 3); - librg_entity_chunk_set(world_tracker(), e, librg_chunk_from_realpos(world_tracker(), pos->x, pos->y, 0)); return (uint64_t)e; } void player_despawn(uint64_t ent_id) { - librg_entity_untrack(world_tracker(), ent_id); - ecs_delete(world_ecs(), ent_id); + entity_despawn(ent_id); } diff --git a/code/game/source/profiler.c b/code/game/source/profiler.c index 7cc0467..3fd1b99 100644 --- a/code/game/source/profiler.c +++ b/code/game/source/profiler.c @@ -13,6 +13,7 @@ static profiler profilers[] = { { .id = PROF_UPDATE_SYSTEMS, .name = "update systems" }, { .id = PROF_ENTITY_LERP, .name = "entity lerp" }, { .id = PROF_ENTITY_REMOVAL, .name = "entity removal" }, + { .id = PROF_INTEGRATE_POS, .name = "entity movement" }, }; static_assert((sizeof(profilers)/sizeof(profilers[0])) == MAX_PROF, "mismatched profilers"); diff --git a/code/game/source/world/world.c b/code/game/source/world/world.c index 4f85d77..ffc5fb9 100644 --- a/code/game/source/world/world.c +++ b/code/game/source/world/world.c @@ -42,7 +42,7 @@ entity_view world_build_entity_view(int64_t e) { view.blocks_used = 1; for (int i = 0; i < world.chunk_size*world.chunk_size; i += 1) { - view.blocks[i] = *ecs_vector_get(chpos->blocks, uint8_t, i); + view.blocks[i] = world.block_mapping[chpos->id][i]; } } @@ -139,6 +139,8 @@ int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) { ECS_IMPORT(world.ecs, General); ECS_IMPORT(world.ecs, Net); world.ecs_update = ecs_query_new(world.ecs, "net.ClientInfo, general.Position"); + world.chunk_mapping = zpl_malloc(sizeof(ecs_entity_t)*zpl_square(chunk_amount)); + world.block_mapping = zpl_malloc(sizeof(uint8_t*)*zpl_square(chunk_amount)); int32_t world_build_status = worldgen_test(&world); ZPL_ASSERT(world_build_status >= 0); @@ -149,14 +151,16 @@ int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) { librg_entity_track(world.tracker, e); librg_entity_chunk_set(world.tracker, e, i); librg_chunk_to_chunkpos(world.tracker, i, &chunk->x, &chunk->y, NULL); - chunk->blocks = NULL; + world.chunk_mapping[i] = e; + world.block_mapping[i] = zpl_malloc(sizeof(uint8_t)*zpl_square(chunk_size)); + chunk->id = i; for (int y = 0; y < world.chunk_size; y += 1) { for (int x = 0; x < world.chunk_size; x += 1) { int chk = world.chunk_size * i; int chk_x = chk % world.dim; int chk_y = chk / world.dim; - uint8_t *c = ecs_vector_add(&chunk->blocks, uint8_t); + uint8_t *c = &world.block_mapping[i][(y*chunk_size)+x]; *c = world.data[(chk_y+y)*world.dim + (chk_x+x)]; } } @@ -171,6 +175,11 @@ int32_t world_destroy(void) { librg_world_destroy(world.tracker); ecs_fini(world.ecs); zpl_mfree(world.data); + zpl_mfree(world.chunk_mapping); + for (int i = 0; i < zpl_square(world.chunk_amount); i+=1) { + zpl_mfree(world.block_mapping[i]); + } + zpl_mfree(world.block_mapping); zpl_memset(&world, 0, sizeof(world)); zpl_printf("[INFO] World was destroyed.\n"); return WORLD_ERROR_NONE; @@ -269,3 +278,41 @@ uint16_t world_chunk_amount(void) { uint16_t world_dim(void) { return WORLD_BLOCK_SIZE * world.chunk_size * world.chunk_amount; } + +ecs_entity_t world_chunk_mapping(librg_chunk id) { + assert(id >= 0 && id < zpl_square(world.chunk_amount)); + return world.chunk_mapping[id]; +} + +world_block_lookup world_block_from_realpos(float x, float y) { + x = zpl_clamp(x, 0, world_dim()-1); + y = zpl_clamp(y, 0, world_dim()-1); + librg_chunk chunk_id = librg_chunk_from_realpos(world.tracker, x, y, 0); + ecs_entity_t e = world.chunk_mapping[chunk_id]; + int32_t size = world.chunk_size * WORLD_BLOCK_SIZE; + int16_t chunk_x, chunk_y; + librg_chunk_to_chunkpos(world.tracker, chunk_id, &chunk_x, &chunk_y, NULL); + + // NOTE(zaklaus): pos relative to chunk + float chx = x - chunk_x * size; + float chy = y - chunk_y * size; + + uint32_t bx = (uint32_t)chx / WORLD_BLOCK_SIZE; + uint32_t by = (uint32_t)chy / WORLD_BLOCK_SIZE; + uint32_t block_idx = (by*world.chunk_size)+bx; + uint8_t block_id = world.block_mapping[chunk_id][block_idx]; + + // NOTE(zaklaus): pos relative to block's center + float box = chx - bx * WORLD_BLOCK_SIZE - WORLD_BLOCK_SIZE/2.0f; + float boy = chy - by * WORLD_BLOCK_SIZE - WORLD_BLOCK_SIZE/2.0f; + + world_block_lookup lookup = { + .id = block_idx, + .block_id = block_id, + .chunk_id = e, + .ox = box, + .oy = boy, + }; + + return lookup; +} diff --git a/code/modules/modules/general.h b/code/modules/modules/general.h index 78cb403..9933bc2 100644 --- a/code/modules/modules/general.h +++ b/code/modules/modules/general.h @@ -4,19 +4,20 @@ #include "world/world.h" ECS_STRUCT(Vector2D, { - float x; - float y; + float x; + float y; }); ECS_STRUCT(Chunk, { - int16_t x; - int16_t y; - ecs_vector(uint8_t) blocks; -}); + uint32_t id; + int16_t x; + int16_t y; + //ecs_vector(uint8_t) blocks; + }); ECS_STRUCT(Drawable, { - uint16_t id; -}); + uint16_t id; + }); ECS_ALIAS(Vector2D, Position); @@ -28,9 +29,9 @@ typedef struct { } General; #define GeneralImportHandles(handles)\ - ECS_IMPORT_COMPONENT(handles, Chunk);\ - ECS_IMPORT_COMPONENT(handles, Vector2D);\ - ECS_IMPORT_COMPONENT(handles, Position);\ - ECS_IMPORT_COMPONENT(handles, Drawable);\ +ECS_IMPORT_COMPONENT(handles, Chunk);\ +ECS_IMPORT_COMPONENT(handles, Vector2D);\ +ECS_IMPORT_COMPONENT(handles, Position);\ +ECS_IMPORT_COMPONENT(handles, Drawable);\ void GeneralImport(ecs_world_t *ecs); diff --git a/code/modules/modules/physics.h b/code/modules/modules/physics.h index f41cc29..4342f66 100644 --- a/code/modules/modules/physics.h +++ b/code/modules/modules/physics.h @@ -13,16 +13,16 @@ typedef struct { ECS_DECLARE_COMPONENT(Velocity); ECS_DECLARE_ENTITY(MoveWalk); ECS_DECLARE_ENTITY(UpdateTrackerPos); - ECS_DECLARE_ENTITY(HandleCollisions); + ECS_DECLARE_ENTITY(IntegratePositions); } Physics; #define PhysicsImportHandles(handles)\ - ECS_IMPORT_TYPE(handles, Movement);\ - ECS_IMPORT_ENTITY(handles, Walking);\ - ECS_IMPORT_ENTITY(handles, Flying);\ - ECS_IMPORT_COMPONENT(handles, Velocity);\ - ECS_IMPORT_ENTITY(handles, MoveWalk);\ - ECS_IMPORT_ENTITY(handles, UpdateTrackerPos);\ - ECS_IMPORT_ENTITY(handles, HandleCollisions);\ +ECS_IMPORT_TYPE(handles, Movement);\ +ECS_IMPORT_ENTITY(handles, Walking);\ +ECS_IMPORT_ENTITY(handles, Flying);\ +ECS_IMPORT_COMPONENT(handles, Velocity);\ +ECS_IMPORT_ENTITY(handles, MoveWalk);\ +ECS_IMPORT_ENTITY(handles, UpdateTrackerPos);\ +ECS_IMPORT_ENTITY(handles, IntegratePositions);\ void PhysicsImport(ecs_world_t *ecs); diff --git a/code/modules/source/physics.c b/code/modules/source/physics.c index 6ea45dc..317755f 100644 --- a/code/modules/source/physics.c +++ b/code/modules/source/physics.c @@ -1,30 +1,59 @@ + #include "zpl.h" #include "modules/physics.h" #include "world/world.h" +#include "world/blocks.h" +#include "profiler.h" +#define PHY_BLOCK_COLLISION 1 #define PHY_WALK_DRAG 0.12 +#define PHY_LOOKAHEAD(x) (zpl_sign(x)*16.0f) +#define PHY_CORRECTION(x) ((zpl_max(0.0f, (WORLD_BLOCK_SIZE/2.0f) - zpl_abs(x))*zpl_sign(x)))*(WORLD_BLOCK_SIZE/2.0f) void MoveWalk(ecs_iter_t *it) { - Position *p = ecs_column(it, Position, 1); - Velocity *v = ecs_column(it, Velocity, 2); + Velocity *v = ecs_column(it, Velocity, 1); for (int i = 0; i < it->count; i++) { - p[i].x += v[i].x * it->delta_time; - p[i].y += v[i].y * it->delta_time; v[i].x = zpl_lerp(v[i].x, 0.0f, PHY_WALK_DRAG); v[i].y = zpl_lerp(v[i].y, 0.0f, PHY_WALK_DRAG); } } -void HandleCollisions(ecs_iter_t *it) { - Position *p = ecs_column(it, Position, 1); - - for (int i = 0; i < it->count; i++) { - // NOTE(zaklaus): world bounds - { - double w = (double)world_dim(); - p[i].x = zpl_clamp(p[i].x, 0, w-1); - p[i].y = zpl_clamp(p[i].y, 0, w-1); +void IntegratePositions(ecs_iter_t *it) { + profile(PROF_INTEGRATE_POS) { + Position *p = ecs_column(it, Position, 1); + Velocity *v = ecs_column(it, Velocity, 2); + + for (int i = 0; i < it->count; i++) { + // NOTE(zaklaus): world bounds + { + double w = (double)world_dim(); + p[i].x = zpl_clamp(p[i].x, 0, w-1); + p[i].y = zpl_clamp(p[i].y, 0, w-1); + } + +#if PHY_BLOCK_COLLISION==1 + // NOTE(zaklaus): X axis + { + world_block_lookup lookup = world_block_from_realpos(p[i].x+PHY_LOOKAHEAD(v[i].x), p[i].y); + uint32_t flags = blocks_get_flags(lookup.block_id); + if (flags & BLOCK_FLAG_COLLISION) { + v[i].x = PHY_CORRECTION(lookup.ox); + } + } + + // NOTE(zaklaus): Y axis + { + world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y+PHY_LOOKAHEAD(v[i].y)); + uint32_t flags = blocks_get_flags(lookup.block_id); + if (flags & BLOCK_FLAG_COLLISION) { + v[i].y = PHY_CORRECTION(lookup.oy); + } + } +#endif + + p[i].x += v[i].x * it->delta_time; + p[i].y += v[i].y * it->delta_time; } } } @@ -47,8 +76,8 @@ void PhysicsImport(ecs_world_t *ecs) { ECS_META(ecs, Velocity); - ECS_SYSTEM(ecs, MoveWalk, EcsOnUpdate, general.Position, Velocity); - ECS_SYSTEM(ecs, HandleCollisions, EcsOnValidate, general.Position, Velocity); + ECS_SYSTEM(ecs, MoveWalk, EcsOnUpdate, Velocity); + ECS_SYSTEM(ecs, IntegratePositions, EcsOnValidate, general.Position, Velocity); ECS_SYSTEM(ecs, UpdateTrackerPos, EcsPostUpdate, general.Position); ECS_SET_TYPE(Movement); @@ -57,5 +86,5 @@ void PhysicsImport(ecs_world_t *ecs) { ECS_SET_COMPONENT(Velocity); ECS_SET_ENTITY(MoveWalk); ECS_SET_ENTITY(UpdateTrackerPos); - ECS_SET_ENTITY(HandleCollisions); + ECS_SET_ENTITY(IntegratePositions); }