feat(engine): 重构游戏引擎核心逻辑

- 重新设计了引擎的初始化和运行流程
- 引入了实体组件系统(ECS)和物理系统
- 优化了渲染系统和输入系统
- 移除了不必要的资源管理系统
- 调整了日志系统的实现
This commit is contained in:
ZZY
2025-06-29 18:46:36 +08:00
parent 5ce660e3a6
commit 89bede93a9
37 changed files with 1294 additions and 350 deletions

View File

@@ -1,7 +1,7 @@
# 编译器设置
CC = gcc
CFLAGS = -Wall -Wextra -g -I../game_engine
LDFLAGS =
LDFLAGS = -lgdi32
# 目录设置
ROOT_DIR := .
@@ -10,18 +10,9 @@ ENGINE_DIR := ../game_engine
# 手动指定源文件目录
SRC_DIRS = $(ROOT_DIR) \
$(ROOT_DIR)/test \
$(ROOT_DIR)/plantform \
$(ROOT_DIR)/plantform/win_term \
$(ENGINE_DIR) \
$(ENGINE_DIR)/components \
$(ENGINE_DIR)/events \
$(ENGINE_DIR)/physics \
$(ENGINE_DIR)/pynic_log \
$(ENGINE_DIR)/render \
$(ENGINE_DIR)/resources \
$(ENGINE_DIR)/timer \
$(ENGINE_DIR)/utils
$(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)))

22
game_core/ge_color.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef __GE_COLOR_H__
#define __GE_COLOR_H__
// 基础颜色宏
#define GE_COLOR_BLACK RGB(0, 0, 0)
#define GE_COLOR_WHITE RGB(255, 255, 255)
#define GE_COLOR_RED RGB(255, 0, 0)
#define GE_COLOR_GREEN RGB(0, 255, 0)
#define GE_COLOR_BLUE RGB(0, 0, 255)
#define GE_COLOR_YELLOW RGB(255, 255, 0)
#define GE_COLOR_CYAN RGB(0, 255, 255)
#define GE_COLOR_MAGENTA RGB(255, 0, 255)
#define GE_COLOR_GRAY RGB(128, 128, 128)
// 扩展颜色宏
#define GE_COLOR_DARK_RED RGB(139, 0, 0)
#define GE_COLOR_DARK_GREEN RGB(0, 100, 0)
#define GE_COLOR_DARK_BLUE RGB(0, 0, 139)
#define GE_COLOR_ORANGE RGB(255, 165, 0)
#define GE_COLOR_PURPLE RGB(128, 0, 128)
#endif // __GE_COLOR_H__

View File

@@ -1,12 +1,13 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.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"
// #include "plantform/win_term/interface.h"
FILE* fp;
logger_t logger;
@@ -17,56 +18,96 @@ static void log_handler
fflush(fp);
}
#define MAX_BULLET 16
#define BASIC_SPEED (1 << GE_PHYSICS_VELOCITY_BIT)
typedef struct {
ge_entity_t* player;
ge_entity_t* bullet[MAX_BULLET];
ge_render_rect_t screen;
} game_ctx_t;
void init(ge_core_t* core) {
register_win_term(core);
register_win_timer(core);
fp = fopen("./log.txt", "w+");
(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,
}
};
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},
};
}
ctx.screen = (ge_render_rect_t) {
.pos = {0, 0},
.size = core->_render.screen_size,
};
core->context = &ctx;
}
void run(ge_core_t* core) {
static ge_vector2i_t pos = vec2i(0, 0);
// LOG_INFO("vec2: %d, %d", a.x, a.y);
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;
int key = terminal_get_key((win_term_t*)core->render.content);
switch (key) {
case 'w':
MLOG_INFO(&logger, "w");
core->render.clear(&core->render);
pos = vec2i_add(pos, GE_VEC2I_UP);
core->render.draw(&core->render, pos, "@");
break;
case 'a':
MLOG_INFO(&logger, "a");
core->render.clear(&core->render);
pos = vec2i_add(pos, GE_VEC2I_LEFT);
core->render.draw(&core->render, pos, "@");
break;
case 's':
MLOG_INFO(&logger, "s");
core->render.clear(&core->render);
pos = vec2i_add(pos, GE_VEC2I_DOWN);
core->render.draw(&core->render, pos, "@");
break;
case 'd':
MLOG_INFO(&logger, "d");
core->render.clear(&core->render);
pos = vec2i_add(pos, GE_VEC2I_RIGHT);
core->render.draw(&core->render, pos, "@");
break;
case 'q':
core->state = GE_ENGINE_STATE_EXIT;
MLOG_INFO(&logger, "exit");
break;
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);
break;
case 'a':
MLOG_INFO(&logger, "a");
*pos = vec2i_add(*pos, GE_VEC2I_LEFT);
break;
case 's':
MLOG_INFO(&logger, "s");
*pos = vec2i_add(*pos, GE_VEC2I_DOWN);
break;
case 'd':
MLOG_INFO(&logger, "d");
*pos = vec2i_add(*pos, GE_VEC2I_RIGHT);
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
break;
case 'q':
core->state = GE_ENGINE_STATE_EXIT;
MLOG_INFO(&logger, "exit");
break;
}
}
}
int main(void) {
ge_core_t core;
ge_engine_init(&core);
core.callbacks.init = init;
core.callbacks.run = run;
ge_engine_run(&core);
int ge_main(ge_core_t* core) {
core->callbacks.init = init;
core->callbacks.process = process;
ge_engine_run(core);
return 0;
}

View File

@@ -0,0 +1,10 @@
#ifndef __WIN_INTERFACE_H__
#define __WIN_INTERFACE_H__
#include <ge_common.h>
void win_sleep_us(ge_u32_t us);
ge_u32_t win_get_us(void);
#endif // __WIN_INTERFACE_H__

View File

@@ -0,0 +1,293 @@
#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) {
BYTE keyboardState[256] = {0};
WORD charBuffer = 0;
// 获取当前键盘状态
GetKeyboardState(keyboardState);
// 将虚拟键码转换为ASCII字符
if (ToAscii((UINT)wParam, (UINT)((lParam >> 16) & 0xFF),
keyboardState, &charBuffer, 0) > 0) {
// // 只返回可打印字符ASCII 32-126
// if (charBuffer >= 32 && charBuffer <= 126) {
return (char)charBuffer;
// }
}
return 0; // 不可打印字符或转换失败
}
// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
ge_core_t* core = (ge_core_t*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
switch (uMsg) {
case WM_KEYDOWN:
if (!core) {
goto END;
}
// 将按键转换为字符
char ch = win_key_to_char(wParam, lParam);
ge_input_event_t event;
if (ch != 0) {
// 将字符放入事件缓冲区
event.num = ch;
core->_input.func_send(&core->_input, event);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
core->state = GE_ENGINE_STATE_EXIT;
return 0;
default:
END:
return DefWindowProc(hwnd, uMsg, wParam, 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;
while (PeekMessage(&msg, xctx->hwnd, 0, 0, 0)) {
GetMessage(&msg, NULL, 0, 0);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
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;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(
0,
CLASS_NAME,
"Game Engine",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 128 * 4, 128 * 4,
NULL, NULL, hInstance, NULL
);
if (hwnd == NULL) return 0;
// 初始化引擎核心
static ge_core_t core;
ge_engine_init(&core);
// 创建双缓冲上下文
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;
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指针关联到窗口
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&core);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// 运行主游戏逻辑
ge_main(&core);
// 清理资源
SelectObject(wctx->hdc_back, wctx->hbm_old);
DeleteObject(wctx->hbm_back);
DeleteDC(wctx->hdc_back);
ReleaseDC(hwnd, wctx->hdc_front);
free(wctx);
return 0;
}

View File

@@ -0,0 +1,31 @@
#include <windows.h>
#include <ge_common.h>
// 获取当前时间(微秒)
ge_u32_t win_get_us(void) {
static LARGE_INTEGER frequency;
static BOOL initialized = FALSE;
if (!initialized) {
QueryPerformanceFrequency(&frequency);
initialized = TRUE;
}
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
return (ge_u32_t)((counter.QuadPart * 1000000) / frequency.QuadPart);
}
// 微秒级休眠
void win_sleep_us(ge_u32_t us) {
if (us < 1000) {
// 短时间使用忙等待
ge_u32_t start = win_get_us();
while ((win_get_us() - start) < us) {
// 空循环
}
} else {
// 长时间使用Sleep
Sleep(us / 1000);
}
}

View File

@@ -11,8 +11,8 @@ static inline void register_win_term(ge_core_t* core);
void win_sleep_ms(uint32_t ms);
uint32_t win_get_timer_ms();
static inline void register_win_timer(ge_core_t* core) {
core->timer.sleep_ms = (ge_sleep_ms_func_t)win_sleep_ms;
core->timer.get_ms = (ge_get_ms_func_t)win_get_timer_ms;
core->_timer.sleep_ms = (ge_sleep_ms_func_t)win_sleep_ms;
core->_timer.get_ms = (ge_get_ms_func_t)win_get_timer_ms;
}
static void win_render_draw(ge_render_t* ctx, ge_vector2i_t pos, const char* data) {
@@ -44,7 +44,7 @@ static void win_render_draw(ge_render_t* ctx, ge_vector2i_t pos, const char* dat
}
// 普通字符处理
char char_str[2] = { *ptr, '\0' };
char char_str[2] = { *ptr };
terminal_print(term, current_x, current_y, char_str);
current_x++;
@@ -61,14 +61,19 @@ static void win_render_getsize(ge_render_t* ctx, ge_vector2i_t* size) {
size->x = terminal_get_width((win_term_t*)ctx->content);
}
static void win_render_flush(ge_render_t* ctx) {
terminal_flush((win_term_t*)ctx->content);
}
static inline void register_win_term(ge_core_t* core) {
win_term_t* ctx = terminal_init();
Assert(ctx != NULL);
core->render.content = ctx;
core->render.init = NULL;
core->render.draw = win_render_draw;
core->render.clear = win_render_clear;
core->render.getsize = win_render_getsize;
core->_render.content = ctx;
core->_render.init = NULL;
core->_render.draw = win_render_draw;
core->_render.clear = win_render_clear;
core->_render.getsize = win_render_getsize;
core->_render.flush = win_render_flush;
}
#endif // __WIN_TERM_INTERFACE_H__

View File

@@ -2,6 +2,7 @@
#include <stdlib.h>
#include <stdint.h>
#include <windows.h>
#include <pynic_log/pynic_log.h>
#include "win_term.h"
// 终端抽象结构体
@@ -9,35 +10,108 @@ struct win_term {
HANDLE hStdout;
HANDLE hStdin;
CONSOLE_SCREEN_BUFFER_INFO csbi;
int width;
int height;
char buffer[120 * 30 + 120 * 30];
};
#define ESC "\x1b"
#define CSI "\x1b["
// 初始化终端
win_term_t* terminal_init() {
win_term_t* term = malloc(sizeof(win_term_t));
Assert(term != NULL);
term->hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
term->hStdin = GetStdHandle(STD_INPUT_HANDLE);
// Using UTF-8
SetConsoleOutputCP(65001);
SetConsoleCP(65001);
// 保存初始控制台信息
GetConsoleScreenBufferInfo(term->hStdout, &term->csbi);
// 设置输入模式(禁用行缓冲)
DWORD mode = 0;
GetConsoleMode(term->hStdin, &mode);
SetConsoleMode(term->hStdin, mode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT));
// 关闭光标(游标)
CONSOLE_CURSOR_INFO cursorInfo;
GetConsoleCursorInfo(term->hStdout, &cursorInfo);
cursorInfo.bVisible = FALSE; // 设置为 FALSE 隐藏光标
SetConsoleCursorInfo(term->hStdout, &cursorInfo);
// https://learn.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences
// Set output mode to handle virtual terminal sequences
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut == INVALID_HANDLE_VALUE) {
return NULL;
}
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
if (hIn == INVALID_HANDLE_VALUE) {
return NULL;
}
system("cls");
DWORD dwOriginalOutMode = 0;
DWORD dwOriginalInMode = 0;
if (!GetConsoleMode(hOut, &dwOriginalOutMode)) {
return NULL;
}
if (!GetConsoleMode(hIn, &dwOriginalInMode)) {
return NULL;
}
DWORD dwRequestedOutModes = ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
DWORD dwRequestedInModes = ENABLE_VIRTUAL_TERMINAL_INPUT;
DWORD dwOutMode = dwOriginalOutMode | dwRequestedOutModes;
if (!SetConsoleMode(hOut, dwOutMode)) {
// we failed to set both modes, try to step down mode gracefully.
dwRequestedOutModes = ENABLE_VIRTUAL_TERMINAL_PROCESSING;
dwOutMode = dwOriginalOutMode | dwRequestedOutModes;
if (!SetConsoleMode(hOut, dwOutMode)) {
// Failed to set any VT mode, can't do anything here.
return NULL;
}
}
DWORD dwInMode = dwOriginalInMode | dwRequestedInModes;
if (!SetConsoleMode(hIn, dwInMode)) {
// Failed to set VT input mode, can't do anything here.
return NULL;
}
// disable cursor
printf(CSI"?25l");
term->height = terminal_get_height(term);
term->width = terminal_get_width(term);
terminal_clean(term);
terminal_flush(term);
return term;
}
void terminal_flush(win_term_t* term) {
// ESC[nJ
// printf(CSI"2J");
// for (int i = 0; i < term->height; i++) {
// // ESC[x;yH 设置光标位置 x 行 y 列
// // WriteConsole(term->hStdout, term->buffer + ((term->width + 1) * i), term->width, NULL, NULL);
// WriteFile(term->hStdout, term->buffer + ((term->width + 1) * i), term->width - 1, NULL, NULL);
// }
printf(CSI"0;0H%s", term->buffer);
// fflush(stdout);
}
int terminal_clean(win_term_t* term) {
// printf(CSI"2J");
memset(term->buffer, ' ', sizeof(term->buffer));
for (int i = 1; i <= term->height; i++) {
term->buffer[i * term->width] = '\n';
}
term->buffer[term->width * term->height] = '\0';
return 0;
}
// 打印文本(带位置和颜色)
void terminal_print(win_term_t* term, int x, int y, const char* text) {
// terminal_set_cursor(term, x, y);
// printf(CSI"%d;%dH%s", y, x, text);
while (*text) {
*((term->buffer) + ((term->width + 1) * y) + x) = *text;
x++;
text++;
}
}
// 清理终端
void terminal_cleanup(win_term_t* term) {
// 恢复初始属性
@@ -45,19 +119,6 @@ void terminal_cleanup(win_term_t* term) {
free(term);
}
// 设置光标位置
static inline void terminal_set_cursor(win_term_t* term, int x, int y) {
COORD coord = { x, y };
SetConsoleCursorPosition(term->hStdout, coord);
}
// 打印文本(带位置和颜色)
void terminal_print(win_term_t* term, int x, int y, const char* text) {
terminal_set_cursor(term, x, y);
printf("%s", text);
fflush(stdout);
}
// 获取按键事件
int terminal_get_key(win_term_t* term) {
INPUT_RECORD ir;
@@ -86,60 +147,6 @@ int terminal_get_height(win_term_t* term) {
return term->csbi.srWindow.Bottom - term->csbi.srWindow.Top + 1;
}
int terminal_clean(win_term_t* term) {
// Write the sequence for clearing the display.
DWORD written = 0;
PCWSTR sequence = L"\x1b[2J";
if (!WriteConsoleW(term->hStdout, sequence, (DWORD)wcslen(sequence), &written, NULL))
{
// If we fail, try to restore the mode on the way out.
SetConsoleMode(term->hStdout, ENABLE_VIRTUAL_TERMINAL_PROCESSING);
return GetLastError();
}
return 0;
}
// int main() {
// win_term_t* term = terminal_init();
// // 清屏
// system("cls");
// // 打印彩色文本
// terminal_print(term, 10, 5,
// "按方向键移动, ESC退出");
// int x = 10, y = 10;
// char* player = "@";
// while (1) {
// // 绘制玩家
// terminal_print(term, x, y,
// player);
// // 获取按键
// int key = terminal_get_key(term);
// // 擦除旧位置
// terminal_print(term, x, y, " ");
// // 处理移动
// switch (key) {
// case VK_UP: y--; break;
// case VK_DOWN: y++; break;
// case VK_LEFT: x--; break;
// case VK_RIGHT: x++; break;
// case VK_ESCAPE:
// terminal_cleanup(term);
// return 0;
// }
// // 边界检查
// if (x < 0) x = 0;
// if (y < 0) y = 0;
// }
// }
uint32_t win_get_timer_ms() {
static LARGE_INTEGER freq = {0};
static BOOL has_freq = FALSE;

View File

@@ -10,5 +10,6 @@ int terminal_get_key(win_term_t* term);
int terminal_get_width(win_term_t* term);
int terminal_get_height(win_term_t* term);
int terminal_clean(win_term_t* term);
void terminal_flush(win_term_t* term);
#endif // __WIN_TERM_H__

9
game_core/tilemap.c Normal file
View File

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