feat(game): 添加基础游戏引擎和渲染模块
- 新增游戏引擎核心模块,包括初始化和运行逻辑 - 实现基本的渲染功能,支持控制台输出 - 添加物理引擎基础,包括碰撞检测 - 集成日志系统,用于调试和信息输出 - 创建窗口和输入管理模块
This commit is contained in:
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
.*/
|
||||||
|
!.gitignore
|
||||||
|
build/
|
||||||
|
test/
|
||||||
|
*.txt
|
||||||
|
|
||||||
|
*.obj
|
||||||
|
*.dll
|
||||||
|
*.lib
|
||||||
|
*.exe
|
||||||
|
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
*.out
|
||||||
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__
|
||||||
20
game_engine/components/ge_entity.h
Normal file
20
game_engine/components/ge_entity.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef __GE_ENTIRY_H__
|
||||||
|
#define __GE_ENTIRY_H__
|
||||||
|
|
||||||
|
#include "../ge_config.h"
|
||||||
|
#include "../math/ge_vector2i.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t id;
|
||||||
|
uint16_t components;
|
||||||
|
} ge_entity_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ge_vector2i_t position;
|
||||||
|
unsigned char z;
|
||||||
|
} ge_position_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // __GE_ENTIRY_H__
|
||||||
6
game_engine/components/ge_sprite.h
Normal file
6
game_engine/components/ge_sprite.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef __GE_SPRITE_H__
|
||||||
|
#define __GE_SPRITE_H__
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // __GE_SPRITE_H__
|
||||||
10
game_engine/events/ge_events_bus.h
Normal file
10
game_engine/events/ge_events_bus.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#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__
|
||||||
12
game_engine/ge_common.h
Normal file
12
game_engine/ge_common.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#ifndef __GE_COMMON_H__
|
||||||
|
#define __GE_COMMON_H__
|
||||||
|
|
||||||
|
#include <pynic_log/pynic_log.h>
|
||||||
|
#include <utils/ge_maroc.h>
|
||||||
|
|
||||||
|
#include <ge_config.h>
|
||||||
|
#include <utils/ge_kfifo.h>
|
||||||
|
#include <utils/ge_static_alloc.h>
|
||||||
|
#include <utils/ge_vector2i.h>
|
||||||
|
|
||||||
|
#endif // __GE_COMMON_H__
|
||||||
27
game_engine/ge_config.h
Normal file
27
game_engine/ge_config.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#ifndef __GE_CONFIG_H__
|
||||||
|
#define __GE_CONFIG_H__
|
||||||
|
|
||||||
|
#define GE_VEC2_USE_SHORT_NAMES
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef int8_t ge_i8_t;
|
||||||
|
typedef int16_t ge_i16_t;
|
||||||
|
typedef int32_t ge_i32_t;
|
||||||
|
|
||||||
|
typedef uint8_t ge_u8_t;
|
||||||
|
typedef uint16_t ge_u16_t;
|
||||||
|
typedef uint32_t ge_u32_t;
|
||||||
|
|
||||||
|
typedef intptr_t ge_iptr_t;
|
||||||
|
typedef uintptr_t ge_uptr_t;
|
||||||
|
typedef int ge_int_t;
|
||||||
|
typedef unsigned int ge_uint_t;
|
||||||
|
|
||||||
|
typedef uint32_t ge_size_t;
|
||||||
|
typedef uint32_t ge_color_t;
|
||||||
|
|
||||||
|
typedef ge_uint_t ge_uid_t;
|
||||||
|
|
||||||
|
#endif // __GE_CONFIG_H__
|
||||||
54
game_engine/ge_core.c
Normal file
54
game_engine/ge_core.c
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#define __PYNIC_LOG_IMPLIMENT__
|
||||||
|
#include <ge_core.h>
|
||||||
|
|
||||||
|
void ge_engine_init(ge_core_t *core) {
|
||||||
|
// TOOD init all data
|
||||||
|
ge_u8_t* ptr = (ge_u8_t*)core;
|
||||||
|
for (int i = 0; i < (int)sizeof(ge_core_t); i++) {
|
||||||
|
*(ptr + i) = 0;
|
||||||
|
}
|
||||||
|
core->configs.fps = 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ge_init(ge_core_t* core) {
|
||||||
|
/**
|
||||||
|
* check render
|
||||||
|
*/
|
||||||
|
ge_render_t* render_ctx = &core->render;
|
||||||
|
if (render_ctx->content) {
|
||||||
|
Assert(render_ctx->clear && render_ctx->draw
|
||||||
|
&& render_ctx->getsize);
|
||||||
|
GE_SAFE_CALL(render_ctx->init, render_ctx);
|
||||||
|
render_ctx->getsize(render_ctx, &render_ctx->screen_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert(core->timer.sleep_ms != NULL);
|
||||||
|
Assert(core->configs.fps != 0);
|
||||||
|
|
||||||
|
core->timer.fps_ctl.target_fps = 0;
|
||||||
|
if (core->timer.get_ms != NULL) {
|
||||||
|
ge_fps_init(&core->timer.fps_ctl, core->configs.fps,
|
||||||
|
core->timer.get_ms, core->timer.sleep_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ge_render(ge_render_t* ctx) {
|
||||||
|
// ctx->clear(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ge_engine_run(ge_core_t *core) {
|
||||||
|
GE_SAFE_CALL(core->callbacks.init, core);
|
||||||
|
ge_init(core);
|
||||||
|
|
||||||
|
core->state = GE_ENGINE_STATE_RUNNING;
|
||||||
|
while (core->state == GE_ENGINE_STATE_RUNNING) {
|
||||||
|
if (core->timer.fps_ctl.target_fps) ge_fps_begin_frame(&core->timer.fps_ctl);
|
||||||
|
|
||||||
|
GE_SAFE_CALL(core->callbacks.run, core);
|
||||||
|
if (core->render.content) ge_render(&core->render);
|
||||||
|
|
||||||
|
if (core->timer.fps_ctl.target_fps) ge_fps_end_frame(&core->timer.fps_ctl);
|
||||||
|
else core->timer.sleep_ms(1000 / core->configs.fps);
|
||||||
|
}
|
||||||
|
GE_SAFE_CALL(core->callbacks.exit, core);
|
||||||
|
}
|
||||||
44
game_engine/ge_core.h
Normal file
44
game_engine/ge_core.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#ifndef __GE_CORE_H__
|
||||||
|
#define __GE_CORE_H__
|
||||||
|
|
||||||
|
#include <ge_common.h>
|
||||||
|
#include <render/ge_render.h>
|
||||||
|
#include <timer/ge_timer.h>
|
||||||
|
|
||||||
|
struct ge_engine_core;
|
||||||
|
typedef struct ge_engine_core ge_core_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback Function
|
||||||
|
*/
|
||||||
|
typedef void (*ge_init_func_t)(ge_core_t* core);
|
||||||
|
typedef void (*ge_exit_func_t)(ge_core_t* core);
|
||||||
|
typedef void (*ge_run_func_t)(ge_core_t* core);
|
||||||
|
|
||||||
|
typedef enum ge_engine_state {
|
||||||
|
GE_ENGINE_STATE_RUNNING,
|
||||||
|
GE_ENGINE_STATE_EXIT,
|
||||||
|
} ge_engine_state_t;
|
||||||
|
|
||||||
|
struct ge_engine_core {
|
||||||
|
struct {
|
||||||
|
ge_init_func_t init;
|
||||||
|
ge_exit_func_t exit;
|
||||||
|
ge_run_func_t run;
|
||||||
|
} callbacks;
|
||||||
|
ge_engine_state_t state;
|
||||||
|
|
||||||
|
ge_render_t render;
|
||||||
|
ge_timer_t timer;
|
||||||
|
struct {
|
||||||
|
ge_uint_t fps;
|
||||||
|
} configs;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic Call
|
||||||
|
*/
|
||||||
|
void ge_engine_init(ge_core_t *core);
|
||||||
|
void ge_engine_run(ge_core_t *core);
|
||||||
|
|
||||||
|
#endif // __GE_CORE_H__
|
||||||
20
game_engine/physics/ge_collision_box.h
Normal file
20
game_engine/physics/ge_collision_box.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#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__
|
||||||
58
game_engine/physics/ge_collision_tilemap.h
Normal file
58
game_engine/physics/ge_collision_tilemap.h
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
#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; // 无碰撞
|
||||||
|
}
|
||||||
27
game_engine/physics/ge_collison.h
Normal file
27
game_engine/physics/ge_collison.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
|
||||||
|
// // 在 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);
|
||||||
8
game_engine/physics/ge_physics.h
Normal file
8
game_engine/physics/ge_physics.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#ifndef __GE_PHYSICS_H__
|
||||||
|
#define __GE_PHYSICS_H__
|
||||||
|
|
||||||
|
#include <ge_common.h>
|
||||||
|
|
||||||
|
typedef ge_u8_t ge_phy_layers_t;
|
||||||
|
|
||||||
|
#endif // __GE_PHYSICS_H__
|
||||||
212
game_engine/pynic_log/README.md
Normal file
212
game_engine/pynic_log/README.md
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
# Pythonic_log
|
||||||
|
|
||||||
|
纯头文件的轻量化日志库,模仿 Python `logging` 标准库的设计,支持终端彩色输出,不依赖任何标准库。
|
||||||
|
|
||||||
|
## 特性亮点
|
||||||
|
|
||||||
|
- 🚀 **纯头文件实现** - 单文件集成,无需编译
|
||||||
|
- 🧩 **零依赖** - 不依赖标准库(可通过宏配置)
|
||||||
|
- 🌈 **终端彩色输出** - 内置 ANSI 颜色支持
|
||||||
|
- ⚙️ **多级日志系统** - 支持 DEBUG/INFO/WARN/ERROR/FATAL/TRACE
|
||||||
|
- 🧪 **高级断言工具** - 包含 Assert, Panie, TODO 等实用宏
|
||||||
|
- 📦 **模块化设计** - 支持多模块独立日志配置
|
||||||
|
- 🔧 **高度可配置** - 自定义输出、格式化和退出行为
|
||||||
|
|
||||||
|
## 快速入门
|
||||||
|
|
||||||
|
### 基础使用
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "pylike_log.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// 基础日志输出
|
||||||
|
LOG_INFO("Application started");
|
||||||
|
LOG_DEBUG("Debug value: %d", 42);
|
||||||
|
LOG_WARN("This is a warning");
|
||||||
|
LOG_ERROR("Something went wrong!");
|
||||||
|
|
||||||
|
// 断言工具
|
||||||
|
int value = 10;
|
||||||
|
Assert(value > 5); // 条件断言
|
||||||
|
AssertFmt(value != 0, "Value cannot be zero");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 输出示例
|
||||||
|
|
||||||
|
```shell
|
||||||
|
[INFO ] - root - main.c:8 | Application started
|
||||||
|
[DEBUG] - root - main.c:9 | Debug value: 42
|
||||||
|
[WARN ] - root - main.c:10 | This is a warning
|
||||||
|
[ERROR] - root - main.c:11 | Something went wrong!
|
||||||
|
```
|
||||||
|
|
||||||
|
## 核心功能
|
||||||
|
|
||||||
|
### 日志级别控制
|
||||||
|
|
||||||
|
```c
|
||||||
|
// 获取日志器实例
|
||||||
|
logger_t* logger = log_get("my_module");
|
||||||
|
|
||||||
|
// 设置日志级别(只显示 WARN 及以上)
|
||||||
|
log_set_level(logger, LOG_LEVEL_WARN | LOG_LEVEL_ERROR | LOG_LEVEL_FATAL);
|
||||||
|
|
||||||
|
// 模块化日志
|
||||||
|
MLOG_WARN(logger, "Module-specific warning: %s", "Low memory");
|
||||||
|
```
|
||||||
|
|
||||||
|
### 自定义日志处理器
|
||||||
|
|
||||||
|
```c
|
||||||
|
void custom_handler(
|
||||||
|
log_level_t level,
|
||||||
|
const char* module,
|
||||||
|
const char* file,
|
||||||
|
int line,
|
||||||
|
const char* message
|
||||||
|
) {
|
||||||
|
// 自定义处理逻辑(如写入文件/网络)
|
||||||
|
printf("[CUSTOM] %s:%d - %s\n", file, line, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
logger_t* logger = log_get(NULL);
|
||||||
|
log_set_handler(logger, custom_handler);
|
||||||
|
|
||||||
|
LOG_INFO("This will use custom handler");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 断言系统
|
||||||
|
|
||||||
|
```c
|
||||||
|
void critical_function(int* ptr) {
|
||||||
|
// 空指针检查
|
||||||
|
AssertFmt(ptr != NULL, "Received NULL pointer");
|
||||||
|
|
||||||
|
// 范围检查
|
||||||
|
AssertFmt(*ptr >= 0 && *ptr <= 100, "Value %d out of range", *ptr);
|
||||||
|
|
||||||
|
// 标记未实现功能
|
||||||
|
TODO(); // 触发致命错误
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 配置选项
|
||||||
|
|
||||||
|
### 基本配置宏
|
||||||
|
|
||||||
|
| 宏名称 | 默认值 | 描述 |
|
||||||
|
| :-------------------- | :----- | :--------------- |
|
||||||
|
| `LOGGER_MAX_BUF_SIZE` | 512 | 单条日志最大长度 |
|
||||||
|
| `__LOG_NO_COLOR__` | - | 禁用彩色输出 |
|
||||||
|
| `__NO_STDIO__` | - | 禁用标准库依赖 |
|
||||||
|
|
||||||
|
|
||||||
|
### 标准库替代(嵌入式环境)
|
||||||
|
```c
|
||||||
|
// 在包含头文件前定义这些宏
|
||||||
|
#define _pylike_logout_printf(fmt, ...) my_printf(fmt, ##__VA_ARGS__)
|
||||||
|
#define _pylike_snprintf my_snprintf
|
||||||
|
#define _pylike_exit my_exit
|
||||||
|
|
||||||
|
#include "pylike_log.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 颜色自定义
|
||||||
|
编辑 `pylike_color.h` 修改 ANSI 颜色代码,或定义 `__NO_PYLIKE_COLOR__` 完全禁用颜色。
|
||||||
|
|
||||||
|
## 高级用法
|
||||||
|
|
||||||
|
### 性能追踪
|
||||||
|
|
||||||
|
```c
|
||||||
|
void complex_operation() {
|
||||||
|
LOG_TRACE("Entering complex_operation");
|
||||||
|
|
||||||
|
// ... 复杂计算 ...
|
||||||
|
|
||||||
|
LOG_TRACE("Exiting complex_operation");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 致命错误处理
|
||||||
|
|
||||||
|
```c
|
||||||
|
void load_config() {
|
||||||
|
if (!config_valid) {
|
||||||
|
LOG_FATAL("Invalid configuration - aborting");
|
||||||
|
// 此处会自动调用 exit(-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 模块化日志系统
|
||||||
|
|
||||||
|
```c
|
||||||
|
// 创建模块专用日志器
|
||||||
|
logger_t network_logger;
|
||||||
|
init_logger(&network_logger, "NETWORK");
|
||||||
|
|
||||||
|
// 设置模块日志级别
|
||||||
|
log_set_level(&network_logger, LOG_LEVEL_DEBUG | LOG_LEVEL_ERROR);
|
||||||
|
|
||||||
|
// 使用模块日志
|
||||||
|
MLOG_DEBUG(&network_logger, "Connecting to %s", "192.168.1.1");
|
||||||
|
```
|
||||||
|
|
||||||
|
## API 参考
|
||||||
|
|
||||||
|
### 日志级别
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef enum log_level {
|
||||||
|
LOG_LEVEL_NOTSET = 0, // 未设置级别(继承默认配置)
|
||||||
|
LOG_LEVEL_DEBUG = 1 << 0, // 调试信息(开发阶段详细信息)
|
||||||
|
LOG_LEVEL_INFO = 1 << 1, // 常规信息(系统运行状态)
|
||||||
|
LOG_LEVEL_WARN = 1 << 2, // 警告信息(潜在问题提示)
|
||||||
|
LOG_LEVEL_ERROR = 1 << 3, // 错误信息(可恢复的错误)
|
||||||
|
LOG_LEVEL_FATAL = 1 << 4, // 致命错误(导致程序终止的严重错误)
|
||||||
|
LOG_LEVEL_TRACE = 1 << 5, // 追踪(性能追踪或者栈帧追踪)
|
||||||
|
LOG_LEVEL_ALL = 0xFF, // 全级别标志(组合所有日志级别)
|
||||||
|
} log_level_t;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 日志器结构
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct logger {
|
||||||
|
const char* name; // 日志器名称(用于模块区分)
|
||||||
|
log_level_t level; // 当前设置的日志级别
|
||||||
|
log_handler handler; // 日志处理回调函数
|
||||||
|
char buf[LOGGER_MAX_BUF_SIZE]; // 格式化缓冲区
|
||||||
|
} logger_t;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 核心函数
|
||||||
|
|
||||||
|
| 函数 | 描述 |
|
||||||
|
| :---------------------------------------- | :------------------- |
|
||||||
|
| `init_logger(logger_t*, const char*)` | 初始化日志器实例 |
|
||||||
|
| `log_get(const char*)` | 获取或创建日志器实例 |
|
||||||
|
| `log_set_level(logger_t*, log_level_t)` | 设置日志级别 |
|
||||||
|
| `log_set_handler(logger_t*, log_handler)` | 设置自定义日志处理器 |
|
||||||
|
|
||||||
|
## 集成指南
|
||||||
|
|
||||||
|
1. 将 `pylike_log.h` 和 `pylike_color.h` 添加到项目
|
||||||
|
2. 在需要日志的源文件中包含头文件
|
||||||
|
3. (可选)配置自定义输出函数(嵌入式系统)
|
||||||
|
4. 使用提供的日志宏记录信息
|
||||||
|
|
||||||
|
## 设计理念
|
||||||
|
|
||||||
|
- **极简主义**:单头文件实现核心功能
|
||||||
|
- **灵活性**:通过宏配置适应各种环境
|
||||||
|
- **Pythonic API**:借鉴 Python logging 的优秀设计
|
||||||
|
- **安全第一**:所有宏都使用 `do {...} while(0)` 包装
|
||||||
59
game_engine/pynic_log/pynic_color.h
Normal file
59
game_engine/pynic_log/pynic_color.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* @file color.h
|
||||||
|
* @brief ANSI终端颜色控制码定义
|
||||||
|
*
|
||||||
|
* 提供跨平台的终端文本颜色和样式控制支持
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __PYTHONIC_TERMINAL_COLOR_H__
|
||||||
|
#define __PYTHONIC_TERMINAL_COLOR_H__
|
||||||
|
|
||||||
|
/// @name 前景色控制码
|
||||||
|
/// @{
|
||||||
|
#define ANSI_FG_BLACK "\33[30m" ///< 黑色前景
|
||||||
|
#define ANSI_FG_RED "\33[31m" ///< 红色前景
|
||||||
|
#define ANSI_FG_GREEN "\33[32m" ///< 绿色前景
|
||||||
|
#define ANSI_FG_YELLOW "\33[33m" ///< 黄色前景
|
||||||
|
#define ANSI_FG_BLUE "\33[34m" ///< 蓝色前景
|
||||||
|
#define ANSI_FG_MAGENTA "\33[35m" ///< 品红色前景
|
||||||
|
#define ANSI_FG_CYAN "\33[36m" ///< 青色前景
|
||||||
|
#define ANSI_FG_WHITE "\33[37m" ///< 白色前景
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// @name 背景色控制码
|
||||||
|
/// @{
|
||||||
|
#define ANSI_BG_BLACK "\33[40m" ///< 黑色背景
|
||||||
|
#define ANSI_BG_RED "\33[41m" ///< 红色背景
|
||||||
|
#define ANSI_BG_GREEN "\33[42m" ///< 绿色背景
|
||||||
|
#define ANSI_BG_YELLOW "\33[43m" ///< 黄色背景
|
||||||
|
#define ANSI_BG_BLUE "\33[44m" ///< 蓝色背景
|
||||||
|
#define ANSI_BG_MAGENTA "\33[45m" ///< 品红色背景(注:原始代码此处应为45m)
|
||||||
|
#define ANSI_BG_CYAN "\33[46m" ///< 青色背景
|
||||||
|
#define ANSI_BG_WHITE "\33[47m" ///< 白色背景
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// @name 文字样式控制码
|
||||||
|
/// @{
|
||||||
|
#define ANSI_UNDERLINED "\33[4m" ///< 下划线样式
|
||||||
|
#define ANSI_BOLD "\33[1m" ///< 粗体样式
|
||||||
|
#define ANSI_NONE "\33[0m" ///< 重置所有样式
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def ANSI_FMT
|
||||||
|
* @brief 安全文本格式化宏
|
||||||
|
* @param str 目标字符串
|
||||||
|
* @param fmt ANSI格式序列(可组合多个样式)
|
||||||
|
*
|
||||||
|
* @note 当定义ANSI_FMT_DISABLE时自动禁用颜色输出
|
||||||
|
* @code
|
||||||
|
* printf(ANSI_FMT("Warning!", ANSI_FG_YELLOW ANSI_BOLD));
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
#ifndef ANSI_FMT_DISABLE
|
||||||
|
#define ANSI_FMT(str, fmt) fmt str ANSI_NONE ///< 启用样式包裹
|
||||||
|
#else
|
||||||
|
#define ANSI_FMT(str, fmt) str ///< 禁用样式输出
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // __PYTHONIC_TERMINAL_COLOR_H__
|
||||||
288
game_engine/pynic_log/pynic_log.h
Normal file
288
game_engine/pynic_log/pynic_log.h
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
/**
|
||||||
|
* @file pythonic_log.h
|
||||||
|
* @brief 模仿python标准库logger的日志系统核心模块(支持多级日志、断言和异常处理)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __PYTHONIC_LOG_H__
|
||||||
|
#define __PYTHONIC_LOG_H__
|
||||||
|
|
||||||
|
#ifndef __NO_STDIO__
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifndef _pynic_logout_printf
|
||||||
|
#define _pynic_logout_printf(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
#ifndef _pynic_snprintf
|
||||||
|
#define _pynic_snprintf snprintf
|
||||||
|
#endif
|
||||||
|
#ifndef _pynic_exit
|
||||||
|
#define _pynic_exit exit
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __NO_PYNIC_COLOR__
|
||||||
|
#include "pynic_color.h"
|
||||||
|
#else
|
||||||
|
#ifndef __LOG_NO_COLOR__
|
||||||
|
#define __LOG_NO_COLOR__
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PYNIC_LOGGER_MAX_BUF_SIZE
|
||||||
|
#define PYNIC_LOGGER_MAX_BUF_SIZE 512 ///< 单条日志最大缓冲区尺寸
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NULL
|
||||||
|
#define NULL ((void *)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// #define __PYNIC_LOG_IMPLIMENT__
|
||||||
|
|
||||||
|
#ifndef _pynic_exit
|
||||||
|
#warning _pynic_exit not defined, it will make some exit not triggered
|
||||||
|
#endif
|
||||||
|
#ifndef _pynic_logout_printf
|
||||||
|
#error _pynic_logout_printf not defined so log will not work
|
||||||
|
#endif
|
||||||
|
#ifndef _pynic_snprintf
|
||||||
|
#error _pynic_logout_printf not defined so log will not work
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define _PYNIC_STR(str) #str
|
||||||
|
#define PYLIKE_STR(str) _PYNIC_STR(str)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 日志级别枚举
|
||||||
|
*
|
||||||
|
* 定义日志系统的输出级别和组合标志位
|
||||||
|
*/
|
||||||
|
typedef enum log_level {
|
||||||
|
LOG_LEVEL_NOTSET = 0, ///< 未设置级别(继承默认配置)
|
||||||
|
LOG_LEVEL_DEBUG = 1 << 0, ///< 调试信息(开发阶段详细信息)
|
||||||
|
LOG_LEVEL_INFO = 1 << 1, ///< 常规信息(系统运行状态)
|
||||||
|
LOG_LEVEL_WARN = 1 << 2, ///< 警告信息(潜在问题提示)
|
||||||
|
LOG_LEVEL_ERROR = 1 << 3, ///< 错误信息(可恢复的错误)
|
||||||
|
LOG_LEVEL_FATAL = 1 << 4, ///< 致命错误(导致程序终止的严重错误)
|
||||||
|
LOG_LEVEL_TRACE = 1 << 5, ///< 追踪(性能追踪或者栈帧追踪)
|
||||||
|
LOG_LEVEL_ALL = 0xFF, ///< 全级别标志(组合所有日志级别)
|
||||||
|
} log_level_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 日志处理回调函数类型
|
||||||
|
* @param level 日志级别
|
||||||
|
* @param module 模块名称(可为NULL)
|
||||||
|
* @param file 源文件名
|
||||||
|
* @param line 代码行号
|
||||||
|
* @param message 格式化后的日志消息
|
||||||
|
* @todo 待实现模块名称,输入的模块名称,都将被忽略
|
||||||
|
*/
|
||||||
|
typedef void (*pynic_log_handler)(
|
||||||
|
log_level_t level,
|
||||||
|
const char* module,
|
||||||
|
const char* file,
|
||||||
|
int line,
|
||||||
|
const char* message
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 日志器实例结构体
|
||||||
|
*
|
||||||
|
* 每个日志器实例维护独立的配置和缓冲区
|
||||||
|
*/
|
||||||
|
typedef struct logger {
|
||||||
|
const char* name; ///< 日志器名称(用于模块区分)
|
||||||
|
log_level_t level; ///< 当前设置的日志级别
|
||||||
|
pynic_log_handler handler; ///< 日志处理回调函数
|
||||||
|
char buf[PYNIC_LOGGER_MAX_BUF_SIZE]; ///< 格式化缓冲区
|
||||||
|
} logger_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化日志实例 其余参数设置为默认值
|
||||||
|
* @param[in] logger 日志器实例指针
|
||||||
|
* @param[in] name 日志器名称(NULL表示获取默认日志器名称)
|
||||||
|
*/
|
||||||
|
void init_logger(logger_t* logger, const char* name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化日志实例
|
||||||
|
* @param[in] logger 日志器实例指针
|
||||||
|
* @param[in] name 日志器名称(NULL表示获取默认日志器名称)
|
||||||
|
* @param[in] handler 日志处理回调函数
|
||||||
|
*/
|
||||||
|
void init_logger_ex(logger_t* logger, const char* name, pynic_log_handler hander);
|
||||||
|
|
||||||
|
// TODO log_set(); 暂未实现 日志注册
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取或创建日志器实例
|
||||||
|
* @param[in] name 日志器名称(NULL表示获取默认日志器)
|
||||||
|
* @return 日志器实例指针
|
||||||
|
* @warning 若没有找到相应日志器则会返回根日志器
|
||||||
|
*/
|
||||||
|
logger_t* log_get(const char* name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置日志级别
|
||||||
|
* @param[in] logger 目标日志器实例
|
||||||
|
* @param[in] level 要设置的日志级别(可组合多个级别)
|
||||||
|
*/
|
||||||
|
void log_set_level(logger_t* logger, log_level_t level);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置自定义日志处理器
|
||||||
|
* @param[in] logger 目标日志器实例
|
||||||
|
* @param[in] handler 自定义处理函数(NULL恢复默认处理)
|
||||||
|
*/
|
||||||
|
void log_set_handler(logger_t* logger, pynic_log_handler handler);
|
||||||
|
|
||||||
|
#ifndef LOG_MAX_MAROC_BUF_SIZE
|
||||||
|
#define LOG_MAX_MAROC_BUF_SIZE LOGGER_MAX_BUF_SIZE ///< 宏展开缓冲区尺寸
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def _LOG
|
||||||
|
* @brief 内部日志宏(供其他日志宏调用)
|
||||||
|
* @param _module_ 模块实例(NULL表示使用默认日志器)
|
||||||
|
* @param _level_ 日志级别
|
||||||
|
* @param _msg_ 格式字符串
|
||||||
|
* @param ... 可变参数列表
|
||||||
|
*/
|
||||||
|
#define _LOG(_module_, _level_, _msg_, ...) \
|
||||||
|
do { \
|
||||||
|
logger_t* _logger; \
|
||||||
|
if (_module_ == NULL) _logger = log_get(NULL); \
|
||||||
|
else _logger = _module_; \
|
||||||
|
if (_logger && _logger->handler && (_logger->level & (_level_))) { \
|
||||||
|
_pynic_snprintf(_logger->buf, sizeof(_logger->buf), (_msg_), ##__VA_ARGS__); \
|
||||||
|
_logger->handler((_level_), _logger->name, __FILE__, __LINE__, _logger->buf); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/// @name 模块日志宏
|
||||||
|
/// @{
|
||||||
|
#define MLOG_NOTSET(module, ...) _LOG(module, LOG_LEVEL_NOTSET, __VA_ARGS__) ///< 未分类日志
|
||||||
|
#define MLOG_DEBUG( module, ...) _LOG(module, LOG_LEVEL_DEBUG, __VA_ARGS__) ///< 调试日志(需启用DEBUG级别)
|
||||||
|
#define MLOG_INFO( module, ...) _LOG(module, LOG_LEVEL_INFO, __VA_ARGS__) ///< 信息日志(常规运行日志)
|
||||||
|
#define MLOG_WARN( module, ...) _LOG(module, LOG_LEVEL_WARN, __VA_ARGS__) ///< 警告日志(潜在问题)
|
||||||
|
#define MLOG_ERROR( module, ...) _LOG(module, LOG_LEVEL_ERROR, __VA_ARGS__) ///< 错误日志(可恢复错误)
|
||||||
|
#define MLOG_FATAL( module, ...) _LOG(module, LOG_LEVEL_FATAL, __VA_ARGS__) ///< 致命错误日志(程序终止前)
|
||||||
|
#define MLOG_TRACE( module, ...) _LOG(module, LOG_LEVEL_TRACE, __VA_ARGS__) ///< 追踪日志(调用栈跟踪)
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/// @name 快捷日志宏
|
||||||
|
/// @{
|
||||||
|
#define LOG_NOTSET(...) _LOG(NULL, LOG_LEVEL_NOTSET, __VA_ARGS__) ///< 未分类日志
|
||||||
|
#define LOG_DEBUG(...) _LOG(NULL, LOG_LEVEL_DEBUG, __VA_ARGS__) ///< 调试日志(需启用DEBUG级别)
|
||||||
|
#define LOG_INFO(...) _LOG(NULL, LOG_LEVEL_INFO, __VA_ARGS__) ///< 信息日志(常规运行日志)
|
||||||
|
#define LOG_WARN(...) _LOG(NULL, LOG_LEVEL_WARN, __VA_ARGS__) ///< 警告日志(潜在问题)
|
||||||
|
#define LOG_ERROR(...) _LOG(NULL, LOG_LEVEL_ERROR, __VA_ARGS__) ///< 错误日志(可恢复错误)
|
||||||
|
#define LOG_FATAL(...) _LOG(NULL, LOG_LEVEL_FATAL, __VA_ARGS__) ///< 致命错误日志(程序终止前)
|
||||||
|
#define LOG_TRACE(...) _LOG(NULL, LOG_LEVEL_TRACE, __VA_ARGS__) ///< 追踪日志(调用栈跟踪)
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def _Assert
|
||||||
|
* @brief 断言检查内部宏
|
||||||
|
* @param cond 检查条件表达式
|
||||||
|
* @param ... 错误信息参数(格式字符串+参数)
|
||||||
|
*/
|
||||||
|
#define _Assert(cond, ...) \
|
||||||
|
do { \
|
||||||
|
if (!(cond)) { \
|
||||||
|
LOG_FATAL(__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/// @name 断言工具宏
|
||||||
|
/// @{
|
||||||
|
#define AssertFmt(cond, format, ...) _Assert(cond, "Assertion Failure: " format, ## __VA_ARGS__) ///< 带格式的断言检查
|
||||||
|
#define PanicFmt(format, ...) _Assert(0, "Panic: " format, ## __VA_ARGS__) ///< 立即触发致命错误
|
||||||
|
#ifndef Assert
|
||||||
|
#define Assert(cond) AssertFmt(cond, "cond is `" PYLIKE_STR(cond) "`") ///< 基础断言检查
|
||||||
|
#endif
|
||||||
|
#define Panic(...) PanicFmt(__VA_ARGS__) ///< 触发致命错误(带自定义消息)
|
||||||
|
#define TODO() PanicFmt("TODO please implement me") ///< 标记未实现代码(触发致命错误)
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
static inline const char* pynic_level_str(log_level_t level) {
|
||||||
|
const char* level_str;
|
||||||
|
switch (level) {
|
||||||
|
case LOG_LEVEL_DEBUG: level_str = "DEBUG"; break;
|
||||||
|
case LOG_LEVEL_INFO: level_str = "INFO "; break;
|
||||||
|
case LOG_LEVEL_WARN: level_str = "WARN "; break;
|
||||||
|
case LOG_LEVEL_ERROR: level_str = "ERROR"; break;
|
||||||
|
case LOG_LEVEL_FATAL: level_str = "FATAL"; break;
|
||||||
|
case LOG_LEVEL_TRACE: level_str = "TRACE"; break;
|
||||||
|
default: level_str = "NOTSET"; break;
|
||||||
|
}
|
||||||
|
return level_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __PYTHONIC_TERMINAL_COLOR_H__
|
||||||
|
static inline const char* pynic_level_color(log_level_t level) {
|
||||||
|
const char* color_code = ANSI_NONE;
|
||||||
|
switch (level) {
|
||||||
|
case LOG_LEVEL_DEBUG: color_code = ANSI_FG_CYAN; break;
|
||||||
|
case LOG_LEVEL_INFO: color_code = ANSI_FG_GREEN; break;
|
||||||
|
case LOG_LEVEL_TRACE: color_code = ANSI_FG_BLUE; break;
|
||||||
|
case LOG_LEVEL_WARN: color_code = ANSI_FG_YELLOW; break;
|
||||||
|
case LOG_LEVEL_ERROR: color_code = ANSI_FG_RED; break;
|
||||||
|
case LOG_LEVEL_FATAL: color_code = ANSI_FG_RED ANSI_UNDERLINED; break; // 增强对比度
|
||||||
|
default: color_code = ANSI_NONE;
|
||||||
|
}
|
||||||
|
return color_code;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __PYNIC_LOG_IMPLIMENT__
|
||||||
|
|
||||||
|
static void default_handler(log_level_t level, const char* module, const char* file, int line, const char* message) {
|
||||||
|
const char* level_str = pynic_level_str(level);
|
||||||
|
|
||||||
|
/// @note: 定义 __LOG_NO_COLOR__ 会取消颜色输出
|
||||||
|
#ifndef __LOG_NO_COLOR__
|
||||||
|
_pynic_logout_printf(ANSI_BOLD "%s[%s] - %s - %s:%d | %s" ANSI_NONE "\n",
|
||||||
|
pynic_level_color(level), level_str, module, file, line, message);
|
||||||
|
#else
|
||||||
|
_pynic_logout_printf("[%s] %s:%d | %s: %s\n",
|
||||||
|
level_str, file, line, module, message);
|
||||||
|
#endif
|
||||||
|
if (level & LOG_LEVEL_FATAL) {
|
||||||
|
_pynic_exit(-LOG_LEVEL_FATAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static logger_t __pynic_root_logger = {
|
||||||
|
.name = "root",
|
||||||
|
.level = LOG_LEVEL_ALL,
|
||||||
|
.handler = default_handler,
|
||||||
|
};
|
||||||
|
|
||||||
|
void init_logger(logger_t* logger, const char* name) {
|
||||||
|
init_logger_ex(logger, name, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_logger_ex(logger_t* logger, const char* name, pynic_log_handler hander) {
|
||||||
|
logger->name = name;
|
||||||
|
logger->handler = hander ? hander : default_handler;
|
||||||
|
log_set_level(logger, LOG_LEVEL_ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger_t* log_get(const char* name) {
|
||||||
|
if (name == NULL) return &__pynic_root_logger;
|
||||||
|
return &__pynic_root_logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_set_level(logger_t* logger, log_level_t level) {
|
||||||
|
if (logger) logger->level = level;
|
||||||
|
else __pynic_root_logger.level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_set_handler(logger_t* logger, pynic_log_handler handler) {
|
||||||
|
if (logger) logger->handler = handler;
|
||||||
|
else __pynic_root_logger.handler = handler;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // __PYTHONIC_LOG_H__
|
||||||
30
game_engine/render/ge_render.h
Normal file
30
game_engine/render/ge_render.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#ifndef __GE_RENDER_H__
|
||||||
|
#define __GE_RENDER_H__
|
||||||
|
|
||||||
|
#include <ge_common.h>
|
||||||
|
|
||||||
|
struct ge_render_ctx;
|
||||||
|
typedef struct ge_render_ctx ge_render_t;
|
||||||
|
|
||||||
|
typedef void (*ge_render_init_func_t)(ge_render_t* ctx);
|
||||||
|
typedef void (*ge_render_clear_func_t)(ge_render_t* ctx);
|
||||||
|
typedef void (*ge_render_draw_func_t)
|
||||||
|
(ge_render_t* ctx, ge_vector2i_t pos, const char* data);
|
||||||
|
typedef void (*ge_render_drawex_func_t)
|
||||||
|
(ge_render_t* ctx, ge_vector2i_t pos, const char* data, const void* propety);
|
||||||
|
typedef void (*ge_render_getsize_func_t)
|
||||||
|
(ge_render_t* ctx, ge_vector2i_t *size);
|
||||||
|
typedef void (*ge_render_push_event)
|
||||||
|
(ge_render_t* ctx, void* event);
|
||||||
|
|
||||||
|
struct ge_render_ctx {
|
||||||
|
const void* content;
|
||||||
|
ge_vector2i_t screen_size;
|
||||||
|
ge_render_init_func_t init;
|
||||||
|
ge_render_clear_func_t clear;
|
||||||
|
ge_render_draw_func_t draw;
|
||||||
|
ge_render_drawex_func_t drawex;
|
||||||
|
ge_render_getsize_func_t getsize;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __GE_RENDER_H__
|
||||||
38
game_engine/resources/ge_resources.h
Normal file
38
game_engine/resources/ge_resources.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef __GE_RESOURCES_H__
|
||||||
|
#define __GE_RESOURCES_H__
|
||||||
|
|
||||||
|
#include <ge_common.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
} ge_res_bmp_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ge_uid_t res_id;
|
||||||
|
const char* data;
|
||||||
|
} ge_drawable_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
tank 3 * 3 (0)
|
||||||
|
"|\n"
|
||||||
|
" # \n"
|
||||||
|
"###\n"
|
||||||
|
"###\n"
|
||||||
|
|
||||||
|
wall 3 * 3 (1)
|
||||||
|
"@@@\n"
|
||||||
|
"@@@\n"
|
||||||
|
"@@@\n"
|
||||||
|
|
||||||
|
tilemap 3 * 3
|
||||||
|
"1,1,1\n"
|
||||||
|
"-1,1,0\n"
|
||||||
|
"1,0,1\n"
|
||||||
|
|
||||||
|
exece
|
||||||
|
{qiang}
|
||||||
|
|
||||||
|
"@@@\n""@@@\n"
|
||||||
|
"@@@\n""@@@\n"
|
||||||
|
"@@@\n""@@@\n"
|
||||||
|
*/
|
||||||
|
#endif // __GE_RESOURCES_H__
|
||||||
53
game_engine/timer/ge_fps.c
Normal file
53
game_engine/timer/ge_fps.c
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
// ge_fps.c
|
||||||
|
#include "ge_fps.h"
|
||||||
|
|
||||||
|
// 初始化FPS控制器
|
||||||
|
void ge_fps_init(ge_fps_controller_t* fps_ctrl, ge_u32_t target_fps,
|
||||||
|
ge_fps_get_ms_func_t get_ms, ge_fps_sleep_ms_func_t sleep_ms) {
|
||||||
|
fps_ctrl->target_fps = target_fps;
|
||||||
|
fps_ctrl->frame_duration = 1000 / target_fps;
|
||||||
|
fps_ctrl->last_frame_time = get_ms();
|
||||||
|
fps_ctrl->frame_time = 0;
|
||||||
|
fps_ctrl->sleep_time = 0;
|
||||||
|
fps_ctrl->frame_count = 0;
|
||||||
|
fps_ctrl->fps = 0;
|
||||||
|
fps_ctrl->last_fps_time = fps_ctrl->last_frame_time;
|
||||||
|
fps_ctrl->call_get_ms = get_ms;
|
||||||
|
fps_ctrl->call_sleep_ms = sleep_ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 帧开始
|
||||||
|
void ge_fps_begin_frame(ge_fps_controller_t* fps_ctrl) {
|
||||||
|
fps_ctrl->last_frame_time = fps_ctrl->call_get_ms();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 帧结束
|
||||||
|
void ge_fps_end_frame(ge_fps_controller_t* fps_ctrl) {
|
||||||
|
// 计算当前帧耗时
|
||||||
|
ge_u32_t current_time = fps_ctrl->call_get_ms();
|
||||||
|
fps_ctrl->frame_time = current_time - fps_ctrl->last_frame_time;
|
||||||
|
|
||||||
|
// 计算需要休眠的时间
|
||||||
|
if (fps_ctrl->frame_time < fps_ctrl->frame_duration) {
|
||||||
|
fps_ctrl->sleep_time = fps_ctrl->frame_duration - fps_ctrl->frame_time;
|
||||||
|
|
||||||
|
// 高精度休眠
|
||||||
|
fps_ctrl->call_sleep_ms(fps_ctrl->sleep_time);
|
||||||
|
|
||||||
|
// 更新实际休眠后时间
|
||||||
|
current_time = fps_ctrl->call_get_ms();
|
||||||
|
fps_ctrl->sleep_time = current_time - fps_ctrl->last_frame_time - fps_ctrl->frame_time;
|
||||||
|
} else {
|
||||||
|
fps_ctrl->sleep_time = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新FPS计数
|
||||||
|
fps_ctrl->frame_count++;
|
||||||
|
|
||||||
|
// 每秒计算一次实际FPS
|
||||||
|
if (current_time - fps_ctrl->last_fps_time >= 1000) {
|
||||||
|
fps_ctrl->fps = fps_ctrl->frame_count;
|
||||||
|
fps_ctrl->frame_count = 0;
|
||||||
|
fps_ctrl->last_fps_time = current_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
31
game_engine/timer/ge_fps.h
Normal file
31
game_engine/timer/ge_fps.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#ifndef __GE_FPS_H__
|
||||||
|
#define __GE_FPS_H__
|
||||||
|
|
||||||
|
#include <ge_common.h>
|
||||||
|
struct ge_fps_controller;
|
||||||
|
typedef struct ge_fps_controller ge_fps_controller_t;
|
||||||
|
|
||||||
|
typedef ge_u32_t (*ge_fps_get_ms_func_t)(void);
|
||||||
|
typedef void (*ge_fps_sleep_ms_func_t)(ge_u32_t ms);
|
||||||
|
|
||||||
|
void ge_fps_init(ge_fps_controller_t* fps_ctrl, ge_u32_t target_fps,
|
||||||
|
ge_fps_get_ms_func_t get_ms, ge_fps_sleep_ms_func_t sleep_ms);
|
||||||
|
void ge_fps_begin_frame(ge_fps_controller_t* fps_ctrl);
|
||||||
|
void ge_fps_end_frame(ge_fps_controller_t* fps_ctrl);
|
||||||
|
|
||||||
|
// FPS控制器结构体
|
||||||
|
struct ge_fps_controller {
|
||||||
|
ge_u32_t target_fps; // 目标帧率
|
||||||
|
ge_u32_t frame_duration; // 目标每帧时长(毫秒)
|
||||||
|
ge_u32_t last_frame_time; // 上一帧开始时间
|
||||||
|
ge_u32_t frame_time; // 当前帧耗时
|
||||||
|
ge_u32_t sleep_time; // 需要休眠的时间
|
||||||
|
ge_u32_t frame_count; // 帧计数器
|
||||||
|
ge_u32_t fps; // 实际帧率
|
||||||
|
ge_u32_t last_fps_time; // 上次计算FPS的时间
|
||||||
|
|
||||||
|
ge_fps_get_ms_func_t call_get_ms;
|
||||||
|
ge_fps_sleep_ms_func_t call_sleep_ms;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __GE_FPS_H__
|
||||||
0
game_engine/timer/ge_timer.c
Normal file
0
game_engine/timer/ge_timer.c
Normal file
16
game_engine/timer/ge_timer.h
Normal file
16
game_engine/timer/ge_timer.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#ifndef __GE_TIMER_H__
|
||||||
|
#define __GE_TIMER_H__
|
||||||
|
|
||||||
|
#include <ge_config.h>
|
||||||
|
#include "ge_fps.h"
|
||||||
|
|
||||||
|
typedef void (*ge_sleep_ms_func_t)(ge_u32_t);
|
||||||
|
typedef ge_u32_t (*ge_get_ms_func_t)(void);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ge_sleep_ms_func_t sleep_ms;
|
||||||
|
ge_get_ms_func_t get_ms;
|
||||||
|
ge_fps_controller_t fps_ctl;
|
||||||
|
} ge_timer_t;
|
||||||
|
|
||||||
|
#endif // __GE_TIMER_H__
|
||||||
236
game_engine/utils/ge_kfifo.h
Normal file
236
game_engine/utils/ge_kfifo.h
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
/*
|
||||||
|
* A generic kernel FIFO implementation
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009/2010 Stefani Seibold <stefani@seibold.net>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef __GE_KFIFO_H__
|
||||||
|
#define __GE_KFIFO_H__
|
||||||
|
/**
|
||||||
|
* 仿照linux内核中的kfifo实现
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GE_MEMORY_BARRIR
|
||||||
|
#define GE_MEMORY_BARRIR() __sync_synchronize()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef GE_ARRAY_SIZE
|
||||||
|
#define GE_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct __ge_kfifo {
|
||||||
|
unsigned int in;
|
||||||
|
unsigned int out;
|
||||||
|
unsigned int mask;
|
||||||
|
unsigned int esize;
|
||||||
|
void *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define __STRUCT_GE_KFIFO(type, size) \
|
||||||
|
{ \
|
||||||
|
struct __ge_kfifo kfifo; \
|
||||||
|
type buf[((size < 2) || (size & (size - 1))) ? -1 : size]; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DECLARE_KFIFO - macro to declare a fifo object
|
||||||
|
* @fifo: name of the declared fifo
|
||||||
|
* @type: type of the fifo elements
|
||||||
|
* @size: the number of elements in the fifo, this must be a power of 2
|
||||||
|
*/
|
||||||
|
#define DECLARE_GE_KFIFO(fifo, type, size, tname) struct tname __STRUCT_GE_KFIFO(type, size) fifo
|
||||||
|
|
||||||
|
#define INIT_GE_KFIFO(fifo, type) do { \
|
||||||
|
struct __ge_kfifo *__kfifo = &((fifo)->kfifo); \
|
||||||
|
__kfifo->in = 0; \
|
||||||
|
__kfifo->out = 0; \
|
||||||
|
__kfifo->mask = GE_ARRAY_SIZE((fifo).buf) - 1; \
|
||||||
|
__kfifo->esize = sizeof(*(fifo).buf); \
|
||||||
|
__kfifo->data = (fifo).buf; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEFINE_KFIFO - macro to define and initialize a fifo
|
||||||
|
* @fifo: name of the declared fifo datatype
|
||||||
|
* @type: type of the fifo elements
|
||||||
|
* @size: the number of elements in the fifo, this must be a power of 2
|
||||||
|
*
|
||||||
|
* Note: the macro can be used for global and local fifo data type variables.
|
||||||
|
*/
|
||||||
|
#define DEFINE_GE_KFIFO(fifo, type, size, tname) \
|
||||||
|
DECLARE_GE_KFIFO(fifo, type, size, tname) = \
|
||||||
|
(struct tname) { { \
|
||||||
|
.in = 0, \
|
||||||
|
.out = 0, \
|
||||||
|
.mask = GE_ARRAY_SIZE((fifo).buf) - 1, \
|
||||||
|
.esize = sizeof(*(fifo).buf), \
|
||||||
|
.data = (fifo).buf, \
|
||||||
|
}, { 0 } }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kfifo_size - returns the size of the fifo in elements
|
||||||
|
* @fifo: address of the fifo to be used
|
||||||
|
*/
|
||||||
|
#define ge_kfifo_size(fifo) \
|
||||||
|
((fifo)->kfifo.mask + 1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kfifo_len - returns the number of used elements in the fifo
|
||||||
|
* @fifo: address of the fifo to be used
|
||||||
|
*/
|
||||||
|
#define ge_kfifo_len(fifo) \
|
||||||
|
((fifo)->kfifo.in - (fifo)->kfifo.out)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kfifo_is_empty - returns true if the fifo is empty
|
||||||
|
* @fifo: address of the fifo to be used
|
||||||
|
*/
|
||||||
|
#define ge_kfifo_is_empty(fifo) \
|
||||||
|
((fifo)->kfifo.in == (fifo)->kfifo.out)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kfifo_is_full - returns true if the fifo is full
|
||||||
|
* @fifo: address of the fifo to be used
|
||||||
|
*/
|
||||||
|
#define ge_kfifo_is_full(fifo) (ge_kfifo_len(fifo) > (fifo)->kfifo.mask)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kfifo_put - put data into the fifo
|
||||||
|
* @fifo: address of the fifo to be used
|
||||||
|
* @val: the data to be added
|
||||||
|
*
|
||||||
|
* This macro copies the given value into the fifo.
|
||||||
|
* It returns 0 if the fifo was full. Otherwise it returns the number
|
||||||
|
* processed elements.
|
||||||
|
*
|
||||||
|
* Note that with only one concurrent reader and one concurrent
|
||||||
|
* writer, you don't need extra locking to use these macro.
|
||||||
|
*/
|
||||||
|
#define ge_kfifo_put(fifo, val) do { \
|
||||||
|
unsigned int __ret; \
|
||||||
|
struct __ge_kfifo *__kfifo = &((fifo)->kfifo); \
|
||||||
|
__ret = !ge_kfifo_is_full(fifo); \
|
||||||
|
if (__ret) { \
|
||||||
|
((fifo)->buf)[__kfifo->in & __kfifo->mask] = val; \
|
||||||
|
/*smp_wmb();*/ \
|
||||||
|
GE_MEMORY_BARRIR(); \
|
||||||
|
__kfifo->in++; \
|
||||||
|
} \
|
||||||
|
/*__ret;*/ \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kfifo_get - get data from the fifo
|
||||||
|
* @fifo: address of the fifo to be used
|
||||||
|
* @val: address where to store the data
|
||||||
|
*
|
||||||
|
* This macro reads the data from the fifo.
|
||||||
|
* It returns 0 if the fifo was empty. Otherwise it returns the number
|
||||||
|
* processed elements.
|
||||||
|
*
|
||||||
|
* Note that with only one concurrent reader and one concurrent
|
||||||
|
* writer, you don't need extra locking to use these macro.
|
||||||
|
*/
|
||||||
|
#define ge_kfifo_get(fifo, val) do { \
|
||||||
|
unsigned int __ret; \
|
||||||
|
struct __ge_kfifo *__kfifo = &((fifo)->kfifo); \
|
||||||
|
__ret = !ge_kfifo_is_empty(fifo); \
|
||||||
|
if (__ret) { \
|
||||||
|
*val = ((fifo)->buf)[__kfifo->out & __kfifo->mask]; \
|
||||||
|
/*smp_wmb();*/ \
|
||||||
|
GE_MEMORY_BARRIR(); \
|
||||||
|
__kfifo->out++; \
|
||||||
|
} \
|
||||||
|
/*__ret;*/ \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kfifo_peek - get data from the fifo without removing
|
||||||
|
* @fifo: address of the fifo to be used
|
||||||
|
* @val: address where to store the data
|
||||||
|
*
|
||||||
|
* This reads the data from the fifo without removing it from the fifo.
|
||||||
|
* It returns 0 if the fifo was empty. Otherwise it returns the number
|
||||||
|
* processed elements.
|
||||||
|
*
|
||||||
|
* Note that with only one concurrent reader and one concurrent
|
||||||
|
* writer, you don't need extra locking to use these macro.
|
||||||
|
*/
|
||||||
|
#define ge_kfifo_peek(fifo, val) do { \
|
||||||
|
unsigned int __ret; \
|
||||||
|
struct __ge_kfifo *__kfifo = &((fifo)->kfifo); \
|
||||||
|
__ret = !kfifo_is_empty(fifo); \
|
||||||
|
if (__ret) { \
|
||||||
|
*val = ((fifo)->buf)[__kfifo->out & __kfifo->mask]; \
|
||||||
|
/*smp_wmb();*/ \
|
||||||
|
GE_MEMORY_BARRIR(); \
|
||||||
|
} \
|
||||||
|
/*__ret;*/ \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kfifo_in - put data into the fifo
|
||||||
|
* @fifo: address of the fifo to be used
|
||||||
|
* @buf: the data to be added
|
||||||
|
* @n: number of elements to be added
|
||||||
|
*
|
||||||
|
* This macro copies the given buffer into the fifo and returns the
|
||||||
|
* number of copied elements.
|
||||||
|
*
|
||||||
|
* Note that with only one concurrent reader and one concurrent
|
||||||
|
* writer, you don't need extra locking to use these macro.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
#define kfifo_in(fifo, buf, n) \
|
||||||
|
({ \
|
||||||
|
typeof((fifo) + 1) __tmp = (fifo); \
|
||||||
|
typeof(__tmp->ptr_const) __buf = (buf); \
|
||||||
|
unsigned long __n = (n); \
|
||||||
|
const size_t __recsize = sizeof(*__tmp->rectype); \
|
||||||
|
struct __kfifo *__kfifo = &__tmp->kfifo; \
|
||||||
|
(__recsize) ?\
|
||||||
|
__kfifo_in_r(__kfifo, __buf, __n, __recsize) : \
|
||||||
|
__kfifo_in(__kfifo, __buf, __n); \
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* kfifo_out - get data from the fifo
|
||||||
|
* @fifo: address of the fifo to be used
|
||||||
|
* @buf: pointer to the storage buffer
|
||||||
|
* @n: max. number of elements to get
|
||||||
|
*
|
||||||
|
* This macro gets some data from the fifo and returns the numbers of elements
|
||||||
|
* copied.
|
||||||
|
*
|
||||||
|
* Note that with only one concurrent reader and one concurrent
|
||||||
|
* writer, you don't need extra locking to use these macro.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
#define kfifo_out(fifo, buf, n) \
|
||||||
|
({ \
|
||||||
|
typeof((fifo) + 1) __tmp = (fifo); \
|
||||||
|
typeof(__tmp->ptr) __buf = (buf); \
|
||||||
|
unsigned long __n = (n); \
|
||||||
|
const size_t __recsize = sizeof(*__tmp->rectype); \
|
||||||
|
struct __kfifo *__kfifo = &__tmp->kfifo; \
|
||||||
|
(__recsize) ?\
|
||||||
|
__kfifo_out_r(__kfifo, __buf, __n, __recsize) : \
|
||||||
|
__kfifo_out(__kfifo, __buf, __n); \
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif // __GE_KFIFO_H__
|
||||||
129
game_engine/utils/ge_maroc.h
Normal file
129
game_engine/utils/ge_maroc.h
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
#ifndef __GE_MAROC_H__
|
||||||
|
#define __GE_MAROC_H__
|
||||||
|
|
||||||
|
#define GE_ABS(x) ((x) > 0 : (x) : -(x))
|
||||||
|
#define GE_MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||||
|
#define GE_MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
|
||||||
|
#define _GE_STR(x) #x
|
||||||
|
#define GE_STRINGIFY(x) _GE_STR(x)
|
||||||
|
|
||||||
|
#define GE_SAFE_CALL(func, ...) \
|
||||||
|
do { \
|
||||||
|
if (func) func(__VA_ARGS__); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#endif // __GE_MAROC_H__
|
||||||
|
|
||||||
|
/***************************************************************************************
|
||||||
|
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
|
||||||
|
*
|
||||||
|
* NEMU is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
*
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
***************************************************************************************/
|
||||||
|
/*
|
||||||
|
#ifndef __MACRO_H__
|
||||||
|
#define __MACRO_H__
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// macro stringizing
|
||||||
|
#define str_temp(x) #x
|
||||||
|
#define str(x) str_temp(x)
|
||||||
|
|
||||||
|
// strlen() for string constant
|
||||||
|
#define STRLEN(CONST_STR) (sizeof(CONST_STR) - 1)
|
||||||
|
|
||||||
|
// calculate the length of an array
|
||||||
|
#define ARRLEN(arr) (int)(sizeof(arr) / sizeof(arr[0]))
|
||||||
|
|
||||||
|
// macro concatenation
|
||||||
|
#define concat_temp(x, y) x ## y
|
||||||
|
#define concat(x, y) concat_temp(x, y)
|
||||||
|
#define concat3(x, y, z) concat(concat(x, y), z)
|
||||||
|
#define concat4(x, y, z, w) concat3(concat(x, y), z, w)
|
||||||
|
#define concat5(x, y, z, v, w) concat4(concat(x, y), z, v, w)
|
||||||
|
|
||||||
|
// macro testing
|
||||||
|
// See https://stackoverflow.com/questions/26099745/test-if-preprocessor-symbol-is-defined-inside-macro
|
||||||
|
#define CHOOSE2nd(a, b, ...) b
|
||||||
|
#define MUX_WITH_COMMA(contain_comma, a, b) CHOOSE2nd(contain_comma a, b)
|
||||||
|
#define MUX_MACRO_PROPERTY(p, macro, a, b) MUX_WITH_COMMA(concat(p, macro), a, b)
|
||||||
|
// define placeholders for some property
|
||||||
|
#define __P_DEF_0 X,
|
||||||
|
#define __P_DEF_1 X,
|
||||||
|
#define __P_ONE_1 X,
|
||||||
|
#define __P_ZERO_0 X,
|
||||||
|
// define some selection functions based on the properties of BOOLEAN macro
|
||||||
|
#define MUXDEF(macro, X, Y) MUX_MACRO_PROPERTY(__P_DEF_, macro, X, Y)
|
||||||
|
#define MUXNDEF(macro, X, Y) MUX_MACRO_PROPERTY(__P_DEF_, macro, Y, X)
|
||||||
|
#define MUXONE(macro, X, Y) MUX_MACRO_PROPERTY(__P_ONE_, macro, X, Y)
|
||||||
|
#define MUXZERO(macro, X, Y) MUX_MACRO_PROPERTY(__P_ZERO_,macro, X, Y)
|
||||||
|
|
||||||
|
// test if a boolean macro is defined
|
||||||
|
#define ISDEF(macro) MUXDEF(macro, 1, 0)
|
||||||
|
// test if a boolean macro is undefined
|
||||||
|
#define ISNDEF(macro) MUXNDEF(macro, 1, 0)
|
||||||
|
// test if a boolean macro is defined to 1
|
||||||
|
#define ISONE(macro) MUXONE(macro, 1, 0)
|
||||||
|
// test if a boolean macro is defined to 0
|
||||||
|
#define ISZERO(macro) MUXZERO(macro, 1, 0)
|
||||||
|
// test if a macro of ANY type is defined
|
||||||
|
// NOTE1: it ONLY works inside a function, since it calls `strcmp()`
|
||||||
|
// NOTE2: macros defined to themselves (#define A A) will get wrong results
|
||||||
|
#define isdef(macro) (strcmp("" #macro, "" str(macro)) != 0)
|
||||||
|
|
||||||
|
// simplification for conditional compilation
|
||||||
|
#define __IGNORE(...)
|
||||||
|
#define __KEEP(...) __VA_ARGS__
|
||||||
|
// keep the code if a boolean macro is defined
|
||||||
|
#define IFDEF(macro, ...) MUXDEF(macro, __KEEP, __IGNORE)(__VA_ARGS__)
|
||||||
|
// keep the code if a boolean macro is undefined
|
||||||
|
#define IFNDEF(macro, ...) MUXNDEF(macro, __KEEP, __IGNORE)(__VA_ARGS__)
|
||||||
|
// keep the code if a boolean macro is defined to 1
|
||||||
|
#define IFONE(macro, ...) MUXONE(macro, __KEEP, __IGNORE)(__VA_ARGS__)
|
||||||
|
// keep the code if a boolean macro is defined to 0
|
||||||
|
#define IFZERO(macro, ...) MUXZERO(macro, __KEEP, __IGNORE)(__VA_ARGS__)
|
||||||
|
|
||||||
|
// functional-programming-like macro (X-macro)
|
||||||
|
// apply the function `f` to each element in the container `c`
|
||||||
|
// NOTE1: `c` should be defined as a list like:
|
||||||
|
// f(a0) f(a1) f(a2) ...
|
||||||
|
// NOTE2: each element in the container can be a tuple
|
||||||
|
#define MAP(c, f) c(f)
|
||||||
|
|
||||||
|
#define BITMASK(bits) ((1ull << (bits)) - 1)
|
||||||
|
#define BITS(x, hi, lo) (((x) >> (lo)) & BITMASK((hi) - (lo) + 1)) // similar to x[hi:lo] in verilog
|
||||||
|
#define SEXT(x, len) ({ struct { int64_t n : len; } __x = { .n = x }; (uint64_t)__x.n; })
|
||||||
|
|
||||||
|
#define ROUNDUP(a, sz) ((((uintptr_t)a) + (sz) - 1) & ~((sz) - 1))
|
||||||
|
#define ROUNDDOWN(a, sz) ((((uintptr_t)a)) & ~((sz) - 1))
|
||||||
|
|
||||||
|
#define PG_ALIGN __attribute((aligned(4096)))
|
||||||
|
|
||||||
|
#if !defined(likely)
|
||||||
|
#define likely(cond) __builtin_expect(cond, 1)
|
||||||
|
#define unlikely(cond) __builtin_expect(cond, 0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// for AM IOE
|
||||||
|
#define io_read(reg) \
|
||||||
|
({ reg##_T __io_param; \
|
||||||
|
ioe_read(reg, &__io_param); \
|
||||||
|
__io_param; })
|
||||||
|
|
||||||
|
#define io_write(reg, ...) \
|
||||||
|
({ reg##_T __io_param = (reg##_T) { __VA_ARGS__ }; \
|
||||||
|
ioe_write(reg, &__io_param); })
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
*/
|
||||||
6
game_engine/utils/ge_static_alloc.h
Normal file
6
game_engine/utils/ge_static_alloc.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef __GE_STATIC_ALLOC_H__
|
||||||
|
#define __GE_STATIC_ALLOC_H__
|
||||||
|
|
||||||
|
#include <ge_config.h>
|
||||||
|
|
||||||
|
#endif // __GE_STATIC_ALLOC_H__
|
||||||
254
game_engine/utils/ge_vector2i.h
Normal file
254
game_engine/utils/ge_vector2i.h
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
#ifndef __GE_VECTOR2_H__
|
||||||
|
#define __GE_VECTOR2_H__
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file ge_vector2i.h
|
||||||
|
* @brief 2D整数向量库 (仿Godot Vector2i设计) 不依赖任何库的单文件
|
||||||
|
* @defgroup Vector2 2D向量操作
|
||||||
|
*/
|
||||||
|
|
||||||
|
// #define GE_VEC2I_USE_SHORT_NAMES
|
||||||
|
|
||||||
|
#ifdef GE_ABS
|
||||||
|
#undef GE_ABS
|
||||||
|
#define GE_ABS(x) ((x) > 0 ? (x) : -(x))
|
||||||
|
#endif
|
||||||
|
typedef int ge_unit_t; /**< 坐标值类型定义 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct ge_vector2_t
|
||||||
|
* @brief 2D整数向量结构体
|
||||||
|
* @ingroup Vector2
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
ge_unit_t x; /**< X轴坐标 */
|
||||||
|
ge_unit_t y; /**< Y轴坐标 */
|
||||||
|
} ge_vector2i_t;
|
||||||
|
|
||||||
|
// ========================= 构造函数 =========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def GE_VEC2I
|
||||||
|
* @brief 创建向量 (x, y)
|
||||||
|
* @param x_val X坐标值
|
||||||
|
* @param y_val Y坐标值
|
||||||
|
* @return 构造的向量
|
||||||
|
* @ingroup Vector2
|
||||||
|
*/
|
||||||
|
#define GE_VEC2I(x_val, y_val) ((ge_vector2i_t){(x_val), (y_val)})
|
||||||
|
|
||||||
|
// ========================= 常量定义 =========================
|
||||||
|
|
||||||
|
/** @def GE_VEC2I_ZERO 零向量 (0, 0) */
|
||||||
|
#define GE_VEC2I_ZERO GE_VEC2I(0, 0)
|
||||||
|
|
||||||
|
/** @def GE_VEC2I_ONE 单位向量 (1, 1) */
|
||||||
|
#define GE_VEC2I_ONE GE_VEC2I(1, 1)
|
||||||
|
|
||||||
|
/** @def GE_VEC2I_LEFT 左方向向量 (-1, 0) */
|
||||||
|
#define GE_VEC2I_LEFT GE_VEC2I(-1, 0)
|
||||||
|
|
||||||
|
/** @def GE_VEC2I_RIGHT 右方向向量 (1, 0) */
|
||||||
|
#define GE_VEC2I_RIGHT GE_VEC2I(1, 0)
|
||||||
|
|
||||||
|
/** @def GE_VEC2I_UP 上方向向量 (0, -1) */
|
||||||
|
#define GE_VEC2I_UP GE_VEC2I(0, -1)
|
||||||
|
|
||||||
|
/** @def GE_VEC2I_DOWN 下方向向量 (0, 1) */
|
||||||
|
#define GE_VEC2I_DOWN GE_VEC2I(0, 1)
|
||||||
|
|
||||||
|
// ========================= 运算宏定义 =========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def GE_VEC2I_ADD
|
||||||
|
* @brief 向量加法
|
||||||
|
* @param a 第一个向量
|
||||||
|
* @param b 第二个向量
|
||||||
|
* @return a + b
|
||||||
|
* @ingroup Vector2
|
||||||
|
*/
|
||||||
|
#define GE_VEC2I_ADD(a, b) GE_VEC2I((a).x + (b).x, (a).y + (b).y)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def GE_VEC2I_SUB
|
||||||
|
* @brief 向量减法
|
||||||
|
* @param a 被减向量
|
||||||
|
* @param b 减数向量
|
||||||
|
* @return a - b
|
||||||
|
* @ingroup Vector2
|
||||||
|
*/
|
||||||
|
#define GE_VEC2I_SUB(a, b) GE_VEC2I((a).x - (b).x, (a).y - (b).y)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def GE_VEC2I_MUL
|
||||||
|
* @brief 向量分量乘法
|
||||||
|
* @param a 第一个向量
|
||||||
|
* @param b 第二个向量
|
||||||
|
* @return a * b (分量相乘)
|
||||||
|
* @ingroup Vector2
|
||||||
|
*/
|
||||||
|
#define GE_VEC2I_MUL(a, b) GE_VEC2I((a).x * (b).x, (a).y * (b).y)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def GE_VEC2I_DIV
|
||||||
|
* @brief 向量分量除法
|
||||||
|
* @param a 被除向量
|
||||||
|
* @param b 除数向量
|
||||||
|
* @return a / b (分量相除),如果分母为0会出现严重错误
|
||||||
|
* @ingroup Vector2
|
||||||
|
*/
|
||||||
|
#define GE_VEC2I_DIV(a, b) GE_VEC2I( \
|
||||||
|
(a).x / (b).x, \
|
||||||
|
(a).y / (b).y \
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def GE_VEC2I_DIV_SAFE
|
||||||
|
* @brief 向量分量除法
|
||||||
|
* @param a 被除向量
|
||||||
|
* @param b 除数向量
|
||||||
|
* @return a / b (分量相除),除零保护(分母为0返回0)
|
||||||
|
* @ingroup Vector2
|
||||||
|
*/
|
||||||
|
#define GE_VEC2I_DIV_SAFE(a, b) GE_VEC2I( \
|
||||||
|
(b).x ? (a).x / (b).x : 0, \
|
||||||
|
(b).y ? (a).y / (b).y : 0 \
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def GE_VEC2I_MUL_SCALAR
|
||||||
|
* @brief 向量标量乘法
|
||||||
|
* @param v 目标向量
|
||||||
|
* @param s 标量值
|
||||||
|
* @return v * s
|
||||||
|
* @ingroup Vector2
|
||||||
|
*/
|
||||||
|
#define GE_VEC2I_MUL_SCALAR(v, s) GE_VEC2I((v).x * (s), (v).y * (s))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def GE_VEC2I_DOT
|
||||||
|
* @brief 向量点积
|
||||||
|
* @param a 第一个向量
|
||||||
|
* @param b 第二个向量
|
||||||
|
* @return a · b
|
||||||
|
* @ingroup Vector2
|
||||||
|
*/
|
||||||
|
#define GE_VEC2I_DOT(a, b) ((a).x * (b).x + (a).y * (b).y)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def GE_VEC2I_LENGTH_SQR
|
||||||
|
* @brief 向量长度平方
|
||||||
|
* @param v 目标向量
|
||||||
|
* @return |v|²
|
||||||
|
* @ingroup Vector2
|
||||||
|
*/
|
||||||
|
#define GE_VEC2I_LENGTH_SQR(v) (GE_VEC2I_DOT(v, v))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def GE_VEC2I_DISTANCE_SQR
|
||||||
|
* @brief 两点间距离平方
|
||||||
|
* @param a 起点向量
|
||||||
|
* @param b 终点向量
|
||||||
|
* @return |a - b|²
|
||||||
|
* @ingroup Vector2
|
||||||
|
*/
|
||||||
|
#define GE_VEC2I_DISTANCE_SQR(a, b) GE_VEC2I_LENGTH_SQR(GE_VEC2I_SUB(a, b))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def GE_VEC2I_MIN
|
||||||
|
* @brief 分量最小值
|
||||||
|
* @param a 第一个向量
|
||||||
|
* @param b 第二个向量
|
||||||
|
* @return (min(a.x, b.x), min(a.y, b.y))
|
||||||
|
* @ingroup Vector2
|
||||||
|
*/
|
||||||
|
#define GE_VEC2I_MIN(a, b) GE_VEC2I( \
|
||||||
|
((a).x < (b).x) ? (a).x : (b).x, \
|
||||||
|
((a).y < (b).y) ? (a).y : (b).y \
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def GE_VEC2I_MAX
|
||||||
|
* @brief 分量最大值
|
||||||
|
* @param a 第一个向量
|
||||||
|
* @param b 第二个向量
|
||||||
|
* @return (max(a.x, b.x), max(a.y, b.y))
|
||||||
|
* @ingroup Vector2
|
||||||
|
*/
|
||||||
|
#define GE_VEC2I_MAX(a, b) GE_VEC2I( \
|
||||||
|
((a).x > (b).x) ? (a).x : (b).x, \
|
||||||
|
((a).y > (b).y) ? (a).y : (b).y \
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def GE_VEC2I_CLAMP
|
||||||
|
* @brief 分量钳制(钳制操作的意思是:如果某个分量小于最小值,
|
||||||
|
* 则将其设置为最小值;如果大于最大值,则设置为最大值;
|
||||||
|
* 如果在最小值和最大值之间,则保持不变。)
|
||||||
|
* @param v 目标向量
|
||||||
|
* @param min_vec 最小值向量
|
||||||
|
* @param max_vec 最大值向量
|
||||||
|
* @return 钳制后的向量
|
||||||
|
* @ingroup Vector2
|
||||||
|
*/
|
||||||
|
#define GE_VEC2I_CLAMP(v, min_vec, max_vec) \
|
||||||
|
GE_VEC2I_MIN(GE_VEC2I_MAX(v, min_vec), max_vec)
|
||||||
|
|
||||||
|
// ========================= 操作符 =========================
|
||||||
|
/**
|
||||||
|
* @def GE_VEC2I_EQUAL
|
||||||
|
* @brief 判断两个向量是否相等(分量完全相等)
|
||||||
|
* @param a 第一个向量
|
||||||
|
* @param b 第二个向量
|
||||||
|
* @return 1 表示相等,0 表示不相等
|
||||||
|
* @ingroup Vector2
|
||||||
|
*/
|
||||||
|
#define GE_VEC2I_EQUAL(a, b) (((a).x == (b).x) && ((a).y == (b).y))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def GE_VEC2I_NOT_EQUAL
|
||||||
|
* @brief 判断两个向量是否不相等
|
||||||
|
* @param a 第一个向量
|
||||||
|
* @param b 第二个向量
|
||||||
|
* @return 1 表示不相等,0 表示相等
|
||||||
|
* @ingroup Vector2
|
||||||
|
*/
|
||||||
|
#define GE_VEC2I_NOT_EQUAL(a, b) (((a).x != (b).x) || ((a).y != (b).y))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def GE_VEC2I_APPROX_EQUAL
|
||||||
|
* @brief 判断两个向量是否近似相等(在容忍度范围内)
|
||||||
|
* @param a 第一个向量
|
||||||
|
* @param b 第二个向量
|
||||||
|
* @param tolerance 容忍度值
|
||||||
|
* @return 1 表示近似相等,0 表示不近似相等
|
||||||
|
* @note 对于整数向量,通常使用精确比较,但此宏提供了容忍度机制
|
||||||
|
* @ingroup Vector2
|
||||||
|
*/
|
||||||
|
#define GE_VEC2I_APPROX_EQUAL(a, b, tolerance) \
|
||||||
|
((GE_ABS((a).x - (b).x) <= tolerance) && \
|
||||||
|
(GE_ABS((a).y - (b).y) <= tolerance))
|
||||||
|
|
||||||
|
// ========================= 可选前缀模式 =========================
|
||||||
|
#ifdef GE_VEC2I_USE_SHORT_NAMES
|
||||||
|
#define vec2i(x, y) GE_VEC2I(x, y)
|
||||||
|
#define vec2i_zero GE_VEC2I_ZERO
|
||||||
|
#define vec2i_one GE_VEC2I_ONE
|
||||||
|
#define vec2i_add(a, b) GE_VEC2I_ADD(a, b)
|
||||||
|
#define vec2i_sub(a, b) GE_VEC2I_SUB(a, b)
|
||||||
|
#define vec2i_mul(a, b) GE_VEC2I_MUL(a, b)
|
||||||
|
#define vec2i_mul_scalar(v, s) GE_VEC2I_MUL_SCALAR(v, s)
|
||||||
|
#define vec2i_div(a, b) GE_VEC2I_DIV(a, b)
|
||||||
|
#define vec2i_div_safe(a, b) GE_VEC2I_DIV_SAFE(a, b)
|
||||||
|
#define vec2i_dot(a, b) GE_VEC2I_DOT(a, b)
|
||||||
|
#define vec2i_length_sqr(v) GE_VEC2I_LENGTH_SQR(v)
|
||||||
|
#define vec2i_min(a, b) GE_VEC2I_MIN(a, b)
|
||||||
|
#define vec2i_max(a, b) GE_VEC2I_MAX(a, b)
|
||||||
|
#define vec2i_clamp(v, min, max) GE_VEC2I_CLAMP(v, min, max)
|
||||||
|
#define vec2i_equal(a, b) GE_VEC2I_EQUAL(a, b)
|
||||||
|
#define vec2i_not_equal(a, b) GE_VEC2I_NOT_EQUAL(a, b)
|
||||||
|
#define vec2i_approx_equal(a, b, tol) GE_VEC2I_APPROX_EQUAL(a, b, tol)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // __GE_VECTOR2_H__
|
||||||
Reference in New Issue
Block a user