跳转到内容

插件系统

插件系统将引擎功能组织为可复用的模块。每个插件提供一组组件、系统和资源。Estella 内置了多个插件,它们会自动安装——你无需手动配置。

内置插件

在编辑器中创建项目时,所有内置插件默认处于激活状态。它们提供了你在检查器(Inspector)中看到的组件,以及驱动引擎每帧行为的系统。

核心插件

插件提供的功能
资源加载AssetServer,用于在运行时加载纹理、Spine 数据、预制体等资源
预制体Prefabs,用于实例化预制体
输入Input,包含键盘、鼠标和触摸输入——每帧末尾清除
时间轴TimelinePlayer 组件用于播放时间轴动画,TimelineControl 运行时控制 API

UI 插件

插件组件
文本Text — 每帧将文本内容渲染为精灵纹理
图片Image — 显示图片,支持切片、平铺和填充模式
UI 遮罩UIMask — 基于裁剪/模板测试的 UI 元素遮罩
UI 布局UIRect — 相对于相机的锚点 UI 布局
UI 渲染排序自动为 UI 层级分配渲染顺序
UI 交互InteractableButtonUIInteractionUIEvents — 指针命中测试和按钮状态
文本输入TextInput — 可编辑的文本输入框,使用平台原生输入
开关ToggleToggleGroup — 复选框和单选按钮行为
滑块Slider — 带填充和拖拽手柄的交互式值滑块
进度条ProgressBar — 非交互式进度显示
下拉框Dropdown — 下拉列表选择器
滚动视图ScrollView — 支持惯性和弹性回弹的可滚动容器
列表视图ListView — 大列表的虚拟滚动
拖拽Draggable — UI 元素的拖拽移动
焦点Focusable — 键盘焦点和 Tab 导航
安全区域SafeArea — 适配设备安全区域(刘海、圆角)
布局组LayoutGroup — 带方向、间距和对齐的网格式布局

以上插件在每个项目中默认激活。你可以直接从编辑器的组件选择器中使用它们的组件——无需任何设置。

用户脚本

扩展游戏的主要方式是在项目的 src/ 目录中编写 TypeScript 脚本。脚本会被自动编译并包含在构建产物中。

自定义组件

使用 defineComponent() 创建可在编辑器检查器中显示的组件:

import { defineComponent } from 'esengine';
const Health = defineComponent('Health', {
hp: 100,
maxHp: 100,
});

定义后,Health 会出现在编辑器的添加组件菜单中。你可以像内置组件一样将其添加到实体并在检查器中编辑属性。

使用 defineTag() 创建无数据的标记组件:

import { defineTag } from 'esengine';
const Enemy = defineTag('Enemy');

系统

使用 defineSystem()addSystem() 添加每帧执行的游戏逻辑:

import { defineSystem, addSystem, Schedule } from 'esengine';
addSystem(defineSystem([], () => {
// 每帧在 Update 阶段执行
}));

你也可以指定特定的调度阶段:

import { addSystemToSchedule, defineSystem, Schedule } from 'esengine';
addSystemToSchedule(Schedule.FixedUpdate, defineSystem([], () => {
// 以固定时间步执行,适合物理逻辑
}));

对于一次性初始化,使用 addStartupSystem()

import { addStartupSystem, defineSystem } from 'esengine';
addStartupSystem(defineSystem([], () => {
// 游戏启动时执行一次
}));

资源

资源是可在系统间共享的全局单例数据:

import { defineResource, addStartupSystem, defineSystem, Commands } from 'esengine';
const GameState = defineResource<{ score: number; level: number }>(
{ score: 0, level: 1 },
'GameState'
);
addStartupSystem(defineSystem([Commands()], (cmds) => {
cmds.insertResource(GameState, { score: 0, level: 1 });
}));

事件

事件用于系统间的解耦通信:

import { defineEvent, EventWriter, EventReader, defineSystem, addSystem } from 'esengine';
const ScoreEvent = defineEvent<{ points: number }>('Score');
addSystem(defineSystem([EventWriter(ScoreEvent)], (writer) => {
writer.send({ points: 10 });
}));
addSystem(defineSystem([EventReader(ScoreEvent)], (reader) => {
for (const e of reader) {
console.log('Scored:', e.points);
}
}));

系统调度

系统按照定义的顺序在每帧中执行:

阶段执行时机
Startup游戏启动时执行一次
First每帧开始
FixedPreUpdate固定时间步更新之前
FixedUpdate固定时间步更新(物理)
FixedPostUpdate固定时间步更新之后
PreUpdate主更新之前
Update主更新(addSystem 的默认阶段)
PostUpdate主更新之后
Last每帧结束

进阶:插件接口

插件是实现了 Plugin 接口的对象:

import { type Plugin, type App, defineResource, defineComponent, defineSystem, Schedule } from 'esengine';
const ScoreData = defineResource<{ value: number }>({ value: 0 }, 'Score');
const Health = defineComponent('Health', { hp: 100, maxHp: 100 });
const myPlugin: Plugin = {
name: 'MyGamePlugin',
build(app: App) {
app.insertResource(ScoreData, { value: 0 });
app.addSystemToSchedule(Schedule.Update, defineSystem([], () => {
// 游戏逻辑
}));
},
finish(app: App) {
// 所有插件 build 完成后调用 — 可安全读取其他插件注册的资源
},
cleanup(app?: App) {
// 应用关闭时调用 — 释放资源、移除监听器等
},
};

插件生命周期

方法调用时机用途
build(app)插件被添加时注册资源、系统和组件
finish?(app)所有插件 build 完成后依赖其他插件就绪的跨插件初始化
cleanup?(app)应用关闭时释放资源、清理监听器和状态

插件可以声明资源依赖:

const analyticsPlugin: Plugin = {
name: 'AnalyticsPlugin',
dependencies: [Assets],
build(app: App) {
const assets = app.getResource(Assets);
// 使用资源服务器...
},
};

注册自定义插件

在脚本的 setup 函数中注册自定义插件:

import { type App } from 'esengine';
export function setup(app: App) {
app.addPlugin(myPlugin);
}