feat(game): 添加基础游戏引擎和渲染模块
- 新增游戏引擎核心模块,包括初始化和运行逻辑 - 实现基本的渲染功能,支持控制台输出 - 添加物理引擎基础,包括碰撞检测 - 集成日志系统,用于调试和信息输出 - 创建窗口和输入管理模块
This commit is contained in:
65
game_core/Makefile
Normal file
65
game_core/Makefile
Normal 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
72
game_core/main.c
Normal 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;
|
||||
}
|
||||
74
game_core/plantform/win_term/interface.h
Normal file
74
game_core/plantform/win_term/interface.h
Normal 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__
|
||||
178
game_core/plantform/win_term/win_term.c
Normal file
178
game_core/plantform/win_term/win_term.c
Normal 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);
|
||||
}
|
||||
14
game_core/plantform/win_term/win_term.h
Normal file
14
game_core/plantform/win_term/win_term.h
Normal 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__
|
||||
Reference in New Issue
Block a user