From b5b2c90e7512704bf1c45de31ce70dfa4abacb20 Mon Sep 17 00:00:00 2001 From: ZZY <2450266535@qq.com> Date: Wed, 2 Jul 2025 12:14:57 +0800 Subject: [PATCH] =?UTF-8?q?feat(game=5Fcore):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=B8=B8=E6=88=8F=E5=BC=95=E6=93=8E=E5=B9=B6=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=96=B0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构了游戏引擎的核心逻辑和架构 - 添加了新的实体组件系统(ECS) - 实现了简单的碰撞检测和响应 - 新增了地图和子弹功能 - 优化了输入处理和渲染逻辑 - 调整了游戏控制方式 --- game_core/Makefile | 9 +- game_core/config.h | 46 +++++ game_core/entities/bullet.c | 33 +++ game_core/entities/enemy.c | 0 game_core/entities/entities.h | 19 ++ game_core/entities/layer.h | 13 ++ game_core/entities/player.c | 32 +++ game_core/entities/tilemap.c | 81 ++++++++ game_core/entities/tilemap.h | 15 ++ game_core/main.c | 123 ++++++------ game_core/plantform/win_app/win_input.c | 38 ++++ game_core/plantform/win_app/win_interface.h | 21 +- game_core/plantform/win_app/win_main.c | 210 +++----------------- game_core/plantform/win_app/win_render.c | 126 ++++++++++++ game_core/plantform/win_app/win_timer.c | 25 ++- game_core/tilemap.c | 9 - game_engine/ecs/ge_collision_component.h | 34 ++++ game_engine/ecs/ge_collision_system.h | 186 +++++++++++++++++ game_engine/ecs/ge_entity.h | 11 +- game_engine/ecs/ge_physics_component.h | 6 +- game_engine/ecs/ge_physics_system.h | 4 +- game_engine/ecs/ge_render_component.h | 26 ++- game_engine/ecs/ge_render_system.h | 80 +++++--- game_engine/events/ge_events_bus.h | 10 - game_engine/ge_core.c | 6 +- game_engine/ge_core.h | 2 + game_engine/interface/ge_render.h | 16 +- game_engine/physics/ge_collision_box.h | 20 -- game_engine/physics/ge_collision_tilemap.h | 58 ------ game_engine/physics/ge_collison.h | 27 --- game_engine/physics/ge_physics.h | 8 - game_engine/utils/ge_vector2i.h | 3 +- 32 files changed, 856 insertions(+), 441 deletions(-) create mode 100644 game_core/config.h create mode 100644 game_core/entities/bullet.c create mode 100644 game_core/entities/enemy.c create mode 100644 game_core/entities/entities.h create mode 100644 game_core/entities/layer.h create mode 100644 game_core/entities/player.c create mode 100644 game_core/entities/tilemap.c create mode 100644 game_core/entities/tilemap.h create mode 100644 game_core/plantform/win_app/win_input.c create mode 100644 game_core/plantform/win_app/win_render.c delete mode 100644 game_core/tilemap.c create mode 100644 game_engine/ecs/ge_collision_component.h create mode 100644 game_engine/ecs/ge_collision_system.h delete mode 100644 game_engine/events/ge_events_bus.h delete mode 100644 game_engine/physics/ge_collision_box.h delete mode 100644 game_engine/physics/ge_collision_tilemap.h delete mode 100644 game_engine/physics/ge_collison.h delete mode 100644 game_engine/physics/ge_physics.h diff --git a/game_core/Makefile b/game_core/Makefile index 914a73a..795c003 100644 --- a/game_core/Makefile +++ b/game_core/Makefile @@ -9,16 +9,15 @@ BUILD_DIR := build ENGINE_DIR := ../game_engine # 手动指定源文件目录 -SRC_DIRS = $(ROOT_DIR) \ - $(ROOT_DIR)/plantform/win_app \ - $(ENGINE_DIR) - +SRC_DIRS = $(ROOT_DIR) \ + $(ROOT_DIR)/entities \ + $(ROOT_DIR)/plantform/win_app \ + $(ENGINE_DIR) SRCS = $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c)) OBJS := $(patsubst $(ROOT_DIR)/%.c,$(BUILD_DIR)/core/%.o,$(filter $(ROOT_DIR)/%,$(SRCS))) OBJS += $(patsubst $(ENGINE_DIR)/%.c,$(BUILD_DIR)/engine/%.o,$(filter $(ENGINE_DIR)/%,$(SRCS))) - TARGET = $(BUILD_DIR)/../game.exe $(TARGET): $(OBJS) diff --git a/game_core/config.h b/game_core/config.h new file mode 100644 index 0000000..398812d --- /dev/null +++ b/game_core/config.h @@ -0,0 +1,46 @@ +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +/** + * only for main can include + */ + +#include +#include +#include + +#define _pynic_logout_printf(...) fprintf(fp , ##__VA_ARGS__) +#define GE_VEC2I_USE_SHORT_NAMES +#include +/** + * some cross plantform config + */ +#define KEY_UP 'w' +#define KEY_DOWN 's' +#define KEY_LEFT 'a' +#define KEY_RIGHT 'd' +#define KEY_ATTACK 'f' + +#define KEY_EXIT 'q' +#define KEY_STOP 'p' +#define KEY_CONTINUE 'p' + +/** + * logger init part + */ +static FILE* fp; +logger_t logger; +static void log_handler +(log_level_t level, const char* module, const char* file, int line, const char* message) { + fprintf(fp, "[%s] %s:%d | %s: %s\n", + pynic_level_str(level), file, line, module, message); + fflush(fp); +} + +static inline void init_config() { + fp = fopen("D:\\Git_Code\\school_stm32\\game_core\\log.txt", "w+"); + init_logger_ex(&logger, "game", log_handler); + Assert(fp != NULL); +} + +#endif /* __CONFIG_H__ */ diff --git a/game_core/entities/bullet.c b/game_core/entities/bullet.c new file mode 100644 index 0000000..e413e60 --- /dev/null +++ b/game_core/entities/bullet.c @@ -0,0 +1,33 @@ +#include "entities.h" + +void init_bullet(ge_ecs_t* ecs, ge_entity_t **_bullet) { + Assert(ecs != NULL && _bullet != NULL); + ge_ecs_add_entity(ecs, _bullet); + ge_entity_t* bullet = *_bullet; + Assert(bullet != NULL); + + *bullet = (ge_entity_t) { + .user_type = BULLET, + .component_mask = GE_RENDERABLE_MASK | GE_PHYSICS_MASK | GE_COLLIDER_MASK, + .renderable = (ge_render_component_t) { + .type = GE_RENDER_COMP_TYPE_RECT, + .data.rect = { + .size = { 2, 2 }, + .color = GE_COLOR_RED, + } + }, + .physics_body = (ge_physics_component_t) { + .type = GE_PHYSICS_COMP_TYPE_VELOCITY, + .velocity = { 0, 0 }, + }, + .collision = (ge_collision_component_t) { + .type = GE_COLLISION_COMP_TYPE_BOX, + .layers = LAYER_PLAYER_PROJECTILE, + .mask = LAYER_WALL | LAYER_ENEMY_HITBOX, + .collider.box = { + .relative_pos = { 0, 0 }, + .size = { 2, 2 }, + }, + }, + }; +} diff --git a/game_core/entities/enemy.c b/game_core/entities/enemy.c new file mode 100644 index 0000000..e69de29 diff --git a/game_core/entities/entities.h b/game_core/entities/entities.h new file mode 100644 index 0000000..457a0d6 --- /dev/null +++ b/game_core/entities/entities.h @@ -0,0 +1,19 @@ +#ifndef __ENTITY_H__ +#define __ENTITY_H__ + +#include "tilemap.h" +#include "layer.h" + +enum { + PLAYER, + ENEMY, + BULLET, +}; + +void init_player(ge_ecs_t* ecs, ge_entity_t **_player); + +#define MAX_BULLET 16 +#define BASIC_SPEED (1 << GE_PHYSICS_VELOCITY_BIT) +void init_bullet(ge_ecs_t* ecs, ge_entity_t **_bullet); + +#endif \ No newline at end of file diff --git a/game_core/entities/layer.h b/game_core/entities/layer.h new file mode 100644 index 0000000..75c7ad9 --- /dev/null +++ b/game_core/entities/layer.h @@ -0,0 +1,13 @@ +#ifndef __LAYER_H__ +#define __LAYER_H__ + +enum { + LAYER_WALL = 1 << 0, + LAYER_PLAYER_HITBOX = 1 << 1, + LAYER_ENEMY_HITBOX = 1 << 2, + LAYER_PLAYER_PROJECTILE = 1 << 3, + LAYER_ENEMY_PROJECTILE = 1 << 4, + LAYER_POWERUP = 1 << 5, +}; + +#endif /* __LAYER_H__ */ diff --git a/game_core/entities/player.c b/game_core/entities/player.c new file mode 100644 index 0000000..528a40b --- /dev/null +++ b/game_core/entities/player.c @@ -0,0 +1,32 @@ +#include "entities.h" + +void init_player(ge_ecs_t* ecs, ge_entity_t **_player) { + Assert(ecs != NULL && _player != NULL); + ge_ecs_add_entity(ecs, _player); + ge_entity_t* player = *_player; + Assert(player != NULL); + + player->user_type = PLAYER; + player->component_mask = GE_COMPONENT_ACVIVE | GE_RENDERABLE_MASK | GE_PHYSICS_MASK | GE_COLLIDER_MASK; + player->position = (ge_vector2i_t){ TILEMAP_SIZE * 8, TILEMAP_SIZE * 8 }; + player->renderable = (ge_render_component_t) { + .type = GE_RENDER_COMP_TYPE_RECT, + .data.rect = { + .size = {TILEMAP_SIZE, TILEMAP_SIZE}, + .color = GE_COLOR_BLUE, + } + }; + player->physics_body = (ge_physics_component_t) { + .type = GE_PHYSICS_COMP_TYPE_VELOCITY, + .velocity = {0, 0}, + }; + player->collision = (ge_collision_component_t) { + .type = GE_COLLISION_COMP_TYPE_BOX, + .layers = LAYER_PLAYER_HITBOX, + .mask = LAYER_WALL, + .collider.box = { + .relative_pos = {0, 0}, + .size = {TILEMAP_SIZE, TILEMAP_SIZE}, + } + }; +} diff --git a/game_core/entities/tilemap.c b/game_core/entities/tilemap.c new file mode 100644 index 0000000..094c3d5 --- /dev/null +++ b/game_core/entities/tilemap.c @@ -0,0 +1,81 @@ +#include "tilemap.h" +#include +#include + +static ge_render_component_t* tilemap_render[TILEMAP_Y_HEIGHT][TILEMAP_X_WIDTH]; +static ge_layers_t tilemap_layers[TILEMAP_Y_HEIGHT][TILEMAP_X_WIDTH]; +static int real_map[TILEMAP_Y_HEIGHT][TILEMAP_X_WIDTH] = { + {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2}, // 第0行: 顶部边界 + {2,0,0,1,1,0,0,0,0,0,0,1,1,0,0,2}, // 第1行: 敌方出生点预留 + {2,0,0,1,1,0,0,0,0,0,0,1,1,0,0,2}, // 第2行 + {2,0,0,0,0,0,0,1,1,0,0,0,0,0,0,2}, + {2,0,0,0,0,0,0,1,1,0,0,0,0,0,0,2}, + {2,0,1,1,0,0,0,0,0,0,0,0,1,1,0,2}, + {2,0,1,1,0,0,0,0,0,0,0,0,1,1,0,2}, + {2,0,0,0,0,0,1,1,1,1,0,0,0,0,0,2}, // 中央障碍 + {2,0,0,0,0,0,1,0,0,1,0,0,0,0,0,2}, // 中央基地区域 + {2,0,1,1,0,0,1,0,0,1,0,0,1,1,0,2}, + {2,0,1,1,0,0,0,0,0,0,0,0,1,1,0,2}, + {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}, + {2,0,0,1,1,0,0,0,0,0,0,1,1,0,0,2}, // 第12行: 玩家出生点预留 + {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2}, // 第13行: 底部边界 +}; + +static inline void fill_render(void) { + // FIXME you can using ecs to store the entity + static ge_render_component_t render_wall = { + .type = GE_RENDER_COMP_TYPE_RECT, + .data.rect.size.x = TILEMAP_SIZE, + .data.rect.size.y = TILEMAP_SIZE, + .data.rect.color = GE_COLOR_GRAY, + }; + for (int y = 0; y < TILEMAP_Y_HEIGHT; y++) { + for (int x = 0; x < TILEMAP_X_WIDTH; x++) { + if (real_map[y][x] == 0) { + continue; + } + tilemap_render[y][x] = &render_wall; + tilemap_layers[y][x] = LAYER_WALL; + /** + * the global variable default value is NULL + * tilemap_render[y][x] = NULL; + */ + } + } +} + +void init_tilemap(ge_ecs_t* ecs, ge_entity_t **_tilemap) { + Assert(ecs != NULL && _tilemap != NULL); + ge_ecs_add_entity(ecs, _tilemap); + ge_entity_t* tilemap = *_tilemap; + Assert(tilemap != NULL); + + tilemap->component_mask = ( + GE_COMPONENT_ACVIVE | + GE_COMPONENT_POSITION | + GE_COMPONENT_RENDERABLE | + GE_COMPONENT_COLLIDER + ); + /** + * y = 16 + */ + tilemap->position = (ge_vector2i_t) {.x = 0, .y = 16}; + tilemap->renderable = (ge_render_component_t) { + .type = GE_RENDER_COMP_TYPE_TILEMAP, + .data.tilemap.tile_size = TILEMAP_SIZE, + .data.tilemap.map_size.x = TILEMAP_X_WIDTH, + .data.tilemap.map_size.y = TILEMAP_Y_HEIGHT, + .data.tilemap.components = (ge_render_component_t**)tilemap_render, + }; + tilemap->collision = (ge_collision_component_t) { + .type = GE_COLLISION_COMP_TYPE_TILEMAP, + .layers = LAYER_WALL, + .collider.tilemap = { + .tile_size = TILEMAP_SIZE, + .map_size = { TILEMAP_X_WIDTH, TILEMAP_Y_HEIGHT }, + .layers = (ge_layers_t*)tilemap_layers, + }, + }; + + fill_render(); +} diff --git a/game_core/entities/tilemap.h b/game_core/entities/tilemap.h new file mode 100644 index 0000000..6fa4244 --- /dev/null +++ b/game_core/entities/tilemap.h @@ -0,0 +1,15 @@ +#ifndef __TILEMAP_H__ +#define __TILEMAP_H__ + +#include "../ge_color.h" +#include "layer.h" +#include +#include + +#define TILEMAP_SIZE 8 +#define TILEMAP_X_WIDTH 16 +#define TILEMAP_Y_HEIGHT 14 + +void init_tilemap(ge_ecs_t* ecs, ge_entity_t **_tilemap); + +#endif // __TILEMAP_H__ diff --git a/game_core/main.c b/game_core/main.c index 0c9f736..3ad926e 100644 --- a/game_core/main.c +++ b/game_core/main.c @@ -1,64 +1,54 @@ #include #include #include +#include "config.h" #include "ge_color.h" - -#define GE_VEC2I_USE_SHORT_NAMES -#define _pynic_logout_printf(...) fprintf(fp , ##__VA_ARGS__) - -#include -// #include "plantform/win_term/interface.h" -FILE* fp; -logger_t logger; - -static void log_handler -(log_level_t level, const char* module, const char* file, int line, const char* message) { - fprintf(fp, "[%s] %s:%d | %s: %s\n", - pynic_level_str(level), file, line, module, message); - fflush(fp); -} - -#define MAX_BULLET 16 -#define BASIC_SPEED (1 << GE_PHYSICS_VELOCITY_BIT) +#include "entities/entities.h" typedef struct { + int score; ge_entity_t* player; - ge_entity_t* bullet[MAX_BULLET]; + ge_entity_t* bullets[MAX_BULLET]; ge_render_rect_t screen; + ge_entity_t* tilemap; } game_ctx_t; +void collision_response( + void* _ctx, + ge_entity_t* entity_from, + ge_entity_t* entity_to, + const ge_vector2i_t* vector // 反向推荐速度 +) { + game_ctx_t* ctx = _ctx; + (void) ctx; + (void) entity_to; + // For box to box collision + if (vector == NULL) return; + + switch (entity_from->user_type) { + case PLAYER: + case ENEMY: + entity_from->position = vec2i_add(entity_from->position, *vector); + break; + case BULLET: + entity_from->component_mask &= ~GE_COMPONENT_ACVIVE; + break; + default: + break; + } +} + void init(ge_core_t* core) { (void)core; - fp = fopen("D:\\Git_Code\\school_stm32\\game_core\\log.txt", "w+"); - init_logger_ex(&logger, "game", log_handler); - Assert(fp != NULL); static game_ctx_t ctx = { 0 }; - ge_ecs_add_entity(&core->ecs, &ctx.player); - ctx.player->component_mask = GE_COMPONENT_ACVIVE | GE_RENDERABLE_MASK; - ctx.player->position = (ge_vector2i_t){ 32, 32 }; - ctx.player->renderable = (ge_render_component_t) { - .type = GE_RENDER_COMPONENT_TYPE_RECT, - .data.rect = { - .size = {8, 8}, - .color = GE_COLOR_YELLOW, - } - }; + core->_systems.collision.ctx = &ctx; + core->_systems.collision.callback = collision_response; + init_tilemap(&core->ecs, &ctx.tilemap); + init_player(&core->ecs, &ctx.player); for (int i = 0; i < MAX_BULLET; ++i) { - ge_ecs_add_entity(&core->ecs, &ctx.bullet[i]); - ctx.bullet[i]->component_mask = GE_RENDERABLE_MASK | GE_PHYSICS_MASK; - ctx.bullet[i]->renderable = (ge_render_component_t) { - .type = GE_RENDER_COMPONENT_TYPE_RECT, - .data.rect = { - .size = {2, 2}, - .color = GE_COLOR_RED, - } - }; - ctx.bullet[i]->physics_body = (ge_physics_component_t) { - .type = GE_PHYSICS_COMPONENT_TYPE_VELOCITY, - .velocity = { 0, - BASIC_SPEED / 2}, - }; + init_bullet(&core->ecs, &ctx.bullets[i]); } ctx.screen = (ge_render_rect_t) { @@ -71,33 +61,40 @@ void init(ge_core_t* core) { void process(ge_core_t* core) { game_ctx_t* ctx = (game_ctx_t*)core->context; core->_render.func_draw_rect(&core->_render, &ctx->screen, GE_COLOR_WHITE); - ge_vector2i_t* pos = &ctx->player->position; + ge_vector2i_t* player_pos = &ctx->player->position; + ge_vector2i_t* player_vec = &ctx->player->physics_body.velocity; + player_vec->x = 0; + player_vec->y = 0; + core->_render.func_draw_text(&core->_render, &ctx->screen, "Press 'p' to exit", player_pos, GE_COLOR_BLACK); ge_input_event_t key; if (!core->_input.func_recv(&core->_input, &key)) { switch (key.num) { - case 'w': - MLOG_INFO(&logger, "w"); - *pos = vec2i_add(*pos, GE_VEC2I_UP); + case KEY_UP: + MLOG_INFO(&logger, "KEY_UP"); + player_vec->y = - BASIC_SPEED; break; - case 'a': - MLOG_INFO(&logger, "a"); - *pos = vec2i_add(*pos, GE_VEC2I_LEFT); + case KEY_DOWN: + MLOG_INFO(&logger, "KEY_DOWN"); + player_vec->y = BASIC_SPEED; break; - case 's': - MLOG_INFO(&logger, "s"); - *pos = vec2i_add(*pos, GE_VEC2I_DOWN); + case KEY_LEFT: + MLOG_INFO(&logger, "KEY_LEFT"); + player_vec->x = - BASIC_SPEED; break; - case 'd': - MLOG_INFO(&logger, "d"); - *pos = vec2i_add(*pos, GE_VEC2I_RIGHT); + case KEY_RIGHT: + MLOG_INFO(&logger, "KEY_RIGHT"); + player_vec->x = BASIC_SPEED; break; - case 'f': - ctx->bullet[0]->component_mask |= GE_COMPONENT_ACVIVE; - ctx->bullet[0]->position.x = pos->x + 4; // TODO - ctx->bullet[0]->position.y = pos->y; // TODO + case KEY_ATTACK: + ge_entity_t* bullet = ctx->bullets[0]; + bullet->component_mask |= GE_COMPONENT_ACVIVE; + bullet->position.x = player_pos->x + 4; // TODO + bullet->position.y = player_pos->y; // TODO + bullet->physics_body.velocity.x = 0; + bullet->physics_body.velocity.y = - BASIC_SPEED / 2; break; - case 'q': + case KEY_EXIT: core->state = GE_ENGINE_STATE_EXIT; MLOG_INFO(&logger, "exit"); break; diff --git a/game_core/plantform/win_app/win_input.c b/game_core/plantform/win_app/win_input.c new file mode 100644 index 0000000..0d38436 --- /dev/null +++ b/game_core/plantform/win_app/win_input.c @@ -0,0 +1,38 @@ +#include "win_interface.h" + +DECLARE_GE_KFIFO(ge_input_fifo, ge_input_event_t, 16, ge_input_fifo_t); + +static int ge_input_send (ge_input_t* ctx, ge_input_event_t event) { + struct ge_input_fifo_t* fifo = (struct ge_input_fifo_t*)ctx->context; + if (ge_kfifo_is_full(fifo)) { + return -1; + } + ge_kfifo_put(fifo, event); + return 0; +} + +static int ge_input_peek (ge_input_t* ctx, ge_input_event_t* event) { + struct ge_input_fifo_t* fifo = (struct ge_input_fifo_t*)ctx->context; + if (ge_kfifo_is_empty(fifo)) { + return -1; + } + ge_kfifo_peek(fifo, event); + return 0; +} + +static int ge_input_recv (ge_input_t* ctx, ge_input_event_t* event) { + struct ge_input_fifo_t* fifo = (struct ge_input_fifo_t*)ctx->context; + if (ge_kfifo_is_empty(fifo)) { + return -1; + } + ge_kfifo_get(fifo, event); + return 0; +} + +void win_init_input(ge_core_t* core) { + INIT_GE_KFIFO(&ge_input_fifo); + core->_input.context = &ge_input_fifo; + core->_input.func_send = ge_input_send; + core->_input.func_peek = ge_input_peek; + core->_input.func_recv = ge_input_recv; +} \ No newline at end of file diff --git a/game_core/plantform/win_app/win_interface.h b/game_core/plantform/win_app/win_interface.h index 8595ace..2cbbc27 100644 --- a/game_core/plantform/win_app/win_interface.h +++ b/game_core/plantform/win_app/win_interface.h @@ -1,10 +1,25 @@ #ifndef __WIN_INTERFACE_H__ #define __WIN_INTERFACE_H__ -#include +#include +#include +// 自定义渲染上下文结构体(包含双缓冲资源) +typedef struct { + HWND hwnd; // 窗口句柄 + HDC hdc_front; // 前台设备上下文 + HDC hdc_back; // 后台缓冲区设备上下文 + HBITMAP hbm_back; // 后台位图 + HBITMAP hbm_old; // 保存原始位图(用于恢复) + RECT client_rect; // 窗口客户区大小 +} win_render_context_t; -void win_sleep_us(ge_u32_t us); -ge_u32_t win_get_us(void); +#define WIN_HEIGHT 128 +#define WIN_WIDTH 128 +#define WIN_SCARE 4 + +void win_init_input(ge_core_t* core); +void win_init_timer(ge_core_t* core); +void win_init_render(ge_core_t* core, win_render_context_t* wctx); #endif // __WIN_INTERFACE_H__ diff --git a/game_core/plantform/win_app/win_main.c b/game_core/plantform/win_app/win_main.c index d6de4b5..f89332c 100644 --- a/game_core/plantform/win_app/win_main.c +++ b/game_core/plantform/win_app/win_main.c @@ -1,76 +1,4 @@ -#include #include "win_interface.h" -#include - - -// 自定义渲染上下文结构体(包含双缓冲资源) -typedef struct { - HWND hwnd; // 窗口句柄 - HDC hdc_front; // 前台设备上下文 - HDC hdc_back; // 后台缓冲区设备上下文 - HBITMAP hbm_back; // 后台位图 - HBITMAP hbm_old; // 保存原始位图(用于恢复) - RECT client_rect; // 窗口客户区大小 -} win_render_context_t; - -// 双缓冲初始化函数 -static void win_render_init(ge_render_t* ctx, const ge_render_pos2_t* init_screen_size) { - (void) init_screen_size; - win_render_context_t* wctx = (win_render_context_t*)ctx->context; - HDC hdc = GetDC(wctx->hwnd); - - // 获取窗口尺寸 - GetClientRect(wctx->hwnd, &wctx->client_rect); - int width = wctx->client_rect.right - wctx->client_rect.left; - int height = wctx->client_rect.bottom - wctx->client_rect.top; - - // 创建双缓冲资源 - wctx->hdc_front = hdc; - wctx->hdc_back = CreateCompatibleDC(hdc); - wctx->hbm_back = CreateCompatibleBitmap(hdc, width, height); - wctx->hbm_old = (HBITMAP)SelectObject(wctx->hdc_back, wctx->hbm_back); - - // 保存屏幕尺寸到渲染上下文 - ctx->screen_size.x = width; - ctx->screen_size.y = height; -} - -// 清空后台缓冲区 -static void win_render_draw_rect(ge_render_t* ctx, const ge_render_rect_t* rect, ge_render_color_t color) { - win_render_context_t* wctx = (win_render_context_t*)ctx->context; - RECT win_rect = { - .top = rect->pos.y, - .bottom = rect->pos.y + rect->size.y, - .left = rect->pos.x, - .right = rect->pos.x + rect->size.x, - }; - win_rect.bottom *= 4; - win_rect.right *= 4; - win_rect.left *= 4; - win_rect.top *= 4; - HBRUSH brush = CreateSolidBrush(color); // WHITE background - - FillRect(wctx->hdc_back, &win_rect, brush); - DeleteObject(brush); -} - -// 将后台缓冲区复制到屏幕(双缓冲交换) -static void win_render_flush(ge_render_t* ctx) { - win_render_context_t* wctx = (win_render_context_t*)ctx->context; - BitBlt(wctx->hdc_front, - 0, 0, - wctx->client_rect.right, - wctx->client_rect.bottom, - wctx->hdc_back, - 0, 0, - SRCCOPY); -} - -static void win_render_draw_text(ge_render_t* ctx, const ge_render_pos2_t* pos, const char* text) { - win_render_context_t* wctx = (win_render_context_t*)ctx->context; - // 使用GDI函数在 wctx->hdc_back 上绘制 - TextOut(wctx->hdc_back, pos->x, pos->y, text, strlen(text)); -} // 键盘事件处理函数(将按键转换为字符) static char win_key_to_char(WPARAM wParam, LPARAM lParam) { @@ -121,26 +49,6 @@ LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } } -static void win_render_getsize(ge_render_t* ctx, ge_vector2i_t* size) { - win_render_context_t* wctx = (win_render_context_t*)ctx->context; - - GetClientRect(wctx->hwnd, &wctx->client_rect); - ctx->screen_size.x = wctx->client_rect.right - wctx->client_rect.left; - ctx->screen_size.y = wctx->client_rect.bottom - wctx->client_rect.top; - - size->x = ctx->screen_size.x; - size->x = ctx->screen_size.y; -} - -// Windows计时函数 -static ge_u32_t win_get_ms(void* ctx) { - return GetTickCount(); -} - -static void win_sleep_ms(void* ctx, ge_u32_t ms) { - Sleep(ms); -} - static void win_run_at_frame(ge_core_t* core) { static MSG msg; const win_render_context_t* xctx = (win_render_context_t*)core->_inner_context; @@ -152,92 +60,42 @@ static void win_run_at_frame(ge_core_t* core) { } } -static void win_render_drawex(ge_render_t* ctx, ge_vector2i_t pos, const char* data, const void* propety) { - - win_render_context_t* wctx = (win_render_context_t*)ctx->context; - - // 将 data 转换为 HBITMAP (假设传入的是位图句柄) - HBITMAP hBitmap = (HBITMAP)data; - - // 创建兼容的内存设备上下文 - HDC hdcMem = CreateCompatibleDC(wctx->hdc_back); - - // 将位图选入内存设备上下文 - HBITMAP hOldBitmap = (HBITMAP)SelectObject(hdcMem, hBitmap); - - // 获取位图尺寸 - BITMAP bitmap; - GetObject(hBitmap, sizeof(BITMAP), &bitmap); - - // 绘制位图到后台缓冲区 - BitBlt( - wctx->hdc_back, // 目标设备上下文 - pos.x, // 目标左上角X - pos.y, // 目标左上角Y - bitmap.bmWidth, // 位图宽度 - bitmap.bmHeight, // 位图高度 - hdcMem, // 源设备上下文 - 0, // 源左上角X - 0, // 源左上角Y - SRCCOPY // 光栅操作码 - ); - - // 清理资源 - SelectObject(hdcMem, hOldBitmap); - DeleteDC(hdcMem); -} - // 主游戏逻辑函数 int ge_main(ge_core_t* core); -DECLARE_GE_KFIFO(ge_input_fifo, ge_input_event_t, 16, ge_input_fifo_t); - -static int ge_input_send (ge_input_t* ctx, ge_input_event_t event) { - struct ge_input_fifo_t* fifo = (struct ge_input_fifo_t*)ctx->context; - if (ge_kfifo_is_full(fifo)) { - return -1; - } - ge_kfifo_put(fifo, event); - return 0; -} - -static int ge_input_peek (ge_input_t* ctx, ge_input_event_t* event) { - struct ge_input_fifo_t* fifo = (struct ge_input_fifo_t*)ctx->context; - if (ge_kfifo_is_empty(fifo)) { - return -1; - } - ge_kfifo_peek(fifo, event); - return 0; -} - -static int ge_input_recv (ge_input_t* ctx, ge_input_event_t* event) { - struct ge_input_fifo_t* fifo = (struct ge_input_fifo_t*)ctx->context; - if (ge_kfifo_is_empty(fifo)) { - return -1; - } - ge_kfifo_get(fifo, event); - return 0; -} // Windows入口点 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { (void) hPrevInstance; (void) lpCmdLine; - // 创建窗口 - wchar_t CLASS_NAME[] = L"GameWindow"; WNDCLASS wc = {0}; wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; - wc.lpszClassName = CLASS_NAME; + wc.lpszClassName = TEXT("GameWindow"); RegisterClass(&wc); + // 计算正确的窗口尺寸 ===================================== + RECT clientRect = {0}; + clientRect.right = WIN_WIDTH * WIN_SCARE; // 期望的客户区宽度 + clientRect.bottom = WIN_HEIGHT * WIN_SCARE; // 期望的客户区高度 + + // 使用 AdjustWindowRectEx 计算窗口尺寸(遵循微软建议) + DWORD dwExStyle = 0; // 无扩展样式 + DWORD dwStyle = WS_OVERLAPPEDWINDOW; + AdjustWindowRectEx(&clientRect, dwStyle, FALSE, dwExStyle); + + int windowWidth = clientRect.right - clientRect.left; + int windowHeight = clientRect.bottom - clientRect.top; + // ====================================================== + HWND hwnd = CreateWindowEx( - 0, - CLASS_NAME, - "Game Engine", - WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, CW_USEDEFAULT, 128 * 4, 128 * 4, + dwExStyle, // 使用计算时的扩展样式 + wc.lpszClassName, + TEXT("Game Engine"), + dwStyle, + CW_USEDEFAULT, CW_USEDEFAULT, + windowWidth, windowHeight, // 使用计算出的窗口尺寸 NULL, NULL, hInstance, NULL ); @@ -250,33 +108,15 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine // 创建双缓冲上下文 win_render_context_t* wctx = (win_render_context_t*)malloc(sizeof(win_render_context_t)); wctx->hwnd = hwnd; - - // 配置渲染器 - core._render.context = wctx; - core._render.init_func = (ge_render_init_func_t)win_render_init; - core._render.func_flush = (ge_render_flush_func_t)win_render_flush; - core._render.func_draw_text = (ge_render_draw_text_func_t)win_render_draw_text; - core._render.func_draw_rect = (ge_render_draw_rect_func_t)win_render_draw_rect; - - // // 初始化渲染器 - // if (core->render.init) core->render.init(&core->render); - - // 配置计时器 - core._timer.func_getms = (ge_timer_getms_func_t)win_get_ms; - core._timer.func_sleepms = (ge_timer_sleepms_func_t)win_sleep_ms; + win_init_render(&core, wctx); + win_init_timer(&core); + win_init_input(&core); core._inner_run = win_run_at_frame; - core._inner_context = wctx; - - INIT_GE_KFIFO(&ge_input_fifo); - core._input.context = &ge_input_fifo; - core._input.func_send = ge_input_send; - core._input.func_peek = ge_input_peek; - core._input.func_recv = ge_input_recv; + core._inner_context = (void*)wctx; // 将core指针关联到窗口 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&core); - ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); // 运行主游戏逻辑 diff --git a/game_core/plantform/win_app/win_render.c b/game_core/plantform/win_app/win_render.c new file mode 100644 index 0000000..50b4dc3 --- /dev/null +++ b/game_core/plantform/win_app/win_render.c @@ -0,0 +1,126 @@ +#include "win_interface.h" + +// 双缓冲初始化函数 +static int win_render_init(ge_render_t* ctx, const ge_render_pos2_t* init_screen_size) { + (void) init_screen_size; + win_render_context_t* wctx = (win_render_context_t*)ctx->context; + HDC hdc = GetDC(wctx->hwnd); + + // 获取窗口尺寸 + GetClientRect(wctx->hwnd, &wctx->client_rect); + int width = wctx->client_rect.right - wctx->client_rect.left; + int height = wctx->client_rect.bottom - wctx->client_rect.top; + + // 创建双缓冲资源 + wctx->hdc_front = hdc; + wctx->hdc_back = CreateCompatibleDC(hdc); + wctx->hbm_back = CreateCompatibleBitmap(hdc, width, height); + wctx->hbm_old = (HBITMAP)SelectObject(wctx->hdc_back, wctx->hbm_back); + + // 保存屏幕尺寸到渲染上下文 + ctx->screen_size.x = width; + ctx->screen_size.y = height; + return 0; +} + +// 清空后台缓冲区 +static int win_render_draw_rect(ge_render_t* ctx, const ge_render_rect_t* rect, ge_render_color_t color) { + win_render_context_t* wctx = (win_render_context_t*)ctx->context; + RECT win_rect = { + .top = rect->pos.y, + .bottom = rect->pos.y + rect->size.y, + .left = rect->pos.x, + .right = rect->pos.x + rect->size.x, + }; + win_rect.bottom *= WIN_SCARE; + win_rect.right *= WIN_SCARE; + win_rect.left *= WIN_SCARE; + win_rect.top *= WIN_SCARE; + HBRUSH brush = CreateSolidBrush(color); // WHITE background + + FillRect(wctx->hdc_back, &win_rect, brush); + DeleteObject(brush); + return 0; +} + +// 将后台缓冲区复制到屏幕(双缓冲交换) +static int win_render_flush(ge_render_t* ctx) { + win_render_context_t* wctx = (win_render_context_t*)ctx->context; + BitBlt(wctx->hdc_front, + 0, 0, + wctx->client_rect.right, + wctx->client_rect.bottom, + wctx->hdc_back, + 0, 0, + SRCCOPY); + return 0; +} + +static int win_render_draw_text ( + ge_render_t* ctx, + const ge_render_pos2_t* pos, + ge_render_unit_t font_size, + ge_render_color_t font_color, + const char* text) +{ + win_render_context_t* wctx = (win_render_context_t*)ctx->context; + // 使用GDI函数在 wctx->hdc_back 上绘制 + TextOut(wctx->hdc_back, pos->x, pos->y, text, strlen(text)); + return 0; +} + +void win_init_render(ge_core_t* core, win_render_context_t* wctx) { + core->_render.context = wctx; + core->_render.init_func = win_render_init; + core->_render.func_flush = win_render_flush; + core->_render.func_draw_text = win_render_draw_text; + core->_render.func_draw_rect = win_render_draw_rect; +} + +__attribute__ ((__unused__)) +static void win_render_drawex(ge_render_t* ctx, ge_vector2i_t pos, const char* data, const void* propety) { + (void) propety; + win_render_context_t* wctx = (win_render_context_t*)ctx->context; + + // 将 data 转换为 HBITMAP (假设传入的是位图句柄) + HBITMAP hBitmap = (HBITMAP)data; + + // 创建兼容的内存设备上下文 + HDC hdcMem = CreateCompatibleDC(wctx->hdc_back); + + // 将位图选入内存设备上下文 + HBITMAP hOldBitmap = (HBITMAP)SelectObject(hdcMem, hBitmap); + + // 获取位图尺寸 + BITMAP bitmap; + GetObject(hBitmap, sizeof(BITMAP), &bitmap); + + // 绘制位图到后台缓冲区 + BitBlt( + wctx->hdc_back, // 目标设备上下文 + pos.x, // 目标左上角X + pos.y, // 目标左上角Y + bitmap.bmWidth, // 位图宽度 + bitmap.bmHeight, // 位图高度 + hdcMem, // 源设备上下文 + 0, // 源左上角X + 0, // 源左上角Y + SRCCOPY // 光栅操作码 + ); + + // 清理资源 + SelectObject(hdcMem, hOldBitmap); + DeleteDC(hdcMem); +} + +__attribute__ ((__unused__)) +static void win_render_getsize(ge_render_t* ctx, ge_vector2i_t* size) { + win_render_context_t* wctx = (win_render_context_t*)ctx->context; + + GetClientRect(wctx->hwnd, &wctx->client_rect); + ctx->screen_size.x = wctx->client_rect.right - wctx->client_rect.left; + ctx->screen_size.y = wctx->client_rect.bottom - wctx->client_rect.top; + + size->x = ctx->screen_size.x; + size->x = ctx->screen_size.y; +} diff --git a/game_core/plantform/win_app/win_timer.c b/game_core/plantform/win_app/win_timer.c index c496cf0..28cf352 100644 --- a/game_core/plantform/win_app/win_timer.c +++ b/game_core/plantform/win_app/win_timer.c @@ -1,5 +1,15 @@ -#include -#include +#include "win_interface.h" +// Windows计时函数 +#include +static ge_time_t win_get_ms(ge_timer_t* ctx) { + (void) ctx; + return GetTickCount(); +} + +static void win_sleep_ms(const ge_timer_t* ctx, ge_time_t ms) { + (void) ctx; + Sleep(ms); +} // 获取当前时间(微秒) ge_u32_t win_get_us(void) { @@ -28,4 +38,13 @@ void win_sleep_us(ge_u32_t us) { // 长时间使用Sleep Sleep(us / 1000); } -} \ No newline at end of file +} + +void win_init_timer(ge_core_t* core) { + core->_timer.time = 0; + core->_timer.func_sleepms = win_sleep_ms; + core->_timer.func_getms = win_get_ms; + core->_timer.func_sleepus = NULL; + core->_timer.func_getus = NULL; +} + diff --git a/game_core/tilemap.c b/game_core/tilemap.c deleted file mode 100644 index 20b7887..0000000 --- a/game_core/tilemap.c +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __TILEMAP_H__ -#define __TILEMAP_H__ - -typedef struct { - -}; - - -#endif // __TILE_MAP_H__ diff --git a/game_engine/ecs/ge_collision_component.h b/game_engine/ecs/ge_collision_component.h new file mode 100644 index 0000000..5146876 --- /dev/null +++ b/game_engine/ecs/ge_collision_component.h @@ -0,0 +1,34 @@ +#ifndef __GE_COLLISION_COMPONENT_H__ +#define __GE_COLLISION_COMPONENT_H__ + +#include +typedef unsigned char ge_layers_t; + +typedef enum { + GE_COLLISION_COMP_TYPE_NONE, + GE_COLLISION_COMP_TYPE_BOX, + GE_COLLISION_COMP_TYPE_TILEMAP, +} ge_collision_type_t; + +typedef struct ge_collision_box { + ge_vector2i_t relative_pos; + ge_vector2i_t size; +} ge_collision_box_t; + +typedef struct ge_collision_tilemap { + ge_vector2i_t map_size; + ge_unit_t tile_size; + ge_layers_t* layers; +} ge_collision_tilemap_t; + +typedef struct ge_collision_component { + ge_collision_type_t type; + ge_layers_t layers; + ge_layers_t mask; + struct { + ge_collision_box_t box; + ge_collision_tilemap_t tilemap; + } collider; +} ge_collision_component_t; + +#endif /* __GE_COLLISION_COMPONENT_H__ */ diff --git a/game_engine/ecs/ge_collision_system.h b/game_engine/ecs/ge_collision_system.h new file mode 100644 index 0000000..731db11 --- /dev/null +++ b/game_engine/ecs/ge_collision_system.h @@ -0,0 +1,186 @@ +#ifndef __GE_COLLISION_SYSTEM_H__ +#define __GE_COLLISION_SYSTEM_H__ + +#include "ge_entity.h" +#include "ge_collision_component.h" +#include +#include +#include + + +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__ */ diff --git a/game_engine/ecs/ge_entity.h b/game_engine/ecs/ge_entity.h index 115f470..3b9ba6b 100644 --- a/game_engine/ecs/ge_entity.h +++ b/game_engine/ecs/ge_entity.h @@ -1,10 +1,10 @@ #ifndef __GE_ENTIRY_H__ #define __GE_ENTIRY_H__ -#include #include #include "ge_render_component.h" #include "ge_physics_component.h" +#include "ge_collision_component.h" #include typedef uint16_t ge_ecs_id_t; // 支持65536个实体 @@ -15,8 +15,8 @@ typedef enum { GE_COMPONENT_POSITION = 1 << 1, GE_COMPONENT_TRANSFORM = 1 << 2, // TODO not implimented GE_COMPONENT_RENDERABLE = 1 << 3, - GE_COMPONENT_PHYSICS_BODY = 1 << 4, // TODO not implimented - GE_COMPONENT_COLLIDER = 1 << 5, // TODO not implimented + GE_COMPONENT_PHYSICS_BODY = 1 << 4, + GE_COMPONENT_COLLIDER = 1 << 5, GE_COMPONENT_TIMED_LIFE = 1 << 6, // TODO not implimented } ge_ecs_mask_t; @@ -24,13 +24,17 @@ typedef enum { (GE_COMPONENT_POSITION | GE_COMPONENT_RENDERABLE) #define GE_PHYSICS_MASK \ (GE_COMPONENT_POSITION | GE_COMPONENT_PHYSICS_BODY) +#define GE_COLLIDER_MASK \ + (GE_COMPONENT_POSITION | GE_COMPONENT_COLLIDER) typedef struct ge_entity { + int user_type; ge_ecs_id_t id; ge_ecs_mask_t component_mask; ge_vector2i_t position; ge_render_component_t renderable; ge_physics_component_t physics_body; + ge_collision_component_t collision; } ge_entity_t; typedef struct { @@ -43,7 +47,6 @@ typedef struct ge_ecs { ge_ecs_storage_t storage; } ge_ecs_t; - static inline ge_ecs_id_t ge_ecs_add_entity(ge_ecs_t* ecs, ge_entity_t** entity) { ge_ecs_id_t id = ++ecs->storage.count; if (id >= GE_ECS_MAX) { diff --git a/game_engine/ecs/ge_physics_component.h b/game_engine/ecs/ge_physics_component.h index 5218715..b3a127d 100644 --- a/game_engine/ecs/ge_physics_component.h +++ b/game_engine/ecs/ge_physics_component.h @@ -7,9 +7,9 @@ #define GE_PHYSICS_ACCELERATION_BIT 3 typedef enum { - GE_PHYSICS_COMPONENT_TYPE_NONE = 1 << 0, - GE_PHYSICS_COMPONENT_TYPE_VELOCITY = 1 << 1, - GE_PHYSICS_COMPONENT_TYPE_ACCELERATION = 1 << 2, + GE_PHYSICS_COMP_TYPE_NONE = 1 << 0, + GE_PHYSICS_COMP_TYPE_VELOCITY = 1 << 1, + GE_PHYSICS_COMP_TYPE_ACCELERATION = 1 << 2, } ge_physics_component_type_t; typedef struct ge_physics_component { diff --git a/game_engine/ecs/ge_physics_system.h b/game_engine/ecs/ge_physics_system.h index 6d02c9d..bd37a1d 100644 --- a/game_engine/ecs/ge_physics_system.h +++ b/game_engine/ecs/ge_physics_system.h @@ -25,11 +25,11 @@ ge_physics_system_run_all(ge_physics_system_t* ctx) { Assert(comp != NULL); ge_physics_component_type_t type= comp->type; - if (type & GE_PHYSICS_COMPONENT_TYPE_ACCELERATION) { + if (type & GE_PHYSICS_COMP_TYPE_ACCELERATION) { comp->velocity.x += comp->acceleration.x >> GE_PHYSICS_ACCELERATION_BIT; comp->velocity.y += comp->acceleration.y >> GE_PHYSICS_ACCELERATION_BIT; } - if (type & GE_PHYSICS_COMPONENT_TYPE_VELOCITY) { + if (type & GE_PHYSICS_COMP_TYPE_VELOCITY) { entity->position.x += comp->velocity.x >> GE_PHYSICS_VELOCITY_BIT; entity->position.y += comp->velocity.y >> GE_PHYSICS_VELOCITY_BIT; } diff --git a/game_engine/ecs/ge_render_component.h b/game_engine/ecs/ge_render_component.h index 8cf647b..d7e5ec3 100644 --- a/game_engine/ecs/ge_render_component.h +++ b/game_engine/ecs/ge_render_component.h @@ -4,14 +4,21 @@ #include typedef enum { - GE_RENDER_COMPONENT_TYPE_NONE, - GE_RENDER_COMPONENT_TYPE_POINT, - GE_RENDER_COMPONENT_TYPE_TEXT, - GE_RENDER_COMPONENT_TYPE_RECT, - GE_RENDER_COMPONENT_TYPE_RECOURCE, + GE_RENDER_COMP_TYPE_NONE, + GE_RENDER_COMP_TYPE_POINT, + GE_RENDER_COMP_TYPE_TEXT, + GE_RENDER_COMP_TYPE_RECT, + GE_RENDER_COMP_TYPE_RECOURCE, + + GE_RENDER_COMP_TYPE_TILEMAP, + + GE_RENDER_COMP_TYPE_SIZE, /* never used, only for calulate size */ } ge_render_component_type_t; -typedef struct ge_render_component { +struct ge_render_component; +typedef struct ge_render_component ge_render_component_t; + +struct ge_render_component { ge_render_component_type_t type; union { struct { @@ -21,7 +28,12 @@ typedef struct ge_render_component { ge_render_pos2_t size; ge_render_color_t color; } rect; + struct { + ge_render_pos2_t map_size; // number of tiles + ge_render_unit_t tile_size; + ge_render_component_t** components; // not be tilemap + } tilemap; } data; -} ge_render_component_t; +}; #endif // __GE_RENDER_COMPONENT_H__ diff --git a/game_engine/ecs/ge_render_system.h b/game_engine/ecs/ge_render_system.h index f1320a7..524ad92 100644 --- a/game_engine/ecs/ge_render_system.h +++ b/game_engine/ecs/ge_render_system.h @@ -18,6 +18,43 @@ ge_render_system_init(ge_render_system_t* ctx, ge_render_t* render) { #define _GE_RENDERABLE_MASK (GE_RENDERABLE_MASK | GE_COMPONENT_ACVIVE) +static inline void +ge_render_system_draw_basic( +ge_render_system_t* ctx, +ge_render_component_t* comp, +ge_render_pos2_t* pos) { + Assert(comp->type < GE_RENDER_COMP_TYPE_SIZE); + switch (comp->type) { + case GE_RENDER_COMP_TYPE_POINT: + ctx->render->func_draw_point( + ctx->render, + pos, + comp->data.point.color + ); + break; + case GE_RENDER_COMP_TYPE_RECT: + ctx->render->func_draw_rect( + ctx->render, + &(ge_render_rect_t) { + *pos, + comp->data.rect.size, + }, + comp->data.rect.color + ); + break; + case GE_RENDER_COMP_TYPE_TEXT: + TODO(); + break; + case GE_RENDER_COMP_TYPE_RECOURCE: + TODO(); + break; + default: + LOG_WARN("render component not set Avaliable type %d at [%d,%d]", + comp->type, pos->x, pos->y); + break; + } +} + static inline void ge_render_system_draw_all(ge_render_system_t* ctx) { Assert(ctx != NULL); @@ -35,33 +72,22 @@ ge_render_system_draw_all(ge_render_system_t* ctx) { ge_render_component_t* comp = &entity->renderable; Assert(comp != NULL); - switch (comp->type) { - case GE_RENDER_COMPONENT_TYPE_POINT: - ctx->render->func_draw_point( - ctx->render, - &pos, - comp->data.point.color - ); - break; - case GE_RENDER_COMPONENT_TYPE_RECT: - ctx->render->func_draw_rect( - ctx->render, - &(ge_render_rect_t) { - pos, - comp->data.rect.size, - }, - comp->data.rect.color - ); - break; - case GE_RENDER_COMPONENT_TYPE_TEXT: - TODO(); - break; - case GE_RENDER_COMPONENT_TYPE_RECOURCE: - TODO(); - break; - default: - LOG_WARN("render component not set Avaliable type %d, id %d", comp->type, i); - break; + if (comp->type != GE_RENDER_COMP_TYPE_TILEMAP) { + ge_render_system_draw_basic(ctx, comp, &pos); + continue; + } + + for (int y = 0; y < comp->data.tilemap.map_size.y; y++) { + for (int x = 0; x < comp->data.tilemap.map_size.x; x++, pos.x += comp->data.tilemap.tile_size) { + ge_render_component_t* tile = (comp->data.tilemap.components) + [y * comp->data.tilemap.map_size.x + x]; + if (tile == NULL || tile->type == GE_RENDER_COMP_TYPE_NONE) { + continue; + } + ge_render_system_draw_basic(ctx, tile, &pos); + } + pos.x -= comp->data.tilemap.map_size.x * comp->data.tilemap.tile_size; + pos.y += comp->data.tilemap.tile_size; } } } diff --git a/game_engine/events/ge_events_bus.h b/game_engine/events/ge_events_bus.h deleted file mode 100644 index 8777d45..0000000 --- a/game_engine/events/ge_events_bus.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef __GE_EVENTS_BUS_H__ -#define __GE_EVENTS_BUS_H__ - -#include - -typedef struct { - DECLARE_GE_KFIFO(event_queue, void*, 16, ge_queue_t); -} ge_event_queue_t; - -#endif // __GE_EVENTS_BUS_H__ diff --git a/game_engine/ge_core.c b/game_engine/ge_core.c index 99c4a99..535ebff 100644 --- a/game_engine/ge_core.c +++ b/game_engine/ge_core.c @@ -29,10 +29,11 @@ static inline void ge_init(ge_core_t* core) { } ge_render_system_init(&core->_systems.render, &core->_render); // TODO using other storage system - core->_systems.render.ecs = &core->ecs.storage; core->ecs.storage.count = 0; - + + core->_systems.render.ecs = &core->ecs.storage; core->_systems.physics.ecs = &core->ecs.storage; + core->_systems.collision.ecs = &core->ecs.storage; } void ge_engine_run(ge_core_t *core) { @@ -45,6 +46,7 @@ void ge_engine_run(ge_core_t *core) { ge_physics_system_run_all(&core->_systems.physics); GE_SAFE_CALL(core->callbacks.process, core); + ge_collision_system_run_all(&core->_systems.collision); ge_render_system_draw_all(&core->_systems.render); GE_SAFE_CALL(core->_render.func_flush, &core->_render); diff --git a/game_engine/ge_core.h b/game_engine/ge_core.h index a0d460e..8b28ab9 100644 --- a/game_engine/ge_core.h +++ b/game_engine/ge_core.h @@ -5,6 +5,7 @@ #include #include #include +#include struct ge_engine_core; typedef struct ge_engine_core ge_core_t; @@ -38,6 +39,7 @@ struct ge_engine_core { struct { ge_render_system_t render; ge_physics_system_t physics; + ge_collision_system_t collision; } _systems; ge_render_t _render; ge_timer_t _timer; diff --git a/game_engine/interface/ge_render.h b/game_engine/interface/ge_render.h index e45dd78..6b5eac6 100644 --- a/game_engine/interface/ge_render.h +++ b/game_engine/interface/ge_render.h @@ -22,10 +22,18 @@ typedef struct ge_render ge_render_t; typedef int(*ge_render_init_func_t) (ge_render_t* ctx, const ge_render_pos2_t* init_screen_size); typedef int(*ge_render_flush_func_t) (ge_render_t* ctx); -typedef int(*ge_render_draw_point_func_t) (ge_render_t* ctx, const ge_render_pos2_t* pos, ge_render_color_t color); -typedef int(*ge_render_draw_rect_func_t) (ge_render_t* ctx, const ge_render_rect_t* rect, ge_render_color_t color); -typedef int(*ge_render_draw_text_func_t) (ge_render_t* ctx, const ge_render_pos2_t* pos, const char* text); -typedef int(*ge_render_draw_resource_func_t)(ge_render_t* ctx, const ge_render_pos2_t* pos, const ge_resource_t* res); +typedef int(*ge_render_draw_point_func_t) + (ge_render_t* ctx, const ge_render_pos2_t* pos, ge_render_color_t color); +typedef int(*ge_render_draw_rect_func_t) + (ge_render_t* ctx, const ge_render_rect_t* rect, ge_render_color_t color); +typedef int(*ge_render_draw_text_func_t) ( + ge_render_t* ctx, + const ge_render_pos2_t* pos, + ge_render_unit_t font_size, + ge_render_color_t font_color, + const char* text); +typedef int(*ge_render_draw_resource_func_t) + (ge_render_t* ctx, const ge_render_pos2_t* pos, const ge_resource_t* res); struct ge_render { void* context; diff --git a/game_engine/physics/ge_collision_box.h b/game_engine/physics/ge_collision_box.h deleted file mode 100644 index 12f848c..0000000 --- a/game_engine/physics/ge_collision_box.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __GE_COLLISION_H__ -#define __GE_COLLISION_H__ - -#include "ge_physics.h" - -typedef struct ge_collision { - ge_phy_layers_t layers; - ge_phy_layers_t mask; - ge_vector2i_t position; - ge_vector2i_t size; -} ge_phy_box_t; - -static inline int check_box_collision(ge_phy_box_t* a, ge_phy_box_t* b) { - return (a->position.x < b->position.x + b->size.x) && - (a->position.x + a->size.x > b->position.x) && - (a->position.y < b->position.y + b->size.y) && - (a->position.y + a->size.y > b->position.y); -} - -#endif // __GE_COLLISION_H__ diff --git a/game_engine/physics/ge_collision_tilemap.h b/game_engine/physics/ge_collision_tilemap.h deleted file mode 100644 index ed6fa84..0000000 --- a/game_engine/physics/ge_collision_tilemap.h +++ /dev/null @@ -1,58 +0,0 @@ - -#include "ge_physics.h" -#include "ge_collision_box.h" - -typedef struct { - ge_vector2i_t position; - ge_vector2i_t size; - ge_int_t tile_size; - ge_phy_layers_t* layers; // 二维layer数组 -} ge_phy_tilemap_t; - -static inline int check_tilemap_collision(ge_phy_tilemap_t* tilemap, ge_phy_box_t* entity_collision) { - Assert(tilemap->tile_size > 0); - const int tile_size_int = tilemap->tile_size; - - // 计算瓦片地图的网格尺寸(列数和行数) - const int grid_cols = tilemap->size.x / tile_size_int; - const int grid_rows = tilemap->size.y / tile_size_int; - - // 计算实体碰撞盒在瓦片地图局部坐标系中的位置 - const ge_vector2i_t local_entity_pos = GE_VEC2I_SUB( - entity_collision->position, - tilemap->position - ); - - // 计算实体边界(使用闭区间) - 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 - 1; // 闭区间右边界 - const int entity_bottom = entity_top + entity_collision->size.y - 1; // 闭区间下边界 - - // 计算覆盖的瓦片范围 - 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); - - // 遍历实体覆盖的瓦片区域 - for (int y = start_tile_y; y <= end_tile_y; y++) { - for (int x = start_tile_x; x <= end_tile_x; x++) { - // 计算当前瓦片的边界(开区间) - 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 && tilemap->layers[y * grid_cols + x] != 0) { - return 1; // 检测到碰撞 - } - } - } - - return 0; // 无碰撞 -} diff --git a/game_engine/physics/ge_collison.h b/game_engine/physics/ge_collison.h deleted file mode 100644 index 3ac9be5..0000000 --- a/game_engine/physics/ge_collison.h +++ /dev/null @@ -1,27 +0,0 @@ - - -// // 在 ge_common.h 中定义通用碰撞类型 -// typedef enum ge_collision_type { -// GE_COLLISION_TYPE_BOX, // 实体间碰撞 -// GE_COLLISION_TYPE_TILEMAP // 实体与瓦片地图碰撞 -// } ge_collision_type_t; - -// // 通用碰撞数据结构 -// typedef struct { -// ge_collision_type_t type; -// void* entityA; // 主要实体(通常是被检测的实体) -// void* entityB; // 对于BOX碰撞,这是另一个实体;对于TILEMAP,这是瓦片地图 -// ge_vector2i_t collision_point; -// union { -// struct { -// int tile_x; -// int tile_y; -// } tilemap_data; // 瓦片地图碰撞特有数据 -// struct { -// // 可以添加实体间碰撞特有数据 -// } box_data; -// } specific; -// } ge_collision_event_t; - -// // 碰撞回调函数类型 -// typedef void (*ge_collision_callback_t)(ge_collision_event_t* event); diff --git a/game_engine/physics/ge_physics.h b/game_engine/physics/ge_physics.h deleted file mode 100644 index 8a7e53d..0000000 --- a/game_engine/physics/ge_physics.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __GE_PHYSICS_H__ -#define __GE_PHYSICS_H__ - -#include - -typedef ge_u8_t ge_phy_layers_t; - -#endif // __GE_PHYSICS_H__ diff --git a/game_engine/utils/ge_vector2i.h b/game_engine/utils/ge_vector2i.h index 1924410..7cd8b22 100644 --- a/game_engine/utils/ge_vector2i.h +++ b/game_engine/utils/ge_vector2i.h @@ -7,6 +7,8 @@ * @brief 2D整数向量库 (仿Godot Vector2i设计) 不依赖任何库的单文件 * @defgroup Vector2 2D向量操作 */ +#include +typedef int32_t ge_unit_t; /**< 坐标值类型定义 */ // #define GE_VEC2I_USE_SHORT_NAMES @@ -14,7 +16,6 @@ #undef GE_ABS #define GE_ABS(x) ((x) > 0 ? (x) : -(x)) #endif -typedef int32_t ge_unit_t; /**< 坐标值类型定义 */ /** * @struct ge_vector2_t