feat(game_core): 重构游戏引擎并添加新功能

- 重构了游戏引擎的核心逻辑和架构
- 添加了新的实体组件系统(ECS)
- 实现了简单的碰撞检测和响应
- 新增了地图和子弹功能
- 优化了输入处理和渲染逻辑
- 调整了游戏控制方式
This commit is contained in:
ZZY
2025-07-02 12:14:57 +08:00
parent 89bede93a9
commit b5b2c90e75
32 changed files with 856 additions and 441 deletions

View File

@@ -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
View 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__ */

View 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 },
},
},
};
}

View File

View 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

View 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__ */

View 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},
}
};
}

View 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();
}

View 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__

View File

@@ -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;

View 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;
}

View File

@@ -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__

View File

@@ -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);
// 运行主游戏逻辑

View 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;
}

View File

@@ -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;
}

View File

@@ -1,9 +0,0 @@
#ifndef __TILEMAP_H__
#define __TILEMAP_H__
typedef struct {
};
#endif // __TILE_MAP_H__