feat(game): 添加基础游戏引擎和渲染模块

- 新增游戏引擎核心模块,包括初始化和运行逻辑
- 实现基本的渲染功能,支持控制台输出
- 添加物理引擎基础,包括碰撞检测
- 集成日志系统,用于调试和信息输出
- 创建窗口和输入管理模块
This commit is contained in:
ZZY
2025-06-24 02:16:01 +08:00
commit 5ce660e3a6
30 changed files with 2056 additions and 0 deletions

65
game_core/Makefile Normal file
View File

@@ -0,0 +1,65 @@
# 编译器设置
CC = gcc
CFLAGS = -Wall -Wextra -g -I../game_engine
LDFLAGS =
# 目录设置
ROOT_DIR := .
BUILD_DIR := build
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
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)
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
# 当前目录下的文件编译规则
$(BUILD_DIR)/core/%.o: $(ROOT_DIR)/%.c
@mkdir -p $(@D)
$(CC) $(CFLAGS) -c $< -o $@
# 引擎目录下的文件编译规则
$(BUILD_DIR)/engine/%.o: $(ENGINE_DIR)/%.c
@mkdir -p $(@D)
$(CC) $(CFLAGS) -c $< -o $@
$(BUILD_DIR):
mkdir -p $(BUILD_DIR)
debug:
@echo "Source files:"
@echo $(SRCS)
@echo "Object files:"
@echo $(OBJS)
all: build
build: $(TARGET)
run: build
./$(TARGET)
clean:
rm -rf $(BUILD_DIR)
.PHONY: all build run clean debug

72
game_core/main.c Normal file
View File

@@ -0,0 +1,72 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.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);
}
void init(ge_core_t* core) {
register_win_term(core);
register_win_timer(core);
fp = fopen("./log.txt", "w+");
init_logger_ex(&logger, "game", log_handler);
Assert(fp != NULL);
}
void run(ge_core_t* core) {
static ge_vector2i_t pos = vec2i(0, 0);
// LOG_INFO("vec2: %d, %d", a.x, a.y);
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;
}
}
int main(void) {
ge_core_t core;
ge_engine_init(&core);
core.callbacks.init = init;
core.callbacks.run = run;
ge_engine_run(&core);
return 0;
}

View File

@@ -0,0 +1,74 @@
#ifndef __WIN_TERM_INTERFACE_H__
#define __WIN_TERM_INTERFACE_H__
// #include <render/ge_render.h>
#include <ge_core.h>
#include <string.h>
#include "win_term.h"
// typedef void(*ge_render_data_func_t)(const void* content, ge_vector2i_t pos, const char* data);
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;
}
static void win_render_draw(ge_render_t* ctx, ge_vector2i_t pos, const char* data) {
win_term_t* term = (win_term_t*)ctx->content;
const int start_x = pos.x;
const int start_y = pos.y;
int current_x = start_x;
int current_y = start_y;
const char* ptr = data;
while (*ptr) {
if (*ptr == '\n') {
// 换行处理重置X坐标Y坐标下移一行
current_x = start_x;
current_y++;
ptr++;
continue;
}
// 处理制表符(可选)
if (*ptr == '\t') {
int spaces = 4 - ((current_x - start_x) % 4); // 4空格制表符
for (int i = 0; i < spaces; i++) {
terminal_print(term, current_x, current_y, " ");
current_x++;
}
ptr++;
continue;
}
// 普通字符处理
char char_str[2] = { *ptr, '\0' };
terminal_print(term, current_x, current_y, char_str);
current_x++;
ptr++;
}
}
static void win_render_clear(ge_render_t* ctx) {
terminal_clean((win_term_t*)ctx->content);
}
static void win_render_getsize(ge_render_t* ctx, ge_vector2i_t* size) {
size->y = terminal_get_height((win_term_t*)ctx->content);
size->x = terminal_get_width((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;
}
#endif // __WIN_TERM_INTERFACE_H__

View File

@@ -0,0 +1,178 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <windows.h>
#include "win_term.h"
// 终端抽象结构体
struct win_term {
HANDLE hStdout;
HANDLE hStdin;
CONSOLE_SCREEN_BUFFER_INFO csbi;
};
// 初始化终端
win_term_t* terminal_init() {
win_term_t* term = malloc(sizeof(win_term_t));
term->hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
term->hStdin = GetStdHandle(STD_INPUT_HANDLE);
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);
system("cls");
return term;
}
// 清理终端
void terminal_cleanup(win_term_t* term) {
// 恢复初始属性
SetConsoleTextAttribute(term->hStdout, term->csbi.wAttributes);
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;
DWORD count;
while (1) {
ReadConsoleInput(term->hStdin, &ir, 1, &count);
if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) {
// 返回虚拟键码
// return ir.Event.KeyEvent.wVirtualKeyCode;
// 如果需要字符可改用:
return ir.Event.KeyEvent.uChar.AsciiChar;
}
}
}
int terminal_get_width(win_term_t* term) {
GetConsoleScreenBufferInfo(term->hStdout, &term->csbi);
return term->csbi.srWindow.Right - term->csbi.srWindow.Left + 1;
}
int terminal_get_height(win_term_t* term) {
GetConsoleScreenBufferInfo(term->hStdout, &term->csbi);
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;
if (!has_freq) {
has_freq = QueryPerformanceFrequency(&freq);
if (!has_freq) {
// 回退到低精度计时
return (uint32_t)GetTickCount();
}
}
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
return (uint32_t)((counter.QuadPart * 1000) / freq.QuadPart);
}
void win_sleep_ms(uint32_t ms) {
if (ms == 0) return;
// 高精度休眠(Windows)
HANDLE timer = CreateWaitableTimer(NULL, TRUE, NULL);
if (!timer) {
Sleep(ms);
return;
}
LARGE_INTEGER ft;
ft.QuadPart = -(10000LL * ms); // 负值表示相对时间
if (SetWaitableTimer(timer, &ft, 0, NULL, NULL, FALSE)) {
WaitForSingleObject(timer, INFINITE);
}
CloseHandle(timer);
}

View File

@@ -0,0 +1,14 @@
#ifndef __WIN_TERM_H__
#define __WIN_TERM_H__
struct win_term;
typedef struct win_term win_term_t;
win_term_t* terminal_init();
void terminal_cleanup(win_term_t* term);
void terminal_print(win_term_t* term, int x, int y, const char* text);
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);
#endif // __WIN_TERM_H__