feat(game_core): 重构游戏引擎并添加新功能
- 重构了游戏引擎的核心逻辑和架构 - 添加了新的实体组件系统(ECS) - 实现了简单的碰撞检测和响应 - 新增了地图和子弹功能 - 优化了输入处理和渲染逻辑 - 调整了游戏控制方式
This commit is contained in:
@@ -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)
|
||||
|
||||
46
game_core/config.h
Normal file
46
game_core/config.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef __CONFIG_H__
|
||||
#define __CONFIG_H__
|
||||
|
||||
/**
|
||||
* only for main can include
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define _pynic_logout_printf(...) fprintf(fp , ##__VA_ARGS__)
|
||||
#define GE_VEC2I_USE_SHORT_NAMES
|
||||
#include <ge_core.h>
|
||||
/**
|
||||
* 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__ */
|
||||
33
game_core/entities/bullet.c
Normal file
33
game_core/entities/bullet.c
Normal file
@@ -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 },
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
0
game_core/entities/enemy.c
Normal file
0
game_core/entities/enemy.c
Normal file
19
game_core/entities/entities.h
Normal file
19
game_core/entities/entities.h
Normal file
@@ -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
|
||||
13
game_core/entities/layer.h
Normal file
13
game_core/entities/layer.h
Normal file
@@ -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__ */
|
||||
32
game_core/entities/player.c
Normal file
32
game_core/entities/player.c
Normal file
@@ -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},
|
||||
}
|
||||
};
|
||||
}
|
||||
81
game_core/entities/tilemap.c
Normal file
81
game_core/entities/tilemap.c
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "tilemap.h"
|
||||
#include <ecs/ge_render_component.h>
|
||||
#include <ecs/ge_collision_component.h>
|
||||
|
||||
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();
|
||||
}
|
||||
15
game_core/entities/tilemap.h
Normal file
15
game_core/entities/tilemap.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef __TILEMAP_H__
|
||||
#define __TILEMAP_H__
|
||||
|
||||
#include "../ge_color.h"
|
||||
#include "layer.h"
|
||||
#include <ecs/ge_entity.h>
|
||||
#include <pynic_log/pynic_log.h>
|
||||
|
||||
#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__
|
||||
123
game_core/main.c
123
game_core/main.c
@@ -1,64 +1,54 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "config.h"
|
||||
#include "ge_color.h"
|
||||
|
||||
#define GE_VEC2I_USE_SHORT_NAMES
|
||||
#define _pynic_logout_printf(...) fprintf(fp , ##__VA_ARGS__)
|
||||
|
||||
#include <ge_core.h>
|
||||
// #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;
|
||||
|
||||
38
game_core/plantform/win_app/win_input.c
Normal file
38
game_core/plantform/win_app/win_input.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -1,10 +1,25 @@
|
||||
#ifndef __WIN_INTERFACE_H__
|
||||
#define __WIN_INTERFACE_H__
|
||||
|
||||
#include <ge_common.h>
|
||||
#include <windows.h>
|
||||
#include <ge_core.h>
|
||||
|
||||
// 自定义渲染上下文结构体(包含双缓冲资源)
|
||||
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__
|
||||
|
||||
@@ -1,76 +1,4 @@
|
||||
#include <windows.h>
|
||||
#include "win_interface.h"
|
||||
#include <ge_core.h>
|
||||
|
||||
|
||||
// 自定义渲染上下文结构体(包含双缓冲资源)
|
||||
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);
|
||||
// 运行主游戏逻辑
|
||||
|
||||
126
game_core/plantform/win_app/win_render.c
Normal file
126
game_core/plantform/win_app/win_render.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -1,5 +1,15 @@
|
||||
#include <windows.h>
|
||||
#include <ge_common.h>
|
||||
#include "win_interface.h"
|
||||
// Windows计时函数
|
||||
#include <stdint.h>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#ifndef __TILEMAP_H__
|
||||
#define __TILEMAP_H__
|
||||
|
||||
typedef struct {
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // __TILE_MAP_H__
|
||||
34
game_engine/ecs/ge_collision_component.h
Normal file
34
game_engine/ecs/ge_collision_component.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef __GE_COLLISION_COMPONENT_H__
|
||||
#define __GE_COLLISION_COMPONENT_H__
|
||||
|
||||
#include <utils/ge_vector2i.h>
|
||||
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__ */
|
||||
186
game_engine/ecs/ge_collision_system.h
Normal file
186
game_engine/ecs/ge_collision_system.h
Normal file
@@ -0,0 +1,186 @@
|
||||
#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__ */
|
||||
@@ -1,10 +1,10 @@
|
||||
#ifndef __GE_ENTIRY_H__
|
||||
#define __GE_ENTIRY_H__
|
||||
|
||||
#include <ge_core.h>
|
||||
#include <utils/ge_vector2i.h>
|
||||
#include "ge_render_component.h"
|
||||
#include "ge_physics_component.h"
|
||||
#include "ge_collision_component.h"
|
||||
|
||||
#include <stdint.h>
|
||||
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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -4,14 +4,21 @@
|
||||
#include <interface/ge_render.h>
|
||||
|
||||
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__
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#ifndef __GE_EVENTS_BUS_H__
|
||||
#define __GE_EVENTS_BUS_H__
|
||||
|
||||
#include <ge_common.h>
|
||||
|
||||
typedef struct {
|
||||
DECLARE_GE_KFIFO(event_queue, void*, 16, ge_queue_t);
|
||||
} ge_event_queue_t;
|
||||
|
||||
#endif // __GE_EVENTS_BUS_H__
|
||||
@@ -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);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <ecs/ge_entity.h>
|
||||
#include <ecs/ge_render_system.h>
|
||||
#include <ecs/ge_physics_system.h>
|
||||
#include <ecs/ge_collision_system.h>
|
||||
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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__
|
||||
@@ -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; // 无碰撞
|
||||
}
|
||||
@@ -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);
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef __GE_PHYSICS_H__
|
||||
#define __GE_PHYSICS_H__
|
||||
|
||||
#include <ge_common.h>
|
||||
|
||||
typedef ge_u8_t ge_phy_layers_t;
|
||||
|
||||
#endif // __GE_PHYSICS_H__
|
||||
@@ -7,6 +7,8 @@
|
||||
* @brief 2D整数向量库 (仿Godot Vector2i设计) 不依赖任何库的单文件
|
||||
* @defgroup Vector2 2D向量操作
|
||||
*/
|
||||
#include <stdint.h>
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user