Files
school_stm32/game_engine/ecs/ge_collision_system.h
ZZY b5b2c90e75 feat(game_core): 重构游戏引擎并添加新功能
- 重构了游戏引擎的核心逻辑和架构
- 添加了新的实体组件系统(ECS)
- 实现了简单的碰撞检测和响应
- 新增了地图和子弹功能
- 优化了输入处理和渲染逻辑
- 调整了游戏控制方式
2025-07-02 12:14:57 +08:00

187 lines
7.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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__ */