physics: handle entity collisions
parent
37dc992c2e
commit
99a65431d8
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue