MiMoEngine ASM: x86-64 MASM汇编+C混合架构游戏引擎
核心热路径(数学/内存/字符串/性能分析)用MASM汇编(SSE/AVX2) 外围系统(窗口/渲染/ECS/音频/UI/脚本)用C语言 含自研编辑器、VisualScript积木编程、中文渲染(glyph_atlas)
This commit is contained in:
commit
2d56cb3bc9
91 files changed
+9988
No files matched your search
@@ -0,0 +1,135 @@
|
||||
// ============================================================================
|
||||
// block_core.c - 积木核心操作
|
||||
// ============================================================================
|
||||
|
||||
#include "block_core.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// 创建积木
|
||||
Block* block_create(BlockType type, float x, float y) {
|
||||
Block* block = (Block*)calloc(1, sizeof(Block));
|
||||
if (!block) return NULL;
|
||||
|
||||
block->type = type;
|
||||
block->x = x;
|
||||
block->y = y;
|
||||
block->width = 200;
|
||||
block->height = 60;
|
||||
|
||||
// 初始化槽位
|
||||
block->input_count = 0;
|
||||
block->output_count = 0;
|
||||
|
||||
for (int i = 0; i < MAX_BLOCK_INPUTS; i++) {
|
||||
block->inputs[i].owner = block;
|
||||
block->inputs[i].id = i;
|
||||
}
|
||||
for (int i = 0; i < MAX_BLOCK_OUTPUTS; i++) {
|
||||
block->outputs[i].owner = block;
|
||||
block->outputs[i].id = i;
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
// 销毁积木
|
||||
void block_destroy(Block* block) {
|
||||
if (!block) return;
|
||||
free(block);
|
||||
}
|
||||
|
||||
// 添加输入槽位
|
||||
BlockSlot* block_add_input(Block* block, SlotKind kind, SlotValueType value_type, const char* label) {
|
||||
if (block->input_count >= MAX_BLOCK_INPUTS) return NULL;
|
||||
|
||||
BlockSlot* slot = &block->inputs[block->input_count];
|
||||
slot->kind = kind;
|
||||
slot->value_type = value_type;
|
||||
if (label) strncpy(slot->label, label, MAX_BLOCK_LABEL - 1);
|
||||
slot->value.type = value_type;
|
||||
|
||||
block->input_count++;
|
||||
return slot;
|
||||
}
|
||||
|
||||
// 添加输出槽位
|
||||
BlockSlot* block_add_output(Block* block, SlotKind kind, SlotValueType value_type, const char* label) {
|
||||
if (block->output_count >= MAX_BLOCK_OUTPUTS) return NULL;
|
||||
|
||||
BlockSlot* slot = &block->outputs[block->output_count];
|
||||
slot->kind = kind;
|
||||
slot->value_type = value_type;
|
||||
if (label) strncpy(slot->label, label, MAX_BLOCK_LABEL - 1);
|
||||
|
||||
block->output_count++;
|
||||
return slot;
|
||||
}
|
||||
|
||||
// 连接两个槽位
|
||||
bool block_connect(BlockSlot* from, BlockSlot* to) {
|
||||
if (!from || !to) return false;
|
||||
if (from->owner == to->owner) return false; // 不能自连
|
||||
|
||||
// 断开旧连接
|
||||
if (from->connected) from->connected->connected = NULL;
|
||||
if (to->connected) to->connected->connected = NULL;
|
||||
|
||||
from->connected = to;
|
||||
to->connected = from;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 断开槽位
|
||||
void block_disconnect(BlockSlot* slot) {
|
||||
if (!slot || !slot->connected) return;
|
||||
slot->connected->connected = NULL;
|
||||
slot->connected = NULL;
|
||||
}
|
||||
|
||||
// 检查槽位是否已连接
|
||||
bool block_slot_connected(BlockSlot* slot) {
|
||||
return slot && slot->connected != NULL;
|
||||
}
|
||||
|
||||
// 获取槽位的值
|
||||
SlotValue block_slot_get_value(BlockSlot* slot) {
|
||||
if (!slot) {
|
||||
SlotValue empty = {0};
|
||||
return empty;
|
||||
}
|
||||
|
||||
// 如果有连接,从连接的积木获取值
|
||||
if (slot->connected && slot->connected->owner) {
|
||||
Block* source = slot->connected->owner;
|
||||
// 从源积木的输出槽位获取值
|
||||
for (int i = 0; i < source->output_count; i++) {
|
||||
if (&source->outputs[i] == slot->connected) {
|
||||
return source->outputs[i].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return slot->value;
|
||||
}
|
||||
|
||||
// 设置槽位的值
|
||||
void block_slot_set_value(BlockSlot* slot, SlotValue value) {
|
||||
if (slot) slot->value = value;
|
||||
}
|
||||
|
||||
// 积木是否包含点
|
||||
bool block_contains_point(Block* block, float px, float py) {
|
||||
return px >= block->x && px <= block->x + block->width
|
||||
&& py >= block->y && py <= block->y + block->height;
|
||||
}
|
||||
|
||||
// 获取槽位的世界位置
|
||||
void block_slot_world_pos(BlockSlot* slot, float* wx, float* wy) {
|
||||
if (!slot || !slot->owner) {
|
||||
*wx = 0; *wy = 0;
|
||||
return;
|
||||
}
|
||||
*wx = slot->owner->x + slot->x;
|
||||
*wy = slot->owner->y + slot->y;
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
#pragma once
|
||||
|
||||
// ============================================================================
|
||||
// block_core.h - 积木核心数据结构
|
||||
// ============================================================================
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define MAX_BLOCK_INPUTS 8
|
||||
#define MAX_BLOCK_OUTPUTS 8
|
||||
#define MAX_BLOCK_LABEL 64
|
||||
|
||||
// 颜色
|
||||
typedef struct {
|
||||
float r, g, b, a;
|
||||
} Color;
|
||||
|
||||
typedef Color BlockColor;
|
||||
|
||||
// 预定义颜色
|
||||
#define BLOCK_COLOR_EVENT {0.9f, 0.2f, 0.2f, 1.0f} // 红
|
||||
#define BLOCK_COLOR_ACTION {0.2f, 0.5f, 0.9f, 1.0f} // 蓝
|
||||
#define BLOCK_COLOR_CONTROL {0.2f, 0.7f, 0.3f, 1.0f} // 绿
|
||||
#define BLOCK_COLOR_OPERATOR {0.9f, 0.6f, 0.1f, 1.0f} // 橙
|
||||
#define BLOCK_COLOR_VARIABLE {0.7f, 0.3f, 0.8f, 1.0f} // 紫
|
||||
#define BLOCK_COLOR_FUNCTION {0.9f, 0.8f, 0.2f, 1.0f} // 黄
|
||||
#define BLOCK_COLOR_DATA {0.4f, 0.7f, 0.9f, 1.0f} // 浅蓝
|
||||
|
||||
// 槽位值类型
|
||||
typedef enum {
|
||||
SLOT_VALUE_NUMBER = 0,
|
||||
SLOT_VALUE_STRING,
|
||||
SLOT_VALUE_BOOLEAN,
|
||||
SLOT_VALUE_VECTOR3,
|
||||
SLOT_VALUE_ANY
|
||||
} SlotValueType;
|
||||
|
||||
// 槽位值
|
||||
typedef struct {
|
||||
SlotValueType type;
|
||||
union {
|
||||
double number;
|
||||
char string[256];
|
||||
int boolean;
|
||||
struct { float x, y, z; } vector3;
|
||||
} as;
|
||||
} SlotValue;
|
||||
|
||||
// 槽位类型
|
||||
typedef enum {
|
||||
SLOT_STATEMENT = 0, // 语句槽位 (执行流)
|
||||
SLOT_VALUE // 值槽位 (数据流)
|
||||
} SlotKind;
|
||||
|
||||
// 槽位
|
||||
typedef struct BlockSlot {
|
||||
int id;
|
||||
SlotKind kind;
|
||||
SlotValueType value_type;
|
||||
char label[MAX_BLOCK_LABEL];
|
||||
|
||||
struct Block* owner; // 所属积木
|
||||
struct BlockSlot* connected; // 连接的槽位
|
||||
|
||||
SlotValue value; // 当前值 (仅值槽位)
|
||||
|
||||
float x, y; // 相对于积木的位置
|
||||
float width, height; // 槽位尺寸
|
||||
} BlockSlot;
|
||||
|
||||
// 积木类型
|
||||
typedef enum {
|
||||
// 事件
|
||||
BLOCK_EVENT_START = 0,
|
||||
BLOCK_EVENT_UPDATE,
|
||||
BLOCK_EVENT_COLLISION,
|
||||
BLOCK_EVENT_KEY_PRESSED,
|
||||
BLOCK_EVENT_MOUSE_CLICK,
|
||||
BLOCK_EVENT_TIMER,
|
||||
|
||||
// 动作
|
||||
BLOCK_ACTION_MOVE,
|
||||
BLOCK_ACTION_ROTATE,
|
||||
BLOCK_ACTION_SCALE,
|
||||
BLOCK_ACTION_SET_POSITION,
|
||||
BLOCK_ACTION_SET_ROTATION,
|
||||
BLOCK_ACTION_PLAY_SOUND,
|
||||
BLOCK_ACTION_DESTROY,
|
||||
BLOCK_ACTION_INSTANTIATE,
|
||||
|
||||
// 控制
|
||||
BLOCK_CONTROL_IF,
|
||||
BLOCK_CONTROL_IF_ELSE,
|
||||
BLOCK_CONTROL_REPEAT,
|
||||
BLOCK_CONTROL_REPEAT_TIMES,
|
||||
BLOCK_CONTROL_WHILE,
|
||||
BLOCK_CONTROL_WAIT,
|
||||
BLOCK_CONTROL_FUNCTION_DEF,
|
||||
|
||||
// 运算
|
||||
BLOCK_OP_ADD,
|
||||
BLOCK_OP_SUB,
|
||||
BLOCK_OP_MUL,
|
||||
BLOCK_OP_DIV,
|
||||
BLOCK_OP_MOD,
|
||||
BLOCK_OP_EQUALS,
|
||||
BLOCK_OP_NOT_EQUALS,
|
||||
BLOCK_OP_GREATER,
|
||||
BLOCK_OP_LESS,
|
||||
BLOCK_OP_AND,
|
||||
BLOCK_OP_OR,
|
||||
BLOCK_OP_NOT,
|
||||
|
||||
// 变量
|
||||
BLOCK_VAR_GET,
|
||||
BLOCK_VAR_SET,
|
||||
BLOCK_VAR_CREATE,
|
||||
|
||||
// 函数
|
||||
BLOCK_FUNC_CALL,
|
||||
BLOCK_FUNC_RETURN,
|
||||
|
||||
BLOCK_TYPE_COUNT
|
||||
} BlockType;
|
||||
|
||||
// 积木类型信息
|
||||
typedef struct {
|
||||
BlockType type;
|
||||
const char* name;
|
||||
const char* label;
|
||||
const char* category;
|
||||
BlockColor color;
|
||||
int input_count;
|
||||
int output_count;
|
||||
SlotValueType input_types[MAX_BLOCK_INPUTS];
|
||||
SlotValueType output_types[MAX_BLOCK_OUTPUTS];
|
||||
const char* input_labels[MAX_BLOCK_INPUTS];
|
||||
const char* output_labels[MAX_BLOCK_OUTPUTS];
|
||||
} BlockTypeInfo;
|
||||
|
||||
// 积木
|
||||
typedef struct Block {
|
||||
int id;
|
||||
BlockType type;
|
||||
char label[MAX_BLOCK_LABEL];
|
||||
|
||||
float x, y; // 画布位置
|
||||
float width, height; // 尺寸
|
||||
Color color; // 颜色
|
||||
|
||||
BlockSlot inputs[MAX_BLOCK_INPUTS];
|
||||
int input_count;
|
||||
|
||||
BlockSlot outputs[MAX_BLOCK_OUTPUTS];
|
||||
int output_count;
|
||||
|
||||
struct Block* next; // 下一个积木 (堆叠)
|
||||
struct Block* parent_if; // 所属 if 块 (用于子语句)
|
||||
|
||||
struct BlockWorkspace* workspace;
|
||||
void* user_data;
|
||||
} Block;
|
||||
|
||||
// 连接
|
||||
typedef struct BlockConnection {
|
||||
BlockSlot* from;
|
||||
BlockSlot* to;
|
||||
float points[8]; // 贝塞尔曲线控制点
|
||||
} BlockConnection;
|
||||
|
||||
// 函数声明
|
||||
Block* block_create(BlockType type, float x, float y);
|
||||
void block_destroy(Block* block);
|
||||
BlockSlot* block_add_input(Block* block, SlotKind kind, SlotValueType value_type, const char* label);
|
||||
BlockSlot* block_add_output(Block* block, SlotKind kind, SlotValueType value_type, const char* label);
|
||||
bool block_connect(BlockSlot* from, BlockSlot* to);
|
||||
void block_disconnect(BlockSlot* slot);
|
||||
SlotValue block_slot_get_value(BlockSlot* slot);
|
||||
void block_slot_set_value(BlockSlot* slot, SlotValue value);
|
||||
bool block_contains_point(Block* block, float px, float py);
|
||||
void block_slot_world_pos(BlockSlot* slot, float* wx, float* wy);
|
||||
@@ -0,0 +1,225 @@
|
||||
// ============================================================================
|
||||
// block_editor.c - 编辑器主界面实现
|
||||
// ============================================================================
|
||||
|
||||
#include "block_editor.h"
|
||||
#include "block_types.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int block_editor_init(BlockEditor* editor, void* device, void* context, int width, int height) {
|
||||
memset(editor, 0, sizeof(BlockEditor));
|
||||
|
||||
editor->width = width;
|
||||
editor->height = height;
|
||||
editor->toolbar_height = 30;
|
||||
editor->properties_width = 200;
|
||||
editor->visible = 0;
|
||||
|
||||
// 创建工作区
|
||||
editor->workspace = workspace_create();
|
||||
if (!editor->workspace) return 0;
|
||||
|
||||
// 初始化渲染器
|
||||
if (!block_renderer_init(&editor->renderer, device, context)) {
|
||||
workspace_destroy(editor->workspace);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 初始化交互
|
||||
interaction_init(&editor->interaction);
|
||||
|
||||
// 初始化面板
|
||||
palette_init(&editor->palette);
|
||||
editor->palette.x = 0;
|
||||
editor->palette.y = editor->toolbar_height;
|
||||
|
||||
// 创建执行器
|
||||
editor->executor = executor_create(editor->workspace);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void block_editor_shutdown(BlockEditor* editor) {
|
||||
if (editor->executor) executor_destroy(editor->executor);
|
||||
block_renderer_shutdown(&editor->renderer);
|
||||
if (editor->workspace) workspace_destroy(editor->workspace);
|
||||
}
|
||||
|
||||
void block_editor_show(BlockEditor* editor) {
|
||||
editor->visible = 1;
|
||||
}
|
||||
|
||||
void block_editor_hide(BlockEditor* editor) {
|
||||
editor->visible = 0;
|
||||
}
|
||||
|
||||
int block_editor_is_visible(BlockEditor* editor) {
|
||||
return editor->visible;
|
||||
}
|
||||
|
||||
void block_editor_update(BlockEditor* editor, double delta_time) {
|
||||
if (!editor->visible) return;
|
||||
|
||||
// 更新交互
|
||||
interaction_update(&editor->interaction, editor->workspace);
|
||||
|
||||
// 更新执行器
|
||||
if (editor->executor->state == EXEC_RUNNING) {
|
||||
executor_update(editor->executor, delta_time);
|
||||
}
|
||||
}
|
||||
|
||||
void block_editor_render(BlockEditor* editor) {
|
||||
if (!editor->visible) return;
|
||||
|
||||
block_renderer_begin(&editor->renderer);
|
||||
|
||||
// 绘制工具栏
|
||||
block_renderer_add_quad(&editor->renderer, 0, 0, (float)editor->width, (float)editor->toolbar_height,
|
||||
0.2f, 0.2f, 0.25f, 1.0f);
|
||||
block_renderer_draw_text(&editor->renderer, "积木编辑器", 5, 5, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
// 绘制面板
|
||||
palette_render(&editor->palette, &editor->renderer);
|
||||
|
||||
// 绘制工作区积木
|
||||
float ws_x = editor->palette.width;
|
||||
float ws_y = editor->toolbar_height;
|
||||
float ws_w = (float)editor->width - editor->palette.width - editor->properties_width;
|
||||
float ws_h = (float)editor->height - editor->toolbar_height;
|
||||
|
||||
// 工作区背景
|
||||
block_renderer_add_quad(&editor->renderer, ws_x, ws_y, ws_w, ws_h,
|
||||
0.12f, 0.12f, 0.15f, 1.0f);
|
||||
|
||||
// 绘制连接线
|
||||
for (int i = 0; i < editor->workspace->connection_count; i++) {
|
||||
BlockConnection* conn = &editor->workspace->connections[i];
|
||||
if (conn->from && conn->to) {
|
||||
block_renderer_draw_connection(&editor->renderer, conn->from, conn->to,
|
||||
editor->workspace->view_x, editor->workspace->view_y, editor->workspace->zoom);
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制积木
|
||||
for (int i = 0; i < editor->workspace->block_count; i++) {
|
||||
Block* block = editor->workspace->blocks[i];
|
||||
block_renderer_draw_block(&editor->renderer, block,
|
||||
editor->workspace->view_x, editor->workspace->view_y, editor->workspace->zoom);
|
||||
|
||||
// 高亮选中的积木
|
||||
if (block == editor->workspace->selected_block) {
|
||||
float x = (block->x - editor->workspace->view_x) * editor->workspace->zoom;
|
||||
float y = (block->y - editor->workspace->view_y) * editor->workspace->zoom;
|
||||
float w = block->width * editor->workspace->zoom;
|
||||
float h = block->height * editor->workspace->zoom;
|
||||
block_renderer_add_quad(&editor->renderer, x - 2, y - 2, w + 4, h + 4,
|
||||
1.0f, 1.0f, 0.0f, 0.3f);
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制正在拖拽的连接线
|
||||
if (editor->interaction.connecting_from) {
|
||||
float x1, y1;
|
||||
block_slot_world_pos(editor->interaction.connecting_from, &x1, &y1);
|
||||
x1 = (x1 - editor->workspace->view_x) * editor->workspace->zoom;
|
||||
y1 = (y1 - editor->workspace->view_y) * editor->workspace->zoom;
|
||||
float x2 = (float)editor->interaction.mouse_x;
|
||||
float y2 = (float)editor->interaction.mouse_y;
|
||||
block_renderer_add_line(&editor->renderer, x1, y1, x2, y2, 1.0f, 1.0f, 0.0f, 0.8f);
|
||||
}
|
||||
|
||||
block_renderer_end(&editor->renderer);
|
||||
}
|
||||
|
||||
void block_editor_on_mouse_move(BlockEditor* editor, int x, int y) {
|
||||
if (!editor->visible) return;
|
||||
interaction_mouse_move(&editor->interaction, editor->workspace, x, y);
|
||||
}
|
||||
|
||||
void block_editor_on_mouse_down(BlockEditor* editor, int button) {
|
||||
if (!editor->visible) return;
|
||||
|
||||
int x = editor->interaction.mouse_x;
|
||||
int y = editor->interaction.mouse_y;
|
||||
|
||||
// 检查面板点击
|
||||
if (x < editor->palette.width && y >= editor->toolbar_height) {
|
||||
BlockType type = palette_handle_click(&editor->palette, (float)x, (float)y);
|
||||
if (type >= 0) {
|
||||
block_editor_create_block_from_palette(editor, type, 300, 100);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
interaction_mouse_down(&editor->interaction, editor->workspace, button);
|
||||
}
|
||||
|
||||
void block_editor_on_mouse_up(BlockEditor* editor, int button) {
|
||||
if (!editor->visible) return;
|
||||
interaction_mouse_up(&editor->interaction, editor->workspace, button);
|
||||
}
|
||||
|
||||
void block_editor_on_mouse_wheel(BlockEditor* editor, int delta) {
|
||||
if (!editor->visible) return;
|
||||
interaction_mouse_wheel(&editor->interaction, editor->workspace, delta);
|
||||
}
|
||||
|
||||
void block_editor_on_key(BlockEditor* editor, int key) {
|
||||
if (!editor->visible) return;
|
||||
interaction_key_down(&editor->interaction, editor->workspace, key);
|
||||
}
|
||||
|
||||
void block_editor_on_resize(BlockEditor* editor, int width, int height) {
|
||||
editor->width = width;
|
||||
editor->height = height;
|
||||
}
|
||||
|
||||
void block_editor_create_block_from_palette(BlockEditor* editor, BlockType type, float x, float y) {
|
||||
Block* block = block_create(type, x, y);
|
||||
if (!block) return;
|
||||
|
||||
const BlockTypeInfo* info = block_type_get_info(type);
|
||||
if (info) {
|
||||
strncpy(block->label, info->label, MAX_BLOCK_LABEL - 1);
|
||||
|
||||
// 创建输入槽位
|
||||
for (int i = 0; i < info->input_count; i++) {
|
||||
block_add_input(block,
|
||||
i == 0 ? SLOT_STATEMENT : SLOT_VALUE,
|
||||
info->input_types[i],
|
||||
info->input_labels[i]);
|
||||
}
|
||||
|
||||
// 创建输出槽位
|
||||
for (int i = 0; i < info->output_count; i++) {
|
||||
block_add_output(block,
|
||||
info->output_types[i] == SLOT_VALUE_ANY ? SLOT_STATEMENT : SLOT_VALUE,
|
||||
info->output_types[i],
|
||||
info->output_labels[i]);
|
||||
}
|
||||
}
|
||||
|
||||
workspace_add_block(editor->workspace, block);
|
||||
}
|
||||
|
||||
int block_editor_save(BlockEditor* editor, const char* filepath) {
|
||||
return serializer_save(editor->workspace, filepath);
|
||||
}
|
||||
|
||||
int block_editor_load(BlockEditor* editor, const char* filepath) {
|
||||
return serializer_load(editor->workspace, filepath);
|
||||
}
|
||||
|
||||
void block_editor_run(BlockEditor* editor) {
|
||||
executor_start(editor->executor);
|
||||
}
|
||||
|
||||
void block_editor_stop(BlockEditor* editor) {
|
||||
executor_stop(editor->executor);
|
||||
}
|
||||
|
||||
void block_editor_pause(BlockEditor* editor) {
|
||||
executor_pause(editor->executor);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
// ============================================================================
|
||||
// block_editor.h - 编辑器主界面
|
||||
// ============================================================================
|
||||
|
||||
#include "block_workspace.h"
|
||||
#include "block_renderer.h"
|
||||
#include "block_interaction.h"
|
||||
#include "block_palette.h"
|
||||
#include "block_executor.h"
|
||||
#include "block_serializer.h"
|
||||
|
||||
typedef struct BlockEditor {
|
||||
BlockWorkspace* workspace;
|
||||
BlockRenderer renderer;
|
||||
BlockInteraction interaction;
|
||||
BlockPalette palette;
|
||||
BlockExecutor* executor;
|
||||
|
||||
int width, height;
|
||||
int visible;
|
||||
|
||||
// 工具栏
|
||||
int toolbar_height;
|
||||
|
||||
// 属性面板
|
||||
int properties_width;
|
||||
Block* selected_block;
|
||||
} BlockEditor;
|
||||
|
||||
// 初始化/销毁
|
||||
int block_editor_init(BlockEditor* editor, void* device, void* context, int width, int height);
|
||||
void block_editor_shutdown(BlockEditor* editor);
|
||||
|
||||
// 显示/隐藏
|
||||
void block_editor_show(BlockEditor* editor);
|
||||
void block_editor_hide(BlockEditor* editor);
|
||||
int block_editor_is_visible(BlockEditor* editor);
|
||||
|
||||
// 更新/渲染
|
||||
void block_editor_update(BlockEditor* editor, double delta_time);
|
||||
void block_editor_render(BlockEditor* editor);
|
||||
|
||||
// 输入处理
|
||||
void block_editor_on_mouse_move(BlockEditor* editor, int x, int y);
|
||||
void block_editor_on_mouse_down(BlockEditor* editor, int button);
|
||||
void block_editor_on_mouse_up(BlockEditor* editor, int button);
|
||||
void block_editor_on_mouse_wheel(BlockEditor* editor, int delta);
|
||||
void block_editor_on_key(BlockEditor* editor, int key);
|
||||
void block_editor_on_resize(BlockEditor* editor, int width, int height);
|
||||
|
||||
// 从面板拖拽创建积木
|
||||
void block_editor_create_block_from_palette(BlockEditor* editor, BlockType type, float x, float y);
|
||||
|
||||
// 保存/加载
|
||||
int block_editor_save(BlockEditor* editor, const char* filepath);
|
||||
int block_editor_load(BlockEditor* editor, const char* filepath);
|
||||
|
||||
// 运行控制
|
||||
void block_editor_run(BlockEditor* editor);
|
||||
void block_editor_stop(BlockEditor* editor);
|
||||
void block_editor_pause(BlockEditor* editor);
|
||||
@@ -0,0 +1,191 @@
|
||||
// ============================================================================
|
||||
// block_executor.c - 积木执行引擎实现
|
||||
// ============================================================================
|
||||
|
||||
#include "block_executor.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
BlockExecutor* executor_create(BlockWorkspace* ws) {
|
||||
BlockExecutor* exec = (BlockExecutor*)calloc(1, sizeof(BlockExecutor));
|
||||
if (!exec) return NULL;
|
||||
exec->workspace = ws;
|
||||
return exec;
|
||||
}
|
||||
|
||||
void executor_destroy(BlockExecutor* exec) {
|
||||
free(exec);
|
||||
}
|
||||
|
||||
void executor_start(BlockExecutor* exec) {
|
||||
exec->state = EXEC_RUNNING;
|
||||
exec->call_stack_top = 0;
|
||||
exec->current_block = NULL;
|
||||
|
||||
// 找到 on_start 事件积木
|
||||
for (int i = 0; i < exec->workspace->block_count; i++) {
|
||||
Block* block = exec->workspace->blocks[i];
|
||||
if (block->type == BLOCK_EVENT_START) {
|
||||
exec->current_block = block;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void executor_stop(BlockExecutor* exec) {
|
||||
exec->state = EXEC_IDLE;
|
||||
exec->current_block = NULL;
|
||||
}
|
||||
|
||||
void executor_pause(BlockExecutor* exec) {
|
||||
if (exec->state == EXEC_RUNNING) {
|
||||
exec->state = EXEC_PAUSED;
|
||||
}
|
||||
}
|
||||
|
||||
void executor_resume(BlockExecutor* exec) {
|
||||
if (exec->state == EXEC_PAUSED) {
|
||||
exec->state = EXEC_RUNNING;
|
||||
}
|
||||
}
|
||||
|
||||
void executor_step(BlockExecutor* exec) {
|
||||
if (!exec->current_block) return;
|
||||
|
||||
// 执行当前积木
|
||||
Block* current = exec->current_block;
|
||||
Block* next = NULL;
|
||||
|
||||
switch (current->type) {
|
||||
case BLOCK_EVENT_START:
|
||||
case BLOCK_EVENT_UPDATE:
|
||||
// 执行语句输出连接的积木
|
||||
if (current->output_count > 0) {
|
||||
BlockSlot* out = ¤t->outputs[0];
|
||||
if (out->connected && out->connected->owner) {
|
||||
next = out->connected->owner;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BLOCK_ACTION_MOVE:
|
||||
// 获取参数
|
||||
if (current->input_count >= 4) {
|
||||
SlotValue x = block_slot_get_value(¤t->inputs[1]);
|
||||
SlotValue y = block_slot_get_value(¤t->inputs[2]);
|
||||
SlotValue z = block_slot_get_value(¤t->inputs[3]);
|
||||
// TODO: 实际移动物体
|
||||
}
|
||||
next = current->next;
|
||||
break;
|
||||
|
||||
case BLOCK_CONTROL_IF:
|
||||
if (current->input_count >= 2) {
|
||||
SlotValue cond = block_slot_get_value(¤t->inputs[1]);
|
||||
if (cond.as.boolean) {
|
||||
// 执行 Then 分支
|
||||
if (current->output_count > 0) {
|
||||
BlockSlot* out = ¤t->outputs[0];
|
||||
if (out->connected && out->connected->owner) {
|
||||
next = out->connected->owner;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 执行 Else 分支
|
||||
if (current->output_count > 1) {
|
||||
BlockSlot* out = ¤t->outputs[1];
|
||||
if (out->connected && out->connected->owner) {
|
||||
next = out->connected->owner;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BLOCK_OP_ADD:
|
||||
if (current->input_count >= 2) {
|
||||
SlotValue a = block_slot_get_value(¤t->inputs[0]);
|
||||
SlotValue b = block_slot_get_value(¤t->inputs[1]);
|
||||
SlotValue result;
|
||||
result.type = SLOT_VALUE_NUMBER;
|
||||
result.as.number = a.as.number + b.as.number;
|
||||
if (current->output_count > 0) {
|
||||
current->outputs[0].value = result;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BLOCK_VAR_SET:
|
||||
if (current->input_count >= 2) {
|
||||
SlotValue val = block_slot_get_value(¤t->inputs[1]);
|
||||
// TODO: 获取变量名
|
||||
}
|
||||
next = current->next;
|
||||
break;
|
||||
|
||||
default:
|
||||
next = current->next;
|
||||
break;
|
||||
}
|
||||
|
||||
exec->current_block = next;
|
||||
|
||||
if (!exec->current_block) {
|
||||
exec->state = EXEC_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void executor_update(BlockExecutor* exec, double delta_time) {
|
||||
if (exec->state != EXEC_RUNNING) return;
|
||||
|
||||
// 处理等待
|
||||
if (exec->state == EXEC_WAITING) {
|
||||
exec->wait_timer -= delta_time;
|
||||
if (exec->wait_timer <= 0) {
|
||||
exec->state = EXEC_RUNNING;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 执行多步以避免卡顿
|
||||
int steps = 0;
|
||||
while (exec->current_block && steps < 100) {
|
||||
executor_step(exec);
|
||||
steps++;
|
||||
}
|
||||
}
|
||||
|
||||
void executor_set_variable(BlockExecutor* exec, const char* name, SlotValue value) {
|
||||
// 查找现有变量
|
||||
for (int i = 0; i < exec->variable_count; i++) {
|
||||
if (strcmp(exec->variables[i].name, name) == 0) {
|
||||
exec->variables[i].value = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 创建新变量
|
||||
if (exec->variable_count < MAX_VARIABLES) {
|
||||
Variable* var = &exec->variables[exec->variable_count++];
|
||||
strncpy(var->name, name, 63);
|
||||
var->value = value;
|
||||
var->used = 1;
|
||||
}
|
||||
}
|
||||
|
||||
SlotValue executor_get_variable(BlockExecutor* exec, const char* name) {
|
||||
for (int i = 0; i < exec->variable_count; i++) {
|
||||
if (strcmp(exec->variables[i].name, name) == 0) {
|
||||
return exec->variables[i].value;
|
||||
}
|
||||
}
|
||||
SlotValue empty = {0};
|
||||
return empty;
|
||||
}
|
||||
|
||||
void executor_create_variable(BlockExecutor* exec, const char* name) {
|
||||
SlotValue val = {0};
|
||||
val.type = SLOT_VALUE_NUMBER;
|
||||
val.as.number = 0;
|
||||
executor_set_variable(exec, name, val);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
// ============================================================================
|
||||
// block_executor.h - 积木执行引擎
|
||||
// ============================================================================
|
||||
|
||||
#include "block_workspace.h"
|
||||
|
||||
#define MAX_VARIABLES 256
|
||||
#define MAX_CALL_STACK 32
|
||||
#define MAX_EXEC_STACK 1024
|
||||
|
||||
// 变量
|
||||
typedef struct {
|
||||
char name[64];
|
||||
SlotValue value;
|
||||
int used;
|
||||
} Variable;
|
||||
|
||||
// 调用帧
|
||||
typedef struct {
|
||||
Block* block;
|
||||
Block* return_block;
|
||||
} CallFrame;
|
||||
|
||||
// 执行状态
|
||||
typedef enum {
|
||||
EXEC_IDLE = 0,
|
||||
EXEC_RUNNING,
|
||||
EXEC_WAITING,
|
||||
EXEC_PAUSED,
|
||||
EXEC_ERROR
|
||||
} ExecState;
|
||||
|
||||
// 执行器
|
||||
typedef struct BlockExecutor {
|
||||
BlockWorkspace* workspace;
|
||||
ExecState state;
|
||||
|
||||
Variable variables[MAX_VARIABLES];
|
||||
int variable_count;
|
||||
|
||||
CallFrame call_stack[MAX_CALL_STACK];
|
||||
int call_stack_top;
|
||||
|
||||
Block* current_block;
|
||||
double wait_timer;
|
||||
|
||||
int breakpoint_enabled;
|
||||
Block* breakpoint_block;
|
||||
} BlockExecutor;
|
||||
|
||||
// 初始化/销毁
|
||||
BlockExecutor* executor_create(BlockWorkspace* ws);
|
||||
void executor_destroy(BlockExecutor* exec);
|
||||
|
||||
// 控制
|
||||
void executor_start(BlockExecutor* exec);
|
||||
void executor_stop(BlockExecutor* exec);
|
||||
void executor_pause(BlockExecutor* exec);
|
||||
void executor_resume(BlockExecutor* exec);
|
||||
void executor_step(BlockExecutor* exec);
|
||||
|
||||
// 更新 (每帧调用)
|
||||
void executor_update(BlockExecutor* exec, double delta_time);
|
||||
|
||||
// 变量操作
|
||||
void executor_set_variable(BlockExecutor* exec, const char* name, SlotValue value);
|
||||
SlotValue executor_get_variable(BlockExecutor* exec, const char* name);
|
||||
void executor_create_variable(BlockExecutor* exec, const char* name);
|
||||
@@ -0,0 +1,137 @@
|
||||
// ============================================================================
|
||||
// block_interaction.c - 交互系统实现
|
||||
// ============================================================================
|
||||
|
||||
#include "block_interaction.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <Windows.h>
|
||||
|
||||
void interaction_init(BlockInteraction* inter) {
|
||||
memset(inter, 0, sizeof(BlockInteraction));
|
||||
}
|
||||
|
||||
void interaction_mouse_move(BlockInteraction* inter, BlockWorkspace* ws, int x, int y) {
|
||||
inter->mouse_x = x;
|
||||
inter->mouse_y = y;
|
||||
|
||||
// 拖拽积木
|
||||
if (inter->dragging_block) {
|
||||
float wx, wy;
|
||||
workspace_screen_to_world(ws, (float)x, (float)y, &wx, &wy);
|
||||
inter->dragging_block->x = wx - inter->drag_offset_x;
|
||||
inter->dragging_block->y = wy - inter->drag_offset_y;
|
||||
}
|
||||
|
||||
// 平移画布
|
||||
if (inter->panning) {
|
||||
float dx = (x - inter->pan_start_x) / ws->zoom;
|
||||
float dy = (y - inter->pan_start_y) / ws->zoom;
|
||||
ws->view_x = inter->view_start_x - dx;
|
||||
ws->view_y = inter->view_start_y - dy;
|
||||
}
|
||||
}
|
||||
|
||||
void interaction_mouse_down(BlockInteraction* inter, BlockWorkspace* ws, int button) {
|
||||
inter->mouse_down = 1;
|
||||
inter->mouse_button = button;
|
||||
|
||||
float wx, wy;
|
||||
workspace_screen_to_world(ws, (float)inter->mouse_x, (float)inter->mouse_y, &wx, &wy);
|
||||
|
||||
if (button == 0) { // 左键
|
||||
// 检查是否点击槽位
|
||||
BlockSlot* slot = workspace_find_slot_at(ws, wx, wy);
|
||||
if (slot) {
|
||||
// 开始连接
|
||||
inter->connecting_from = slot;
|
||||
inter->connect_start_x = wx;
|
||||
inter->connect_start_y = wy;
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否点击积木
|
||||
Block* block = workspace_find_block_at(ws, wx, wy);
|
||||
if (block) {
|
||||
inter->dragging_block = block;
|
||||
inter->drag_offset_x = wx - block->x;
|
||||
inter->drag_offset_y = wy - block->y;
|
||||
inter->selected_block = block;
|
||||
ws->selected_block = block;
|
||||
return;
|
||||
}
|
||||
|
||||
// 点击空白区域: 取消选择
|
||||
inter->selected_block = NULL;
|
||||
ws->selected_block = NULL;
|
||||
}
|
||||
|
||||
if (button == 1 || button == 2) { // 右键/中键: 平移
|
||||
inter->panning = 1;
|
||||
inter->pan_start_x = (float)inter->mouse_x;
|
||||
inter->pan_start_y = (float)inter->mouse_y;
|
||||
inter->view_start_x = ws->view_x;
|
||||
inter->view_start_y = ws->view_y;
|
||||
}
|
||||
}
|
||||
|
||||
void interaction_mouse_up(BlockInteraction* inter, BlockWorkspace* ws, int button) {
|
||||
inter->mouse_down = 0;
|
||||
|
||||
if (button == 0) {
|
||||
// 结束拖拽积木
|
||||
inter->dragging_block = NULL;
|
||||
|
||||
// 结束连接
|
||||
if (inter->connecting_from) {
|
||||
float wx, wy;
|
||||
workspace_screen_to_world(ws, (float)inter->mouse_x, (float)inter->mouse_y, &wx, &wy);
|
||||
|
||||
BlockSlot* target = workspace_find_slot_at(ws, wx, wy);
|
||||
if (target && target != inter->connecting_from) {
|
||||
// 尝试建立连接
|
||||
workspace_add_connection(ws, inter->connecting_from, target);
|
||||
}
|
||||
inter->connecting_from = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (button == 1 || button == 2) {
|
||||
inter->panning = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void interaction_key_down(BlockInteraction* inter, BlockWorkspace* ws, int key) {
|
||||
inter->key = key;
|
||||
inter->key_pressed = 1;
|
||||
|
||||
// 删除选中的积木
|
||||
if (key == 46 || key == 8) { // DELETE 或 BACKSPACE
|
||||
if (inter->selected_block) {
|
||||
workspace_remove_block(ws, inter->selected_block->id);
|
||||
inter->selected_block = NULL;
|
||||
ws->selected_block = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// 复制/粘贴 (Ctrl+C / Ctrl+V)
|
||||
if (key == 'C' && (GetAsyncKeyState(VK_CONTROL) & 0x8000)) {
|
||||
// TODO: 复制到剪贴板
|
||||
}
|
||||
if (key == 'V' && (GetAsyncKeyState(VK_CONTROL) & 0x8000)) {
|
||||
// TODO: 从剪贴板粘贴
|
||||
}
|
||||
}
|
||||
|
||||
void interaction_mouse_wheel(BlockInteraction* inter, BlockWorkspace* ws, int delta) {
|
||||
float zoom_factor = delta > 0 ? 1.1f : 0.9f;
|
||||
ws->zoom *= zoom_factor;
|
||||
if (ws->zoom < 0.25f) ws->zoom = 0.25f;
|
||||
if (ws->zoom > 4.0f) ws->zoom = 4.0f;
|
||||
}
|
||||
|
||||
void interaction_update(BlockInteraction* inter, BlockWorkspace* ws) {
|
||||
inter->key_pressed = 0;
|
||||
(void)ws;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
// ============================================================================
|
||||
// block_interaction.h - 交互系统
|
||||
// ============================================================================
|
||||
|
||||
#include "block_workspace.h"
|
||||
|
||||
typedef struct {
|
||||
int mouse_x, mouse_y;
|
||||
int mouse_down;
|
||||
int mouse_button; // 0=left, 1=right, 2=middle
|
||||
int key;
|
||||
int key_pressed;
|
||||
|
||||
// 拖拽状态
|
||||
Block* dragging_block;
|
||||
float drag_offset_x, drag_offset_y;
|
||||
|
||||
// 连接状态
|
||||
BlockSlot* connecting_from;
|
||||
float connect_start_x, connect_start_y;
|
||||
|
||||
// 选择状态
|
||||
Block* selected_block;
|
||||
|
||||
// 平移状态
|
||||
int panning;
|
||||
float pan_start_x, pan_start_y;
|
||||
float view_start_x, view_start_y;
|
||||
} BlockInteraction;
|
||||
|
||||
// 初始化
|
||||
void interaction_init(BlockInteraction* inter);
|
||||
|
||||
// 处理输入事件
|
||||
void interaction_mouse_move(BlockInteraction* inter, BlockWorkspace* ws, int x, int y);
|
||||
void interaction_mouse_down(BlockInteraction* inter, BlockWorkspace* ws, int button);
|
||||
void interaction_mouse_up(BlockInteraction* inter, BlockWorkspace* ws, int button);
|
||||
void interaction_key_down(BlockInteraction* inter, BlockWorkspace* ws, int key);
|
||||
void interaction_mouse_wheel(BlockInteraction* inter, BlockWorkspace* ws, int delta);
|
||||
|
||||
// 更新 (每帧调用)
|
||||
void interaction_update(BlockInteraction* inter, BlockWorkspace* ws);
|
||||
@@ -0,0 +1,140 @@
|
||||
// ============================================================================
|
||||
// block_palette.c - 积木面板实现
|
||||
// ============================================================================
|
||||
|
||||
#include "block_palette.h"
|
||||
#include "block_renderer.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void palette_init(BlockPalette* palette) {
|
||||
memset(palette, 0, sizeof(BlockPalette));
|
||||
palette->width = 200;
|
||||
palette->height = 600;
|
||||
|
||||
// 添加所有积木类型
|
||||
int count;
|
||||
const BlockTypeInfo* all = block_type_get_all(&count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
palette_add_block(palette, all[i].type);
|
||||
}
|
||||
}
|
||||
|
||||
void palette_add_block(BlockPalette* palette, BlockType type) {
|
||||
const BlockTypeInfo* info = block_type_get_info(type);
|
||||
if (!info) return;
|
||||
|
||||
// 查找或创建分类
|
||||
for (int i = 0; i < palette->category_count; i++) {
|
||||
if (strcmp(palette->categories[i].name, info->category) == 0) {
|
||||
palette->categories[i].blocks[palette->categories[i].block_count++] = type;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建新分类
|
||||
if (palette->category_count < MAX_PALETTE_CATEGORIES) {
|
||||
PaletteCategory* cat = &palette->categories[palette->category_count++];
|
||||
strncpy(cat->name, info->category, 63);
|
||||
cat->blocks[cat->block_count++] = type;
|
||||
cat->expanded = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void palette_render(BlockPalette* palette, BlockRenderer* renderer) {
|
||||
// 绘制背景
|
||||
block_renderer_add_quad(renderer, palette->x, palette->y, palette->width, palette->height,
|
||||
0.15f, 0.15f, 0.18f, 1.0f);
|
||||
|
||||
// 绘制标题
|
||||
block_renderer_draw_text(renderer, "积木面板", palette->x + 5, palette->y + 5,
|
||||
1.0f, 1.0f, 1.0f);
|
||||
|
||||
float y = palette->y + 25 - palette->scroll_y;
|
||||
|
||||
for (int c = 0; c < palette->category_count; c++) {
|
||||
PaletteCategory* cat = &palette->categories[c];
|
||||
|
||||
// 绘制分类标题
|
||||
block_renderer_add_quad(renderer, palette->x + 2, y, palette->width - 4, 20,
|
||||
0.25f, 0.25f, 0.3f, 1.0f);
|
||||
|
||||
const char* arrow = cat->expanded ? "[-]" : "[+]";
|
||||
char title[128];
|
||||
snprintf(title, 128, "%s %s (%d)", arrow, cat->name, cat->block_count);
|
||||
block_renderer_draw_text(renderer, title, palette->x + 5, y + 3,
|
||||
0.9f, 0.9f, 0.9f);
|
||||
|
||||
y += 22;
|
||||
|
||||
if (!cat->expanded) continue;
|
||||
|
||||
// 绘制积木列表
|
||||
for (int b = 0; b < cat->block_count; b++) {
|
||||
const BlockTypeInfo* info = block_type_get_info(cat->blocks[b]);
|
||||
if (!info) continue;
|
||||
|
||||
// 搜索过滤
|
||||
if (palette->search_active && palette->search_text[0]) {
|
||||
if (strstr(info->name, palette->search_text) == NULL &&
|
||||
strstr(info->label, palette->search_text) == NULL) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制积木预览
|
||||
block_renderer_add_quad(renderer, palette->x + 5, y, palette->width - 10, 18,
|
||||
info->color.r, info->color.g, info->color.b, info->color.a);
|
||||
|
||||
block_renderer_draw_text(renderer, info->label, palette->x + 10, y + 3,
|
||||
1.0f, 1.0f, 1.0f);
|
||||
|
||||
y += 20;
|
||||
}
|
||||
|
||||
y += 5;
|
||||
}
|
||||
}
|
||||
|
||||
BlockType palette_handle_click(BlockPalette* palette, float mx, float my) {
|
||||
float y = palette->y + 25 - palette->scroll_y;
|
||||
|
||||
for (int c = 0; c < palette->category_count; c++) {
|
||||
PaletteCategory* cat = &palette->categories[c];
|
||||
|
||||
// 检查分类标题点击
|
||||
if (mx >= palette->x && mx <= palette->x + palette->width &&
|
||||
my >= y && my <= y + 20) {
|
||||
cat->expanded = !cat->expanded;
|
||||
return -1;
|
||||
}
|
||||
|
||||
y += 22;
|
||||
|
||||
if (!cat->expanded) continue;
|
||||
|
||||
// 检查积木点击
|
||||
for (int b = 0; b < cat->block_count; b++) {
|
||||
if (mx >= palette->x + 5 && mx <= palette->x + palette->width - 5 &&
|
||||
my >= y && my <= y + 18) {
|
||||
return cat->blocks[b];
|
||||
}
|
||||
y += 20;
|
||||
}
|
||||
|
||||
y += 5;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void palette_search(BlockPalette* palette, const char* text) {
|
||||
if (text) {
|
||||
strncpy(palette->search_text, text, 127);
|
||||
palette->search_active = 1;
|
||||
} else {
|
||||
palette->search_text[0] = '\0';
|
||||
palette->search_active = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
// ============================================================================
|
||||
// block_palette.h - 积木面板
|
||||
// ============================================================================
|
||||
|
||||
#include "block_types.h"
|
||||
#include "block_workspace.h"
|
||||
|
||||
#define MAX_PALETTE_CATEGORIES 16
|
||||
|
||||
typedef struct {
|
||||
char name[64];
|
||||
BlockType blocks[64];
|
||||
int block_count;
|
||||
int expanded;
|
||||
} PaletteCategory;
|
||||
|
||||
typedef struct BlockPalette {
|
||||
PaletteCategory categories[MAX_PALETTE_CATEGORIES];
|
||||
int category_count;
|
||||
|
||||
float x, y, width, height;
|
||||
float scroll_y;
|
||||
|
||||
char search_text[128];
|
||||
int search_active;
|
||||
} BlockPalette;
|
||||
|
||||
// 初始化
|
||||
void palette_init(BlockPalette* palette);
|
||||
|
||||
// 添加积木到面板 (按分类)
|
||||
void palette_add_block(BlockPalette* palette, BlockType type);
|
||||
|
||||
struct BlockRenderer;
|
||||
void palette_render(BlockPalette* palette, struct BlockRenderer* renderer);
|
||||
|
||||
// 处理点击
|
||||
BlockType palette_handle_click(BlockPalette* palette, float mx, float my);
|
||||
|
||||
// 搜索
|
||||
void palette_search(BlockPalette* palette, const char* text);
|
||||
@@ -0,0 +1,142 @@
|
||||
#include "block_renderer.h"
|
||||
#include "block_core.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int block_renderer_init(BlockRenderer* renderer, void* device, void* context) {
|
||||
memset(renderer, 0, sizeof(BlockRenderer));
|
||||
renderer->device = (ID3D11Device*)device;
|
||||
renderer->context = (ID3D11DeviceContext*)context;
|
||||
renderer->vertex_capacity = 65536;
|
||||
renderer->vertex_data = (float*)malloc(renderer->vertex_capacity * 7 * sizeof(float));
|
||||
renderer->index_capacity = 65536;
|
||||
renderer->index_data = (uint32_t*)malloc(renderer->index_capacity * sizeof(uint32_t));
|
||||
return 1;
|
||||
}
|
||||
|
||||
void block_renderer_shutdown(BlockRenderer* renderer) {
|
||||
if (renderer->vertex_data) free(renderer->vertex_data);
|
||||
if (renderer->index_data) free(renderer->index_data);
|
||||
if (renderer->vertex_buffer) renderer->vertex_buffer->lpVtbl->Release(renderer->vertex_buffer);
|
||||
if (renderer->index_buffer) renderer->index_buffer->lpVtbl->Release(renderer->index_buffer);
|
||||
if (renderer->vertex_shader) renderer->vertex_shader->lpVtbl->Release(renderer->vertex_shader);
|
||||
if (renderer->pixel_shader) renderer->pixel_shader->lpVtbl->Release(renderer->pixel_shader);
|
||||
if (renderer->input_layout) renderer->input_layout->lpVtbl->Release(renderer->input_layout);
|
||||
memset(renderer, 0, sizeof(BlockRenderer));
|
||||
}
|
||||
|
||||
void block_renderer_begin(BlockRenderer* renderer) {
|
||||
renderer->vertex_count = 0;
|
||||
renderer->index_count = 0;
|
||||
}
|
||||
|
||||
void block_renderer_end(BlockRenderer* renderer) {
|
||||
if (renderer->vertex_count == 0) return;
|
||||
ID3D11DeviceContext* ctx = renderer->context;
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE mapped;
|
||||
if (SUCCEEDED(ctx->lpVtbl->Map(ctx, (ID3D11Resource*)renderer->vertex_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped))) {
|
||||
memcpy(mapped.pData, renderer->vertex_data, renderer->vertex_count * 7 * sizeof(float));
|
||||
ctx->lpVtbl->Unmap(ctx, (ID3D11Resource*)renderer->vertex_buffer, 0);
|
||||
}
|
||||
|
||||
if (renderer->index_count > 0) {
|
||||
if (SUCCEEDED(ctx->lpVtbl->Map(ctx, (ID3D11Resource*)renderer->index_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped))) {
|
||||
memcpy(mapped.pData, renderer->index_data, renderer->index_count * sizeof(uint32_t));
|
||||
ctx->lpVtbl->Unmap(ctx, (ID3D11Resource*)renderer->index_buffer, 0);
|
||||
}
|
||||
}
|
||||
|
||||
UINT stride = 7 * sizeof(float);
|
||||
UINT offset = 0;
|
||||
ID3D11Buffer* vb = renderer->vertex_buffer;
|
||||
ctx->lpVtbl->IASetVertexBuffers(ctx, 0, 1, &vb, &stride, &offset);
|
||||
|
||||
if (renderer->index_buffer) {
|
||||
ctx->lpVtbl->IASetIndexBuffer(ctx, renderer->index_buffer, DXGI_FORMAT_R32_UINT, 0);
|
||||
}
|
||||
ctx->lpVtbl->IASetInputLayout(ctx, renderer->input_layout);
|
||||
ctx->lpVtbl->IASetPrimitiveTopology(ctx, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
ctx->lpVtbl->VSSetShader(ctx, renderer->vertex_shader, NULL, 0);
|
||||
ctx->lpVtbl->PSSetShader(ctx, renderer->pixel_shader, NULL, 0);
|
||||
|
||||
if (renderer->index_count > 0) {
|
||||
ctx->lpVtbl->DrawIndexed(ctx, renderer->index_count, 0, 0);
|
||||
} else {
|
||||
ctx->lpVtbl->Draw(ctx, renderer->vertex_count, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void block_renderer_add_quad(BlockRenderer* renderer, float x, float y, float w, float h, float r, float g, float b, float a) {
|
||||
if (renderer->vertex_count + 4 > renderer->vertex_capacity) return;
|
||||
if (renderer->index_count + 6 > renderer->index_capacity) return;
|
||||
|
||||
uint32_t base = renderer->vertex_count;
|
||||
float* v = renderer->vertex_data + base * 7;
|
||||
|
||||
v[0*7+0] = x; v[0*7+1] = y; v[0*7+2] = 0; v[0*7+3] = r; v[0*7+4] = g; v[0*7+5] = b; v[0*7+6] = a;
|
||||
v[1*7+0] = x + w; v[1*7+1] = y; v[1*7+2] = 0; v[1*7+3] = r; v[1*7+4] = g; v[1*7+5] = b; v[1*7+6] = a;
|
||||
v[2*7+0] = x + w; v[2*7+1] = y + h; v[2*7+2] = 0; v[2*7+3] = r; v[2*7+4] = g; v[2*7+5] = b; v[2*7+6] = a;
|
||||
v[3*7+0] = x; v[3*7+1] = y + h; v[3*7+2] = 0; v[3*7+3] = r; v[3*7+4] = g; v[3*7+5] = b; v[3*7+6] = a;
|
||||
|
||||
renderer->vertex_count += 4;
|
||||
|
||||
renderer->index_data[renderer->index_count++] = base + 0;
|
||||
renderer->index_data[renderer->index_count++] = base + 1;
|
||||
renderer->index_data[renderer->index_count++] = base + 2;
|
||||
renderer->index_data[renderer->index_count++] = base + 0;
|
||||
renderer->index_data[renderer->index_count++] = base + 2;
|
||||
renderer->index_data[renderer->index_count++] = base + 3;
|
||||
}
|
||||
|
||||
void block_renderer_add_line(BlockRenderer* renderer, float x1, float y1, float x2, float y2, float r, float g, float b, float a) {
|
||||
float dx = x2 - x1;
|
||||
float dy = y2 - y1;
|
||||
float len = sqrtf(dx * dx + dy * dy);
|
||||
if (len < 0.001f) return;
|
||||
float nx = -dy / len;
|
||||
float ny = dx / len;
|
||||
|
||||
block_renderer_add_quad(renderer, x1, y1, x2 - x1, y2 - y1, r, g, b, a);
|
||||
}
|
||||
|
||||
static void block_renderer_add_bezier(BlockRenderer* renderer, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, float r, float g, float b, float a) {
|
||||
int segments = 16;
|
||||
float prev_x = x1, prev_y = y1;
|
||||
for (int i = 1; i <= segments; i++) {
|
||||
float t = (float)i / segments;
|
||||
float mt = 1.0f - t;
|
||||
float px = mt*mt*mt*x1 + 3*mt*mt*t*cx1 + 3*mt*t*t*cx2 + t*t*t*x2;
|
||||
float py = mt*mt*mt*y1 + 3*mt*mt*t*cy1 + 3*mt*t*t*cy2 + t*t*t*y2;
|
||||
block_renderer_add_line(renderer, prev_x, prev_y, px, py, r, g, b, a);
|
||||
prev_x = px;
|
||||
prev_y = py;
|
||||
}
|
||||
}
|
||||
|
||||
void block_renderer_draw_block(BlockRenderer* renderer, Block* block, float view_x, float view_y, float zoom) {
|
||||
float x = (block->x - view_x) * zoom;
|
||||
float y = (block->y - view_y) * zoom;
|
||||
float w = block->width * zoom;
|
||||
float h = block->height * zoom;
|
||||
|
||||
block_renderer_add_quad(renderer, x, y, w, h, 0.3f, 0.3f, 0.35f, 1.0f);
|
||||
block_renderer_add_quad(renderer, x, y, w, 20 * zoom, 0.2f, 0.2f, 0.25f, 1.0f);
|
||||
}
|
||||
|
||||
void block_renderer_draw_connection(BlockRenderer* renderer, BlockSlot* from, BlockSlot* to, float view_x, float view_y, float zoom) {
|
||||
float x1 = (from->owner->x + from->x - view_x) * zoom;
|
||||
float y1 = (from->owner->y + from->y - view_y) * zoom;
|
||||
float x2 = (to->owner->x + to->x - view_x) * zoom;
|
||||
float y2 = (to->owner->y + to->y - view_y) * zoom;
|
||||
|
||||
float dx = fabsf(x2 - x1) * 0.5f;
|
||||
if (dx < 50.0f) dx = 50.0f;
|
||||
|
||||
block_renderer_add_bezier(renderer, x1, y1, x1 + dx, y1, x2 - dx, y2, x2, y2, 0.8f, 0.8f, 0.2f, 1.0f);
|
||||
}
|
||||
|
||||
void block_renderer_draw_text(BlockRenderer* renderer, const char* text, float x, float y, float r, float g, float b) {
|
||||
if (!text) return;
|
||||
(void)renderer; (void)x; (void)y; (void)r; (void)g; (void)b;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "block_workspace.h"
|
||||
#include <d3d11.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct BlockRenderer {
|
||||
ID3D11Device* device;
|
||||
ID3D11DeviceContext* context;
|
||||
ID3D11Buffer* vertex_buffer;
|
||||
ID3D11Buffer* index_buffer;
|
||||
ID3D11VertexShader* vertex_shader;
|
||||
ID3D11PixelShader* pixel_shader;
|
||||
ID3D11InputLayout* input_layout;
|
||||
|
||||
float* vertex_data;
|
||||
int vertex_count;
|
||||
int vertex_capacity;
|
||||
uint32_t* index_data;
|
||||
int index_count;
|
||||
int index_capacity;
|
||||
} BlockRenderer;
|
||||
|
||||
int block_renderer_init(BlockRenderer* renderer, void* device, void* context);
|
||||
void block_renderer_shutdown(BlockRenderer* renderer);
|
||||
void block_renderer_begin(BlockRenderer* renderer);
|
||||
void block_renderer_end(BlockRenderer* renderer);
|
||||
void block_renderer_draw_block(BlockRenderer* renderer, Block* block, float view_x, float view_y, float zoom);
|
||||
void block_renderer_draw_connection(BlockRenderer* renderer, BlockSlot* from, BlockSlot* to, float view_x, float view_y, float zoom);
|
||||
void block_renderer_draw_text(BlockRenderer* renderer, const char* text, float x, float y, float r, float g, float b);
|
||||
void block_renderer_add_quad(BlockRenderer* renderer, float x, float y, float w, float h, float r, float g, float b, float a);
|
||||
void block_renderer_add_line(BlockRenderer* renderer, float x1, float y1, float x2, float y2, float r, float g, float b, float a);
|
||||
@@ -0,0 +1,142 @@
|
||||
// ============================================================================
|
||||
// block_serializer.c - JSON 序列化实现
|
||||
// ============================================================================
|
||||
|
||||
#include "block_serializer.h"
|
||||
#include "block_types.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// 简单的 JSON 生成器
|
||||
typedef struct {
|
||||
char* buffer;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
} JsonBuffer;
|
||||
|
||||
static void json_init(JsonBuffer* buf) {
|
||||
buf->capacity = 4096;
|
||||
buf->buffer = (char*)malloc(buf->capacity);
|
||||
buf->size = 0;
|
||||
buf->buffer[0] = '\0';
|
||||
}
|
||||
|
||||
static void json_append(JsonBuffer* buf, const char* str) {
|
||||
size_t len = strlen(str);
|
||||
if (buf->size + len + 1 > buf->capacity) {
|
||||
buf->capacity = (buf->size + len + 1) * 2;
|
||||
buf->buffer = (char*)realloc(buf->buffer, buf->capacity);
|
||||
}
|
||||
memcpy(buf->buffer + buf->size, str, len + 1);
|
||||
buf->size += len;
|
||||
}
|
||||
|
||||
static void json_free(JsonBuffer* buf) {
|
||||
free(buf->buffer);
|
||||
buf->buffer = NULL;
|
||||
buf->size = 0;
|
||||
}
|
||||
|
||||
int serializer_save(BlockWorkspace* ws, const char* filepath) {
|
||||
char* json = serializer_save_to_string(ws);
|
||||
if (!json) return 0;
|
||||
|
||||
FILE* f = fopen(filepath, "w");
|
||||
if (!f) {
|
||||
free(json);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(f, "%s", json);
|
||||
fclose(f);
|
||||
free(json);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int serializer_load(BlockWorkspace* ws, const char* filepath) {
|
||||
FILE* f = fopen(filepath, "r");
|
||||
if (!f) return 0;
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
long size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
char* json = (char*)malloc(size + 1);
|
||||
fread(json, 1, size, f);
|
||||
json[size] = '\0';
|
||||
fclose(f);
|
||||
|
||||
int result = serializer_load_from_string(ws, json);
|
||||
free(json);
|
||||
return result;
|
||||
}
|
||||
|
||||
char* serializer_save_to_string(BlockWorkspace* ws) {
|
||||
JsonBuffer buf;
|
||||
json_init(&buf);
|
||||
|
||||
json_append(&buf, "{\n");
|
||||
json_append(&buf, " \"version\": \"1.0\",\n");
|
||||
|
||||
// 保存积木
|
||||
json_append(&buf, " \"blocks\": [\n");
|
||||
for (int i = 0; i < ws->block_count; i++) {
|
||||
Block* block = ws->blocks[i];
|
||||
const BlockTypeInfo* info = block_type_get_info(block->type);
|
||||
|
||||
char tmp[256];
|
||||
snprintf(tmp, 256, " {\n");
|
||||
json_append(&buf, tmp);
|
||||
|
||||
snprintf(tmp, 256, " \"id\": %d,\n", block->id);
|
||||
json_append(&buf, tmp);
|
||||
|
||||
snprintf(tmp, 256, " \"type\": \"%s\",\n", info ? info->name : "unknown");
|
||||
json_append(&buf, tmp);
|
||||
|
||||
snprintf(tmp, 256, " \"x\": %.1f,\n", block->x);
|
||||
json_append(&buf, tmp);
|
||||
|
||||
snprintf(tmp, 256, " \"y\": %.1f\n", block->y);
|
||||
json_append(&buf, tmp);
|
||||
|
||||
json_append(&buf, i < ws->block_count - 1 ? " },\n" : " }\n");
|
||||
}
|
||||
json_append(&buf, " ],\n");
|
||||
|
||||
// 保存连接
|
||||
json_append(&buf, " \"connections\": [\n");
|
||||
for (int i = 0; i < ws->connection_count; i++) {
|
||||
BlockConnection* conn = &ws->connections[i];
|
||||
char tmp[128];
|
||||
snprintf(tmp, 128, " {\"from\": %d, \"to\": %d}%s\n",
|
||||
conn->from ? conn->from->owner->id : 0,
|
||||
conn->to ? conn->to->owner->id : 0,
|
||||
i < ws->connection_count - 1 ? "," : "");
|
||||
json_append(&buf, tmp);
|
||||
}
|
||||
json_append(&buf, " ]\n");
|
||||
|
||||
json_append(&buf, "}\n");
|
||||
|
||||
return buf.buffer;
|
||||
}
|
||||
|
||||
int serializer_load_from_string(BlockWorkspace* ws, const char* json_str) {
|
||||
// 简化的 JSON 解析
|
||||
// 完整实现需要完整的 JSON 解析器
|
||||
|
||||
workspace_clear(ws);
|
||||
|
||||
// 查找 "blocks" 数组
|
||||
const char* blocks_start = strstr(json_str, "\"blocks\"");
|
||||
if (!blocks_start) return 0;
|
||||
|
||||
// 简化解析 - 只提取基本信息
|
||||
// 实际项目中应使用 cJSON 或类似库
|
||||
|
||||
// TODO: 实现完整的 JSON 解析
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
// ============================================================================
|
||||
// block_serializer.h - JSON 序列化
|
||||
// ============================================================================
|
||||
|
||||
#include "block_workspace.h"
|
||||
|
||||
// 保存工作区到文件
|
||||
int serializer_save(BlockWorkspace* ws, const char* filepath);
|
||||
|
||||
// 从文件加载工作区
|
||||
int serializer_load(BlockWorkspace* ws, const char* filepath);
|
||||
|
||||
// 保存到字符串
|
||||
char* serializer_save_to_string(BlockWorkspace* ws);
|
||||
|
||||
// 从字符串加载
|
||||
int serializer_load_from_string(BlockWorkspace* ws, const char* json_str);
|
||||
@@ -0,0 +1,326 @@
|
||||
// ============================================================================
|
||||
// block_types.c - 积木类型注册
|
||||
// ============================================================================
|
||||
|
||||
#include "block_types.h"
|
||||
#include <string.h>
|
||||
|
||||
static BlockTypeInfo g_type_info[BLOCK_TYPE_COUNT];
|
||||
static int g_types_initialized = 0;
|
||||
|
||||
void block_types_init(void) {
|
||||
if (g_types_initialized) return;
|
||||
|
||||
int i = 0;
|
||||
|
||||
// === 事件积木 (红色) ===
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_EVENT_START, "event_start", "on_start", "Events",
|
||||
BLOCK_COLOR_EVENT, 0, 1,
|
||||
{}, {SLOT_STATEMENT},
|
||||
{}, {"执行"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_EVENT_UPDATE, "event_update", "on_update", "Events",
|
||||
BLOCK_COLOR_EVENT, 0, 1,
|
||||
{}, {SLOT_STATEMENT},
|
||||
{}, {"执行"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_EVENT_COLLISION, "event_collision", "on_collision", "Events",
|
||||
BLOCK_COLOR_EVENT, 1, 1,
|
||||
{SLOT_VALUE_STRING}, {SLOT_STATEMENT},
|
||||
{"碰撞对象"}, {"执行"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_EVENT_KEY_PRESSED, "event_key", "on_key_pressed", "Events",
|
||||
BLOCK_COLOR_EVENT, 1, 1,
|
||||
{SLOT_VALUE_STRING}, {SLOT_STATEMENT},
|
||||
{"按键"}, {"执行"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_EVENT_MOUSE_CLICK, "event_mouse", "on_mouse_click", "Events",
|
||||
BLOCK_COLOR_EVENT, 0, 1,
|
||||
{}, {SLOT_STATEMENT},
|
||||
{}, {"执行"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_EVENT_TIMER, "event_timer", "on_timer", "Events",
|
||||
BLOCK_COLOR_EVENT, 1, 1,
|
||||
{SLOT_VALUE_NUMBER}, {SLOT_STATEMENT},
|
||||
{"间隔(秒)"}, {"执行"}
|
||||
};
|
||||
|
||||
// === 动作积木 (蓝色) ===
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_ACTION_MOVE, "action_move", "move", "Actions",
|
||||
BLOCK_COLOR_ACTION, 4, 1,
|
||||
{SLOT_STATEMENT, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
|
||||
{SLOT_STATEMENT},
|
||||
{"执行", "X", "Y", "Z"}, {"执行"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_ACTION_ROTATE, "action_rotate", "rotate", "Actions",
|
||||
BLOCK_COLOR_ACTION, 4, 1,
|
||||
{SLOT_STATEMENT, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
|
||||
{SLOT_STATEMENT},
|
||||
{"执行", "X", "Y", "Z"}, {"执行"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_ACTION_SCALE, "action_scale", "scale", "Actions",
|
||||
BLOCK_COLOR_ACTION, 4, 1,
|
||||
{SLOT_STATEMENT, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
|
||||
{SLOT_STATEMENT},
|
||||
{"执行", "X", "Y", "Z"}, {"执行"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_ACTION_SET_POSITION, "action_setpos", "set_position", "Actions",
|
||||
BLOCK_COLOR_ACTION, 4, 1,
|
||||
{SLOT_STATEMENT, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
|
||||
{SLOT_STATEMENT},
|
||||
{"执行", "X", "Y", "Z"}, {"执行"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_ACTION_SET_ROTATION, "action_setrot", "set_rotation", "Actions",
|
||||
BLOCK_COLOR_ACTION, 4, 1,
|
||||
{SLOT_STATEMENT, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
|
||||
{SLOT_STATEMENT},
|
||||
{"执行", "X", "Y", "Z"}, {"执行"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_ACTION_PLAY_SOUND, "action_sound", "play_sound", "Actions",
|
||||
BLOCK_COLOR_ACTION, 2, 1,
|
||||
{SLOT_STATEMENT, SLOT_VALUE_STRING},
|
||||
{SLOT_STATEMENT},
|
||||
{"执行", "音效名"}, {"执行"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_ACTION_DESTROY, "action_destroy", "destroy", "Actions",
|
||||
BLOCK_COLOR_ACTION, 1, 1,
|
||||
{SLOT_STATEMENT},
|
||||
{SLOT_STATEMENT},
|
||||
{"执行"}, {"执行"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_ACTION_INSTANTIATE, "action_instantiate", "instantiate", "Actions",
|
||||
BLOCK_COLOR_ACTION, 2, 1,
|
||||
{SLOT_STATEMENT, SLOT_VALUE_STRING},
|
||||
{SLOT_STATEMENT},
|
||||
{"执行", "预制体"}, {"执行"}
|
||||
};
|
||||
|
||||
// === 控制积木 (绿色) ===
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_CONTROL_IF, "control_if", "if", "Control",
|
||||
BLOCK_COLOR_CONTROL, 2, 2,
|
||||
{SLOT_STATEMENT, SLOT_VALUE_BOOLEAN},
|
||||
{SLOT_STATEMENT, SLOT_STATEMENT},
|
||||
{"执行", "条件"}, {"Then", "Else"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_CONTROL_IF_ELSE, "control_ifelse", "if_else", "Control",
|
||||
BLOCK_COLOR_CONTROL, 2, 2,
|
||||
{SLOT_STATEMENT, SLOT_VALUE_BOOLEAN},
|
||||
{SLOT_STATEMENT, SLOT_STATEMENT},
|
||||
{"执行", "条件"}, {"Then", "Else"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_CONTROL_REPEAT, "control_repeat", "repeat", "Control",
|
||||
BLOCK_COLOR_CONTROL, 2, 1,
|
||||
{SLOT_STATEMENT, SLOT_VALUE_BOOLEAN},
|
||||
{SLOT_STATEMENT},
|
||||
{"执行", "条件"}, {"执行"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_CONTROL_REPEAT_TIMES, "control_count", "repeat_times", "Control",
|
||||
BLOCK_COLOR_CONTROL, 2, 1,
|
||||
{SLOT_STATEMENT, SLOT_VALUE_NUMBER},
|
||||
{SLOT_STATEMENT},
|
||||
{"执行", "次数"}, {"执行"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_CONTROL_WHILE, "control_while", "while", "Control",
|
||||
BLOCK_COLOR_CONTROL, 2, 1,
|
||||
{SLOT_STATEMENT, SLOT_VALUE_BOOLEAN},
|
||||
{SLOT_STATEMENT},
|
||||
{"执行", "条件"}, {"执行"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_CONTROL_WAIT, "control_wait", "wait", "Control",
|
||||
BLOCK_COLOR_CONTROL, 2, 1,
|
||||
{SLOT_STATEMENT, SLOT_VALUE_NUMBER},
|
||||
{SLOT_STATEMENT},
|
||||
{"执行", "秒数"}, {"执行"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_CONTROL_FUNCTION_DEF, "control_funcdef", "function_def", "Control",
|
||||
BLOCK_COLOR_CONTROL, 1, 1,
|
||||
{SLOT_STATEMENT},
|
||||
{SLOT_STATEMENT},
|
||||
{"执行"}, {"执行"}
|
||||
};
|
||||
|
||||
// === 运算积木 (橙色) ===
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_OP_ADD, "op_add", "+", "Operators",
|
||||
BLOCK_COLOR_OPERATOR, 2, 1,
|
||||
{SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
|
||||
{SLOT_VALUE_NUMBER},
|
||||
{"A", "B"}, {"结果"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_OP_SUB, "op_sub", "-", "Operators",
|
||||
BLOCK_COLOR_OPERATOR, 2, 1,
|
||||
{SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
|
||||
{SLOT_VALUE_NUMBER},
|
||||
{"A", "B"}, {"结果"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_OP_MUL, "op_mul", "*", "Operators",
|
||||
BLOCK_COLOR_OPERATOR, 2, 1,
|
||||
{SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
|
||||
{SLOT_VALUE_NUMBER},
|
||||
{"A", "B"}, {"结果"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_OP_DIV, "op_div", "/", "Operators",
|
||||
BLOCK_COLOR_OPERATOR, 2, 1,
|
||||
{SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
|
||||
{SLOT_VALUE_NUMBER},
|
||||
{"A", "B"}, {"结果"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_OP_MOD, "op_mod", "%", "Operators",
|
||||
BLOCK_COLOR_OPERATOR, 2, 1,
|
||||
{SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
|
||||
{SLOT_VALUE_NUMBER},
|
||||
{"A", "B"}, {"结果"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_OP_EQUALS, "op_eq", "==", "Operators",
|
||||
BLOCK_COLOR_OPERATOR, 2, 1,
|
||||
{SLOT_VALUE_ANY, SLOT_VALUE_ANY},
|
||||
{SLOT_VALUE_BOOLEAN},
|
||||
{"A", "B"}, {"结果"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_OP_NOT_EQUALS, "op_neq", "!=", "Operators",
|
||||
BLOCK_COLOR_OPERATOR, 2, 1,
|
||||
{SLOT_VALUE_ANY, SLOT_VALUE_ANY},
|
||||
{SLOT_VALUE_BOOLEAN},
|
||||
{"A", "B"}, {"结果"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_OP_GREATER, "op_gt", ">", "Operators",
|
||||
BLOCK_COLOR_OPERATOR, 2, 1,
|
||||
{SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
|
||||
{SLOT_VALUE_BOOLEAN},
|
||||
{"A", "B"}, {"结果"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_OP_LESS, "op_lt", "<", "Operators",
|
||||
BLOCK_COLOR_OPERATOR, 2, 1,
|
||||
{SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
|
||||
{SLOT_VALUE_BOOLEAN},
|
||||
{"A", "B"}, {"结果"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_OP_AND, "op_and", "AND", "Operators",
|
||||
BLOCK_COLOR_OPERATOR, 2, 1,
|
||||
{SLOT_VALUE_BOOLEAN, SLOT_VALUE_BOOLEAN},
|
||||
{SLOT_VALUE_BOOLEAN},
|
||||
{"A", "B"}, {"结果"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_OP_OR, "op_or", "OR", "Operators",
|
||||
BLOCK_COLOR_OPERATOR, 2, 1,
|
||||
{SLOT_VALUE_BOOLEAN, SLOT_VALUE_BOOLEAN},
|
||||
{SLOT_VALUE_BOOLEAN},
|
||||
{"A", "B"}, {"结果"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_OP_NOT, "op_not", "NOT", "Operators",
|
||||
BLOCK_COLOR_OPERATOR, 1, 1,
|
||||
{SLOT_VALUE_BOOLEAN},
|
||||
{SLOT_VALUE_BOOLEAN},
|
||||
{"A"}, {"结果"}
|
||||
};
|
||||
|
||||
// === 变量积木 (紫色) ===
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_VAR_GET, "var_get", "get_variable", "Variables",
|
||||
BLOCK_COLOR_VARIABLE, 0, 1,
|
||||
{}, {SLOT_VALUE_ANY},
|
||||
{}, {"值"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_VAR_SET, "var_set", "set_variable", "Variables",
|
||||
BLOCK_COLOR_VARIABLE, 2, 1,
|
||||
{SLOT_STATEMENT, SLOT_VALUE_ANY},
|
||||
{SLOT_STATEMENT},
|
||||
{"执行", "值"}, {"执行"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_VAR_CREATE, "var_create", "create_variable", "Variables",
|
||||
BLOCK_COLOR_VARIABLE, 1, 1,
|
||||
{SLOT_STATEMENT},
|
||||
{SLOT_STATEMENT},
|
||||
{"执行"}, {"执行"}
|
||||
};
|
||||
|
||||
// === 函数积木 (黄色) ===
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_FUNC_CALL, "func_call", "call_function", "Functions",
|
||||
BLOCK_COLOR_FUNCTION, 2, 1,
|
||||
{SLOT_STATEMENT, SLOT_VALUE_STRING},
|
||||
{SLOT_STATEMENT},
|
||||
{"执行", "函数名"}, {"执行"}
|
||||
};
|
||||
g_type_info[i++] = (BlockTypeInfo){
|
||||
BLOCK_FUNC_RETURN, "func_return", "return", "Functions",
|
||||
BLOCK_COLOR_FUNCTION, 2, 0,
|
||||
{SLOT_STATEMENT, SLOT_VALUE_ANY},
|
||||
{},
|
||||
{"执行", "返回值"}
|
||||
};
|
||||
|
||||
g_types_initialized = 1;
|
||||
}
|
||||
|
||||
const BlockTypeInfo* block_type_get_info(BlockType type) {
|
||||
if (type < 0 || type >= BLOCK_TYPE_COUNT) return NULL;
|
||||
return &g_type_info[type];
|
||||
}
|
||||
|
||||
const BlockTypeInfo* block_type_get_all(int* count) {
|
||||
*count = BLOCK_TYPE_COUNT;
|
||||
return g_type_info;
|
||||
}
|
||||
|
||||
BlockType block_type_find_by_name(const char* name) {
|
||||
for (int i = 0; i < BLOCK_TYPE_COUNT; i++) {
|
||||
if (strcmp(g_type_info[i].name, name) == 0) {
|
||||
return g_type_info[i].type;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char** block_type_get_categories(int* count) {
|
||||
static const char* categories[] = {
|
||||
"Events", "Actions", "Control", "Operators", "Variables", "Functions"
|
||||
};
|
||||
*count = 6;
|
||||
return categories;
|
||||
}
|
||||
|
||||
const BlockType* block_type_get_by_category(const char* category, int* count) {
|
||||
static BlockType buffer[BLOCK_TYPE_COUNT];
|
||||
*count = 0;
|
||||
for (int i = 0; i < BLOCK_TYPE_COUNT; i++) {
|
||||
if (strcmp(g_type_info[i].category, category) == 0) {
|
||||
buffer[(*count)++] = g_type_info[i].type;
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
// ============================================================================
|
||||
// block_types.h - 积木类型定义
|
||||
// ============================================================================
|
||||
|
||||
#include "block_core.h"
|
||||
|
||||
// 获取积木类型信息
|
||||
const BlockTypeInfo* block_type_get_info(BlockType type);
|
||||
|
||||
// 获取所有类型信息
|
||||
const BlockTypeInfo* block_type_get_all(int* count);
|
||||
|
||||
// 按名称查找类型
|
||||
BlockType block_type_find_by_name(const char* name);
|
||||
|
||||
// 按分类获取类型列表
|
||||
const BlockType* block_type_get_by_category(const char* category, int* count);
|
||||
|
||||
// 获取所有分类
|
||||
const char** block_type_get_categories(int* count);
|
||||
|
||||
// 初始化所有积木类型
|
||||
void block_types_init(void);
|
||||
@@ -0,0 +1,172 @@
|
||||
// ============================================================================
|
||||
// block_workspace.c - 工作区管理实现
|
||||
// ============================================================================
|
||||
|
||||
#include "block_workspace.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
BlockWorkspace* workspace_create(void) {
|
||||
BlockWorkspace* ws = (BlockWorkspace*)calloc(1, sizeof(BlockWorkspace));
|
||||
if (!ws) return NULL;
|
||||
|
||||
ws->zoom = 1.0f;
|
||||
ws->next_block_id = 1;
|
||||
|
||||
return ws;
|
||||
}
|
||||
|
||||
void workspace_destroy(BlockWorkspace* ws) {
|
||||
if (!ws) return;
|
||||
workspace_clear(ws);
|
||||
free(ws);
|
||||
}
|
||||
|
||||
int workspace_add_block(BlockWorkspace* ws, Block* block) {
|
||||
if (ws->block_count >= MAX_WORKSPACE_BLOCKS) return -1;
|
||||
if (!block) return -1;
|
||||
|
||||
block->id = ws->next_block_id++;
|
||||
block->workspace = ws;
|
||||
|
||||
ws->blocks[ws->block_count++] = block;
|
||||
return block->id;
|
||||
}
|
||||
|
||||
void workspace_remove_block(BlockWorkspace* ws, int block_id) {
|
||||
// 先移除所有相关连接
|
||||
for (int i = ws->connection_count - 1; i >= 0; i--) {
|
||||
BlockSlot* from = ws->connections[i].from;
|
||||
BlockSlot* to = ws->connections[i].to;
|
||||
if (from && from->owner && from->owner->id == block_id) {
|
||||
workspace_remove_connection(ws, from);
|
||||
} else if (to && to->owner && to->owner->id == block_id) {
|
||||
workspace_remove_connection(ws, to);
|
||||
}
|
||||
}
|
||||
|
||||
// 移除积木
|
||||
for (int i = 0; i < ws->block_count; i++) {
|
||||
if (ws->blocks[i]->id == block_id) {
|
||||
block_destroy(ws->blocks[i]);
|
||||
ws->blocks[i] = ws->blocks[ws->block_count - 1];
|
||||
ws->block_count--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool workspace_add_connection(BlockWorkspace* ws, BlockSlot* from, BlockSlot* to) {
|
||||
if (ws->connection_count >= MAX_WORKSPACE_CONNECTIONS) return false;
|
||||
if (!from || !to) return false;
|
||||
|
||||
// 检查是否已连接
|
||||
for (int i = 0; i < ws->connection_count; i++) {
|
||||
if (ws->connections[i].from == from || ws->connections[i].to == to) {
|
||||
return false; // 已有连接
|
||||
}
|
||||
}
|
||||
|
||||
// 建立连接
|
||||
block_connect(from, to);
|
||||
|
||||
BlockConnection* conn = &ws->connections[ws->connection_count++];
|
||||
conn->from = from;
|
||||
conn->to = to;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void workspace_remove_connection(BlockWorkspace* ws, BlockSlot* slot) {
|
||||
for (int i = ws->connection_count - 1; i >= 0; i--) {
|
||||
if (ws->connections[i].from == slot || ws->connections[i].to == slot) {
|
||||
block_disconnect(ws->connections[i].from);
|
||||
ws->connections[i] = ws->connections[ws->connection_count - 1];
|
||||
ws->connection_count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void workspace_remove_all_connections(BlockWorkspace* ws, Block* block) {
|
||||
if (!block) return;
|
||||
|
||||
for (int i = ws->connection_count - 1; i >= 0; i--) {
|
||||
BlockSlot* from = ws->connections[i].from;
|
||||
BlockSlot* to = ws->connections[i].to;
|
||||
if ((from && from->owner == block) || (to && to->owner == block)) {
|
||||
block_disconnect(from);
|
||||
ws->connections[i] = ws->connections[ws->connection_count - 1];
|
||||
ws->connection_count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Block* workspace_find_block(BlockWorkspace* ws, int block_id) {
|
||||
for (int i = 0; i < ws->block_count; i++) {
|
||||
if (ws->blocks[i]->id == block_id) return ws->blocks[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Block* workspace_find_block_at(BlockWorkspace* ws, float x, float y) {
|
||||
// 从后往前找 (最后添加的在最上面)
|
||||
for (int i = ws->block_count - 1; i >= 0; i--) {
|
||||
if (block_contains_point(ws->blocks[i], x, y)) {
|
||||
return ws->blocks[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BlockSlot* workspace_find_slot_at(BlockWorkspace* ws, float x, float y) {
|
||||
float threshold = 10.0f;
|
||||
|
||||
for (int i = ws->block_count - 1; i >= 0; i--) {
|
||||
Block* block = ws->blocks[i];
|
||||
|
||||
// 检查输出槽位
|
||||
for (int j = 0; j < block->output_count; j++) {
|
||||
BlockSlot* slot = &block->outputs[j];
|
||||
float sx, sy;
|
||||
block_slot_world_pos(slot, &sx, &sy);
|
||||
float dx = x - sx;
|
||||
float dy = y - sy;
|
||||
if (dx * dx + dy * dy < threshold * threshold) {
|
||||
return slot;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查输入槽位
|
||||
for (int j = 0; j < block->input_count; j++) {
|
||||
BlockSlot* slot = &block->inputs[j];
|
||||
float sx, sy;
|
||||
block_slot_world_pos(slot, &sx, &sy);
|
||||
float dx = x - sx;
|
||||
float dy = y - sy;
|
||||
if (dx * dx + dy * dy < threshold * threshold) {
|
||||
return slot;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void workspace_screen_to_world(BlockWorkspace* ws, float sx, float sy, float* wx, float* wy) {
|
||||
*wx = (sx / ws->zoom) + ws->view_x;
|
||||
*wy = (sy / ws->zoom) + ws->view_y;
|
||||
}
|
||||
|
||||
void workspace_world_to_screen(BlockWorkspace* ws, float wx, float wy, float* sx, float* sy) {
|
||||
*sx = (wx - ws->view_x) * ws->zoom;
|
||||
*sy = (wy - ws->view_y) * ws->zoom;
|
||||
}
|
||||
|
||||
void workspace_clear(BlockWorkspace* ws) {
|
||||
for (int i = 0; i < ws->block_count; i++) {
|
||||
block_destroy(ws->blocks[i]);
|
||||
}
|
||||
ws->block_count = 0;
|
||||
ws->connection_count = 0;
|
||||
ws->selected_block = NULL;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
// ============================================================================
|
||||
// block_workspace.h - 工作区管理
|
||||
// ============================================================================
|
||||
|
||||
#include "block_core.h"
|
||||
|
||||
#define MAX_WORKSPACE_BLOCKS 1024
|
||||
#define MAX_WORKSPACE_CONNECTIONS 2048
|
||||
|
||||
typedef struct BlockWorkspace {
|
||||
Block* blocks[MAX_WORKSPACE_BLOCKS];
|
||||
int block_count;
|
||||
|
||||
BlockConnection connections[MAX_WORKSPACE_CONNECTIONS];
|
||||
int connection_count;
|
||||
|
||||
float view_x, view_y;
|
||||
float zoom;
|
||||
|
||||
Block* selected_block;
|
||||
BlockSlot* dragging_from;
|
||||
float drag_start_x, drag_start_y;
|
||||
|
||||
int next_block_id;
|
||||
|
||||
struct BlockPalette* palette;
|
||||
} BlockWorkspace;
|
||||
|
||||
// 创建/销毁
|
||||
BlockWorkspace* workspace_create(void);
|
||||
void workspace_destroy(BlockWorkspace* ws);
|
||||
|
||||
// 添加/移除积木
|
||||
int workspace_add_block(BlockWorkspace* ws, Block* block);
|
||||
void workspace_remove_block(BlockWorkspace* ws, int block_id);
|
||||
|
||||
// 添加/移除连接
|
||||
bool workspace_add_connection(BlockWorkspace* ws, BlockSlot* from, BlockSlot* to);
|
||||
void workspace_remove_connection(BlockWorkspace* ws, BlockSlot* from);
|
||||
void workspace_remove_all_connections(BlockWorkspace* ws, Block* block);
|
||||
|
||||
// 查找
|
||||
Block* workspace_find_block(BlockWorkspace* ws, int block_id);
|
||||
Block* workspace_find_block_at(BlockWorkspace* ws, float x, float y);
|
||||
BlockSlot* workspace_find_slot_at(BlockWorkspace* ws, float x, float y);
|
||||
|
||||
// 视口变换
|
||||
void workspace_screen_to_world(BlockWorkspace* ws, float sx, float sy, float* wx, float* wy);
|
||||
void workspace_world_to_screen(BlockWorkspace* ws, float wx, float wy, float* sx, float* sy);
|
||||
|
||||
// 清空
|
||||
void workspace_clear(BlockWorkspace* ws);
|
||||
Reference in new issue
Block a user