322 lines
12 KiB
Markdown
322 lines
12 KiB
Markdown
|
|
# Extra2D 模块化脚本系统融合方案
|
|||
|
|
|
|||
|
|
结合 **Yosin 的配置驱动脚本加载** 和 **Kiwano 的模块系统**,为 Extra2D 实现一个强大的模块化 Squirrel 脚本架构。
|
|||
|
|
|
|||
|
|
## 融合架构设计
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|||
|
|
│ Extra2D.exe (C++ 引擎) │
|
|||
|
|
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
|||
|
|
│ │ Application (单例) │ │
|
|||
|
|
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
|
|||
|
|
│ │ │ ModuleList (模块列表) │ │ │
|
|||
|
|
│ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │ │ │
|
|||
|
|
│ │ │ │ ScriptModule│ │ AudioModule │ │ PhysicsModule │ │ │ │
|
|||
|
|
│ │ │ │ (脚本系统) │ │ (音频系统) │ │ (物理系统) │ │ │ │
|
|||
|
|
│ │ │ └─────────────┘ └─────────────┘ └─────────────────┘ │ │ │
|
|||
|
|
│ │ └─────────────────────────────────────────────────────────┘ │ │
|
|||
|
|
│ │ │ │
|
|||
|
|
│ │ - Run() - 运行应用 │ │
|
|||
|
|
│ │ - Use(Module&) - 注册模块 │ │
|
|||
|
|
│ │ - Update(dt) - 更新所有模块 │ │
|
|||
|
|
│ │ - Render() - 渲染所有模块 │ │
|
|||
|
|
│ └─────────────────────────────────────────────────────────────────┘ │
|
|||
|
|
└─────────────────────────────────────────────────────────────────────────┘
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|||
|
|
│ ScriptModule (脚本模块) │
|
|||
|
|
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
|||
|
|
│ │ Squirrel VM │ │
|
|||
|
|
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
|
|||
|
|
│ │ │ ScriptConfig.cfg (配置驱动) │ │ │
|
|||
|
|
│ │ │ - 定义脚本加载顺序 │ │ │
|
|||
|
|
│ │ │ - 定义模块依赖关系 │ │ │
|
|||
|
|
│ │ └─────────────────────────────────────────────────────────┘ │ │
|
|||
|
|
│ │ │ │
|
|||
|
|
│ │ 加载流程: │ │
|
|||
|
|
│ │ 1. 加载 Core 层 (基础工具类) │ │
|
|||
|
|
│ │ 2. 加载 Engine 层 (引擎封装类) │ │
|
|||
|
|
│ │ 3. 执行 User/main.nut (用户入口) │ │
|
|||
|
|
│ │ │ │
|
|||
|
|
│ │ 每帧调用: │ │
|
|||
|
|
│ │ - OnUpdate(dt) -> 调用脚本的 update 函数 │ │
|
|||
|
|
│ │ - OnRender() -> 调用脚本的 render 函数 │ │
|
|||
|
|
│ └─────────────────────────────────────────────────────────────────┘ │
|
|||
|
|
└─────────────────────────────────────────────────────────────────────────┘
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|||
|
|
│ Squirrel 脚本层 │
|
|||
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|||
|
|
│ │ Core/ │ │ Engine/ │ │ User/ │ │ Modules/ │ │
|
|||
|
|
│ │ │ │ │ │ │ │ │ │
|
|||
|
|
│ │ _init.nut │ │ _init.nut │ │ main.nut │ │ ui.nut │ │
|
|||
|
|
│ │ class.nut │ │ app.nut │ │ stages/ │ │ network.nut │ │
|
|||
|
|
│ │ array.nut │ │ director.nut│ │ objects/ │ │ ... │ │
|
|||
|
|
│ │ math.nut │ │ stage.nut │ │ ui/ │ │ │ │
|
|||
|
|
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|||
|
|
└─────────────────────────────────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 核心组件设计
|
|||
|
|
|
|||
|
|
### 1. Module 基类(来自 Kiwano)
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// base/module.h
|
|||
|
|
class Module {
|
|||
|
|
public:
|
|||
|
|
virtual void setupModule(); // 初始化
|
|||
|
|
virtual void destroyModule(); // 销毁
|
|||
|
|
virtual void onUpdate(float dt); // 更新
|
|||
|
|
virtual void onRender(); // 渲染
|
|||
|
|
virtual void handleEvent(Event* evt); // 事件
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. ScriptModule(脚本模块)
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// script/script_module.h
|
|||
|
|
class ScriptModule : public Module {
|
|||
|
|
public:
|
|||
|
|
void setupModule() override {
|
|||
|
|
// 1. 初始化 Squirrel VM
|
|||
|
|
vm_ = sq_open(1024);
|
|||
|
|
|
|||
|
|
// 2. 注册 C++ 绑定
|
|||
|
|
registerBindings(vm_);
|
|||
|
|
|
|||
|
|
// 3. 读取 ScriptConfig.cfg
|
|||
|
|
auto config = loadConfig("scripts/ScriptConfig.cfg");
|
|||
|
|
|
|||
|
|
// 4. 按顺序加载脚本
|
|||
|
|
for (auto& path : config.preload) {
|
|||
|
|
executeFile(path);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 5. 调用 main 函数
|
|||
|
|
callMainFunction();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void onUpdate(float dt) override {
|
|||
|
|
// 调用脚本的全局 update 函数
|
|||
|
|
callScriptFunction("onUpdate", dt);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 脚本配置系统(来自 Yosin)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
# scripts/ScriptConfig.cfg
|
|||
|
|
# 定义脚本加载顺序
|
|||
|
|
|
|||
|
|
# ========== Core 层(基础工具)==========
|
|||
|
|
scripts/core/_init.nut
|
|||
|
|
scripts/core/class.nut
|
|||
|
|
scripts/core/array.nut
|
|||
|
|
scripts/core/table.nut
|
|||
|
|
scripts/core/math.nut
|
|||
|
|
scripts/core/string.nut
|
|||
|
|
|
|||
|
|
# ========== Engine 层(引擎封装)==========
|
|||
|
|
scripts/engine/_init.nut
|
|||
|
|
scripts/engine/application.nut
|
|||
|
|
scripts/engine/director.nut
|
|||
|
|
scripts/engine/stage.nut
|
|||
|
|
scripts/engine/node.nut
|
|||
|
|
scripts/engine/sprite.nut
|
|||
|
|
scripts/engine/action.nut
|
|||
|
|
scripts/engine/audio.nut
|
|||
|
|
scripts/engine/input.nut
|
|||
|
|
|
|||
|
|
# ========== User 层(用户逻辑)==========
|
|||
|
|
scripts/user/stages/start_stage.nut
|
|||
|
|
scripts/user/objects/player.nut
|
|||
|
|
scripts/user/ui/main_ui.nut
|
|||
|
|
scripts/user/main.nut # 入口脚本(最后加载)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4. 脚本类层次结构(来自 Yosin)
|
|||
|
|
|
|||
|
|
```squirrel
|
|||
|
|
// scripts/core/class.nut
|
|||
|
|
// 基础类系统
|
|||
|
|
|
|||
|
|
// scripts/engine/node.nut
|
|||
|
|
class Node {
|
|||
|
|
C_Object = null; // C++ 对象指针
|
|||
|
|
|
|||
|
|
constructor() {
|
|||
|
|
C_Object = Node_Create();
|
|||
|
|
Register_Destruction(C_Object, this);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function setPosition(x, y) {
|
|||
|
|
Node_SetPosition(C_Object, x, y);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function getPosition() {
|
|||
|
|
return Node_GetPosition(C_Object);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// scripts/engine/stage.nut
|
|||
|
|
class Stage extends Node {
|
|||
|
|
constructor() {
|
|||
|
|
C_Object = Stage_Create();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function enter() {
|
|||
|
|
Director_EnterStage(C_Object);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function onUpdate(dt) {
|
|||
|
|
// 子类重写
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// scripts/engine/application.nut
|
|||
|
|
class Application {
|
|||
|
|
title = "Extra2D Game";
|
|||
|
|
width = 1280;
|
|||
|
|
height = 720;
|
|||
|
|
|
|||
|
|
function run(stageClass) {
|
|||
|
|
App_SetTitle(title);
|
|||
|
|
App_SetSize(width, height);
|
|||
|
|
App_Run(stageClass);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5. 用户入口脚本(main.nut)
|
|||
|
|
|
|||
|
|
```squirrel
|
|||
|
|
// scripts/user/main.nut
|
|||
|
|
function main(args) {
|
|||
|
|
// 创建应用
|
|||
|
|
local app = Application();
|
|||
|
|
app.title = "My Game";
|
|||
|
|
app.width = 800;
|
|||
|
|
app.height = 600;
|
|||
|
|
|
|||
|
|
// 运行开始舞台
|
|||
|
|
app.run(StartStage);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 定义开始舞台
|
|||
|
|
class StartStage extends Stage {
|
|||
|
|
function onEnter() {
|
|||
|
|
// 创建精灵
|
|||
|
|
local sprite = Sprite("assets/logo.png");
|
|||
|
|
sprite.setPosition(400, 300);
|
|||
|
|
this.addChild(sprite);
|
|||
|
|
|
|||
|
|
// 创建 UI
|
|||
|
|
local ui = MainUI();
|
|||
|
|
this.addChild(ui);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function onUpdate(dt) {
|
|||
|
|
// 每帧更新
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 实现步骤
|
|||
|
|
|
|||
|
|
### 阶段 1: 基础架构
|
|||
|
|
|
|||
|
|
1. 创建 `base/module.h/cpp`(Kiwano 风格)
|
|||
|
|
2. 修改 `Application` 支持模块注册
|
|||
|
|
3. 实现模块生命周期管理
|
|||
|
|
|
|||
|
|
### 阶段 2: 脚本模块
|
|||
|
|
|
|||
|
|
1. 创建 `script/script_module.h/cpp`
|
|||
|
|
2. 实现配置解析(`ScriptConfig.cfg`)
|
|||
|
|
3. 实现脚本加载器
|
|||
|
|
|
|||
|
|
### 阶段 3: C++ 绑定扩展
|
|||
|
|
|
|||
|
|
1. 扩展 `Node` 绑定(添加 `C_Object` 指针桥接)
|
|||
|
|
2. 添加 `Stage` 类绑定
|
|||
|
|
3. 添加 `Director` 类绑定
|
|||
|
|
4. 添加 `Application` 配置绑定
|
|||
|
|
|
|||
|
|
### 阶段 4: 脚本类库
|
|||
|
|
|
|||
|
|
1. 创建 `scripts/core/` 基础工具
|
|||
|
|
2. 创建 `scripts/engine/` 引擎封装
|
|||
|
|
3. 实现类继承机制
|
|||
|
|
|
|||
|
|
### 阶段 5: 入口机制
|
|||
|
|
|
|||
|
|
1. 实现 `main()` 函数自动调用
|
|||
|
|
2. 实现 `GameWindow` 封装类
|
|||
|
|
3. 创建示例项目
|
|||
|
|
|
|||
|
|
## 文件变更清单
|
|||
|
|
|
|||
|
|
### 新增文件
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
src/extra2d/base/module.h
|
|||
|
|
src/extra2d/base/module.cpp
|
|||
|
|
src/extra2d/script/script_module.h
|
|||
|
|
src/extra2d/script/script_module.cpp
|
|||
|
|
src/extra2d/script/sq_binding_stage.cpp
|
|||
|
|
src/extra2d/script/sq_binding_director.cpp
|
|||
|
|
scripts/ScriptConfig.cfg
|
|||
|
|
scripts/core/_init.nut
|
|||
|
|
scripts/core/class.nut
|
|||
|
|
scripts/engine/_init.nut
|
|||
|
|
scripts/engine/application.nut
|
|||
|
|
scripts/engine/director.nut
|
|||
|
|
scripts/engine/stage.nut
|
|||
|
|
scripts/user/main.nut
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 修改文件
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
src/extra2d/app/application.h/cpp
|
|||
|
|
src/extra2d/script/script_engine.cpp
|
|||
|
|
src/extra2d/script/sq_binding_node.cpp
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 最终效果
|
|||
|
|
|
|||
|
|
用户只需编写脚本即可创建完整游戏:
|
|||
|
|
|
|||
|
|
```squirrel
|
|||
|
|
// 纯脚本游戏
|
|||
|
|
function main() {
|
|||
|
|
local app = Application();
|
|||
|
|
app.config({
|
|||
|
|
title = "Hello World",
|
|||
|
|
width = 800,
|
|||
|
|
height = 600
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
local director = Director();
|
|||
|
|
director.enterStage(MyStage());
|
|||
|
|
|
|||
|
|
app.run();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class MyStage extends Stage {
|
|||
|
|
function onEnter() {
|
|||
|
|
local label = Label("Hello Squirrel!");
|
|||
|
|
label.position = [400, 300];
|
|||
|
|
this.addChild(label);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
请确认此方案后,我将开始
|