- 重构了游戏引擎的核心逻辑和架构 - 添加了新的实体组件系统(ECS) - 实现了简单的碰撞检测和响应 - 新增了地图和子弹功能 - 优化了输入处理和渲染逻辑 - 调整了游戏控制方式
187 lines
7.2 KiB
C
187 lines
7.2 KiB
C
#ifndef __GE_COLLISION_SYSTEM_H__
|
||
#define __GE_COLLISION_SYSTEM_H__
|
||
|
||
#include "ge_entity.h"
|
||
#include "ge_collision_component.h"
|
||
#include <utils/ge_vector2i.h>
|
||
#include <utils/ge_maroc.h>
|
||
#include <pynic_log/pynic_log.h>
|
||
|
||
|
||
typedef void (*ge_collision_response_func_t)(
|
||
void* ctx,
|
||
ge_entity_t* entity_from,
|
||
ge_entity_t* entity_to,
|
||
const ge_vector2i_t* vector // 反向推荐速度
|
||
);
|
||
|
||
typedef struct ge_collision_system {
|
||
ge_ecs_storage_t* ecs;
|
||
void* ctx;
|
||
ge_collision_response_func_t callback;
|
||
} ge_collision_system_t;
|
||
|
||
#define _GE_COLLIDER_MASK (GE_COLLIDER_MASK | GE_COMPONENT_ACVIVE)
|
||
|
||
/**
|
||
* must transit to global position before calling this function
|
||
*/
|
||
static inline int check_box_collision(ge_collision_box_t* a, ge_collision_box_t* b) {
|
||
return (a->relative_pos.x < b->relative_pos.x + b->size.x) &&
|
||
(a->relative_pos.x + a->size.x > b->relative_pos.x) &&
|
||
(a->relative_pos.y < b->relative_pos.y + b->size.y) &&
|
||
(a->relative_pos.y + a->size.y > b->relative_pos.y);
|
||
}
|
||
|
||
static inline ge_vector2i_t check_tilemap_collision(
|
||
ge_collision_box_t* entity_collision,
|
||
ge_vector2i_t* entity_pos,
|
||
ge_collision_tilemap_t* tilemap,
|
||
ge_vector2i_t* tilemap_pos
|
||
) {
|
||
Assert(tilemap->tile_size > 0);
|
||
Assert(tilemap->map_size.x > 0 && tilemap->map_size.y > 0);
|
||
Assert(entity_collision->size.x > 0 && entity_collision->size.y > 0);
|
||
|
||
const ge_unit_t tile_size_int = tilemap->tile_size;
|
||
const ge_unit_t grid_cols = tilemap->map_size.x;
|
||
const ge_unit_t grid_rows = tilemap->map_size.y;
|
||
|
||
// 计算实体碰撞盒在瓦片地图局部坐标系中的位置
|
||
const ge_vector2i_t local_entity_pos = GE_VEC2I_SUB(
|
||
*entity_pos,
|
||
*tilemap_pos
|
||
);
|
||
|
||
// 计算实体边界(闭区间)
|
||
const int entity_left = local_entity_pos.x;
|
||
const int entity_top = local_entity_pos.y;
|
||
const int entity_right = entity_left + entity_collision->size.x;
|
||
const int entity_bottom = entity_top + entity_collision->size.y;
|
||
|
||
// 计算覆盖的瓦片范围
|
||
const int start_tile_x = GE_MAX(0, entity_left / tile_size_int);
|
||
const int start_tile_y = GE_MAX(0, entity_top / tile_size_int);
|
||
const int end_tile_x = GE_MIN(grid_cols - 1, entity_right / tile_size_int);
|
||
const int end_tile_y = GE_MIN(grid_rows - 1, entity_bottom / tile_size_int);
|
||
|
||
ge_vector2i_t correction = GE_VEC2I(0, 0); // 默认无纠正
|
||
|
||
for (int y = start_tile_y; y <= end_tile_y; y++) {
|
||
for (int x = start_tile_x; x <= end_tile_x; x++) {
|
||
if (tilemap->layers[y * grid_cols + x] == 0) continue;
|
||
|
||
// 计算当前瓦片的边界
|
||
const int tile_left = x * tile_size_int;
|
||
const int tile_top = y * tile_size_int;
|
||
const int tile_right = tile_left + tile_size_int;
|
||
const int tile_bottom = tile_top + tile_size_int;
|
||
|
||
// 检查实体是否实际接触到瓦片内部
|
||
const int overlap_x = entity_left < tile_right && entity_right > tile_left;
|
||
const int overlap_y = entity_top < tile_bottom && entity_bottom > tile_top;
|
||
|
||
if (overlap_x && overlap_y) {
|
||
// 计算重叠区域
|
||
const int overlap_left = GE_MAX(entity_left, tile_left);
|
||
const int overlap_right = GE_MIN(entity_right, tile_right);
|
||
const int overlap_top = GE_MAX(entity_top, tile_top);
|
||
const int overlap_bottom = GE_MIN(entity_bottom, tile_bottom);
|
||
|
||
const int overlap_width = overlap_right - overlap_left;
|
||
const int overlap_height = overlap_bottom - overlap_top;
|
||
|
||
// 计算实体中心到瓦片中心的向量
|
||
const int entity_center_x = (entity_left + entity_right) / 2;
|
||
const int entity_center_y = (entity_top + entity_bottom) / 2;
|
||
const int tile_center_x = (tile_left + tile_right) / 2;
|
||
const int tile_center_y = (tile_top + tile_bottom) / 2;
|
||
|
||
const int dx = entity_center_x - tile_center_x;
|
||
const int dy = entity_center_y - tile_center_y;
|
||
|
||
// 确定主要分离方向(选择重叠量较小的轴)
|
||
if (overlap_width < overlap_height) {
|
||
// X轴方向分离(左右方向)
|
||
correction.x = (dx > 0) ? overlap_width : -overlap_width;
|
||
} else {
|
||
// Y轴方向分离(上下方向)
|
||
correction.y = (dy > 0) ? overlap_height : -overlap_height;
|
||
}
|
||
|
||
// 返回第一个找到的碰撞纠正(最大影响)
|
||
return correction;
|
||
}
|
||
}
|
||
}
|
||
|
||
return correction; // 无碰撞时返回零向量
|
||
}
|
||
|
||
// FIXME bad practice
|
||
static inline void
|
||
ge_collision_system_run_all(ge_collision_system_t* ctx) {
|
||
Assert(ctx != NULL);
|
||
if (ctx->callback == NULL) return;
|
||
ge_ecs_storage_t* ecs = ctx->ecs;
|
||
Assert(ecs != NULL);
|
||
for (int i = 1; i <= ecs->count && i < GE_ECS_MAX; ++i) {
|
||
ge_entity_t* entity_i = &ecs->entities[i];
|
||
ge_ecs_mask_t mask_i = entity_i->component_mask;
|
||
if ((mask_i & _GE_COLLIDER_MASK) != _GE_COLLIDER_MASK) continue;
|
||
ge_collision_component_t* comp_i = &entity_i->collision;
|
||
Assert(comp_i != NULL);
|
||
// FIXME this design is bad
|
||
/**
|
||
* 仅仅尝试 (box, box) 和 (box, tilemap) 的碰撞
|
||
*/
|
||
if (comp_i->type != GE_COLLISION_COMP_TYPE_BOX) continue;
|
||
|
||
for (int j = 1; j <= ecs->count && j < GE_ECS_MAX; ++j) {
|
||
if (i == j) continue;
|
||
ge_entity_t* entity_j = &ecs->entities[j];
|
||
ge_ecs_mask_t mask_j = entity_j->component_mask;
|
||
if ((mask_j & _GE_COLLIDER_MASK) != _GE_COLLIDER_MASK) continue;
|
||
ge_collision_component_t* comp_j = &entity_j->collision;
|
||
Assert(comp_j != NULL);
|
||
|
||
/**
|
||
* using layer/mask to accelerate check
|
||
*/
|
||
if ( !(comp_i->mask & comp_j->layers) ) {
|
||
continue;
|
||
}
|
||
|
||
/**
|
||
* Check collision
|
||
*/
|
||
switch (comp_j->type) {
|
||
case GE_COLLISION_COMP_TYPE_BOX: {
|
||
int ret = check_box_collision(&comp_i->collider.box, &comp_j->collider.box);
|
||
if (!ret) ctx->callback(ctx->ctx, entity_i, entity_j, NULL);
|
||
break;
|
||
}
|
||
case GE_COLLISION_COMP_TYPE_TILEMAP: {
|
||
ge_vector2i_t ret = check_tilemap_collision(
|
||
&comp_i->collider.box,
|
||
&entity_i->position,
|
||
&comp_j->collider.tilemap,
|
||
&entity_j->position
|
||
);
|
||
|
||
if (ret.x == 0 && ret.y == 0) {
|
||
continue;
|
||
}
|
||
ctx->callback(ctx->ctx, entity_i, entity_j, &ret);
|
||
break;
|
||
}
|
||
default:
|
||
LOG_WARN("Collision system: Unknown collision type");
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#endif /* __GE_COLLISION_SYSTEM_H__ */
|