sapdon 3.2.2 → 3.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +245 -121
- package/doc/dev/architecture.md +415 -0
- package/doc/dev/cli.md +467 -0
- package/doc/dev/core.md +717 -0
- package/doc/dev/oc.md +582 -0
- package/doc/user/api/biome.md +558 -0
- package/doc/user/api/block.md +945 -0
- package/doc/user/api/entity.md +685 -0
- package/doc/user/api/extra.md +231 -0
- package/doc/user/api/item.md +896 -0
- package/doc/user/api/recipe.md +427 -0
- package/doc/user/api/texture.md +181 -0
- package/doc/user/config/build-config.md +84 -0
- package/doc/user/config/mod-info.md +33 -0
- package/doc/user/faq.md +160 -0
- package/doc/user/quick-start.md +108 -0
- package/doc/user/tutorials/block.md +463 -0
- package/doc/user/tutorials/entity.md +356 -0
- package/doc/user/tutorials/item.md +449 -0
- package/doc/user/tutorials/recipe.md +278 -0
- package/package.json +4 -3
- package/prod/cli/index.js +1 -1
- package/prod/cli/start.js +1 -1458
- package/prod/core/index.d.ts +3290 -2150
- package/prod/core/index.js +1 -59015
- package/prod/core/package.json +7 -0
- package/prod/oc/index.d.ts +348 -108
- package/prod/oc/index.js +1 -1
- package/prod/oc/package.json +7 -0
- package/prod/utils/index.d.ts +20 -2
- package/prod/utils/index.js +1 -1
- package/prod/utils/package.json +7 -0
- package/doc/BlockAPI.md +0 -145
- package/doc/api.md +0 -128
- package/doc/oc/index.md +0 -0
- package/doc/sapdon-ts.md +0 -64
- package/src/templates/js_sapdon/build.config +0 -23
- package/src/templates/js_sapdon/main.mjs +0 -4
- package/src/templates/js_sapdon/mod.info +0 -7
- package/src/templates/js_sapdon/pack_icon.png +0 -0
- package/src/templates/js_sapdon/package.json +0 -20
- package/src/templates/js_sapdon/res/animations/animation_item.animation.json +0 -34
- package/src/templates/js_sapdon/res/animations/large_item.animation.json +0 -27
- package/src/templates/js_sapdon/res/models/blocks/crop.geo.json +0 -48
- package/src/templates/js_sapdon/res/models/entity/animation/animation_item.geo.json +0 -26
- package/src/templates/js_sapdon/res/models/entity/animation/large_item.geo.json +0 -28
- package/src/templates/js_sapdon/res/textures/blocks/none.png +0 -0
- package/src/templates/js_sapdon/res/textures/blocks/test_log_oak.png +0 -0
- package/src/templates/js_sapdon/res/textures/blocks/test_log_top.png +0 -0
- package/src/templates/js_sapdon/res/textures/items/masterball.png +0 -0
- package/src/templates/js_sapdon/scripts/custom_components/cropComponent.js +0 -50
- package/src/templates/js_sapdon/scripts/custom_components/items/gui_book.js +0 -37
- package/src/templates/js_sapdon/scripts/custom_components/registry.js +0 -25
- package/src/templates/js_sapdon/scripts/index.js +0 -0
- package/src/templates/ts_sapdon/build.config +0 -23
- package/src/templates/ts_sapdon/main.ts +0 -11
- package/src/templates/ts_sapdon/mod.info +0 -7
- package/src/templates/ts_sapdon/pack_icon.png +0 -0
- package/src/templates/ts_sapdon/package.json +0 -20
- package/src/templates/ts_sapdon/res/models/blocks/crop.geo.json +0 -48
- package/src/templates/ts_sapdon/res/textures/blocks/test_log_oak.png +0 -0
- package/src/templates/ts_sapdon/res/textures/blocks/test_log_top.png +0 -0
- package/src/templates/ts_sapdon/res/textures/items/masterball.png +0 -0
- package/src/templates/ts_sapdon/scripts/components/cropComponent.ts +0 -44
- package/src/templates/ts_sapdon/scripts/components/items/guiBook.ts +0 -36
- package/src/templates/ts_sapdon/scripts/components/registry.ts +0 -24
- package/src/templates/ts_sapdon/scripts/index.ts +0 -7
- package/src/templates/ts_sapdon/tsconfig.json +0 -117
package/doc/dev/oc.md
ADDED
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
# OC 运行时模块文档
|
|
2
|
+
|
|
3
|
+
`src/oc/` 是 Sapdon 的 **OC (Object-Component) 运行时**,是一个轻量级 ECS 风格游戏框架,专门用于 Minecraft Bedrock 版 Script API 环境。作为 `@sapdon/runtime` 发布。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. 架构总览
|
|
8
|
+
|
|
9
|
+
OC 模块实现了一个简化的 **Entity-Component 架构**:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
┌──────────────────────────────────────────────────┐
|
|
13
|
+
│ Minecraft 世界 │
|
|
14
|
+
│ │
|
|
15
|
+
│ ┌──────────────┐ ┌──────────────┐ │
|
|
16
|
+
│ │ Entity A │ │ Entity B │ │
|
|
17
|
+
│ │ │ │ │ │
|
|
18
|
+
│ │ ComponentManager │ │ ComponentManager │ │
|
|
19
|
+
│ │ ├─ HealthComp │ │ ├─ MoveComp │ │
|
|
20
|
+
│ │ ├─ InputComp │ │ ├─ RenderComp │ │
|
|
21
|
+
│ │ └─ HudComp │ │ └─ HealthComp │ │
|
|
22
|
+
│ └──────────────┘ └──────────────┘ │
|
|
23
|
+
│ │
|
|
24
|
+
│ ┌────────────────────────────────────────────────┐ │
|
|
25
|
+
│ │ Level (实体集合) │ │
|
|
26
|
+
│ │ └─ Scheduler (驱动 tick 循环) │ │
|
|
27
|
+
│ └────────────────────────────────────────────────┘ │
|
|
28
|
+
└──────────────────────────────────────────────────┘
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**核心概念:**
|
|
32
|
+
|
|
33
|
+
| 概念 | 说明 |
|
|
34
|
+
|------|------|
|
|
35
|
+
| **Component** | 附着在实体上的数据+行为单元,每个 Component 有自己的 `onTick()` |
|
|
36
|
+
| **ComponentManager** | 每个实体一个,管理其所有 Component 的生命周期 |
|
|
37
|
+
| **Level** | 实体集合,以 string ID 索引,每个 ID 对应一个 ComponentManager |
|
|
38
|
+
| **Scheduler** | 驱动游戏循环,每 tick 遍历所有实体的所有 Component 调用 `onTick()` |
|
|
39
|
+
| **Optional\<T\>** | Monadic 空值包装器 |
|
|
40
|
+
| **EventEmitter** | 自定义发布/订阅事件系统 |
|
|
41
|
+
|
|
42
|
+
### 目录结构
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
src/oc/
|
|
46
|
+
├── index.ts # 包入口,聚合导出
|
|
47
|
+
├── core.ts # 核心 ECS:Component, ComponentManager, lazyGet, RequireComponents
|
|
48
|
+
├── arch.ts # 引导启动:GameInstance, initialize/finalize
|
|
49
|
+
├── optional.ts # Optional Monad
|
|
50
|
+
├── level.ts # Level + Scheduler 接口
|
|
51
|
+
├── events/
|
|
52
|
+
│ └── index.ts # EventEmitter (自定义事件系统)
|
|
53
|
+
├── input/
|
|
54
|
+
│ └── base.ts # PlayerInputComponent (按键/轴输入)
|
|
55
|
+
├── math/
|
|
56
|
+
│ ├── index.ts # 聚合导出
|
|
57
|
+
│ ├── array.ts # ArrayEx (数组基类)
|
|
58
|
+
│ ├── spline.ts # SplineInterpolator (样条插值)
|
|
59
|
+
│ ├── vec/vec3.ts # Vec3 (3D 向量)
|
|
60
|
+
│ ├── vec/vec4.ts # Vec4 (4D 向量)
|
|
61
|
+
│ └── mat/mat4.ts # Matrix (4x4 矩阵)
|
|
62
|
+
├── action/
|
|
63
|
+
│ ├── interface.ts # ActionValueType, IAction, IActionTrigger, IActionModifier
|
|
64
|
+
│ ├── triggers.ts # ActionTriggers (pressed/released/hold 等)
|
|
65
|
+
│ └── modifiers.ts # ActionModifiers (negate/scale)
|
|
66
|
+
├── ui/hud.ts # HudComponent (基类)
|
|
67
|
+
└── minecraft/ # Minecraft 集成层
|
|
68
|
+
├── index.ts # 聚合导出
|
|
69
|
+
├── core.ts # MinecraftTickingScheduler, MinecraftLevel, MinecraftGameInstance
|
|
70
|
+
├── decorator.ts # @Minecraft, @MinecraftMain, @PlayerSpawned, @EntitySpawned, @SpawnFilter
|
|
71
|
+
├── utils.ts # MinecraftMethod 装饰器, utils 单例
|
|
72
|
+
├── scriptEvent.ts # @ScriptEvent 装饰器 + 事件桥接
|
|
73
|
+
├── input/
|
|
74
|
+
│ └── inputComponent.ts # MinecraftPlayerInputComponent
|
|
75
|
+
├── component/
|
|
76
|
+
│ ├── kinematics.ts # EasyKinematicsComponent (力驱动运动)
|
|
77
|
+
│ └── vehicleComponent.ts # EasyVehicleComponent (载具物理)
|
|
78
|
+
└── ui/
|
|
79
|
+
└── actionbar.ts # PlayerHudComponent (actionbar HUD)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 2. Component 系统 (`core.ts`)
|
|
85
|
+
|
|
86
|
+
### 2.1 接口层级
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
Component<Actor> (接口)
|
|
90
|
+
├── onTick(dt: number): void
|
|
91
|
+
├── detach(): void
|
|
92
|
+
├── getManager(): ComponentManager<Actor>
|
|
93
|
+
└── getEntity(): Actor
|
|
94
|
+
|
|
95
|
+
BasicComponent<Actor> (接口,extends Component)
|
|
96
|
+
├── onAttach(manager: ComponentManager<Actor>): void
|
|
97
|
+
└── onDetach(manager: ComponentManager<Actor>): void
|
|
98
|
+
|
|
99
|
+
RequiredComponent<Actor> (接口,extends BasicComponent)
|
|
100
|
+
└── getComponent<T>(ctor): T
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 2.2 实现层级
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
CustomComponent<Actor> (抽象类,实现 Component)
|
|
107
|
+
└── BaseComponent<Actor> (类,实现 BasicComponent)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**CustomComponent** 使用 `Symbol` 存储 manager 和 entity 引用:
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
const REFLECT_MANAGER = Symbol('manager')
|
|
114
|
+
const REFLECT_ENTITY = Symbol('entity')
|
|
115
|
+
|
|
116
|
+
class CustomComponent<Actor> {
|
|
117
|
+
onTick(dt: number) {} // 默认空实现
|
|
118
|
+
detach() { this.getManager()?.update() }
|
|
119
|
+
getManager() { return this[REFLECT_MANAGER] }
|
|
120
|
+
getEntity() { return this[REFLECT_ENTITY] }
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**BaseComponent** 添加空的 `onAttach` / `onDetach` 钩子。
|
|
125
|
+
|
|
126
|
+
### 2.3 Component 生命周期
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
构造 (new Component())
|
|
130
|
+
│
|
|
131
|
+
▼
|
|
132
|
+
onAttach(manager) ← 被 ComponentManager.attachComponent() 调用
|
|
133
|
+
│
|
|
134
|
+
▼
|
|
135
|
+
onTick(dt) ← 每帧由 ComponentManager.handleTicks() 调用
|
|
136
|
+
│ (可被多次调用)
|
|
137
|
+
▼
|
|
138
|
+
detach() / onDetach(manager) ← 被 ComponentManager.detachComponent() 调用
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 2.4 ComponentManager
|
|
142
|
+
|
|
143
|
+
每个实体拥有一个 `ComponentManager`,管理 `Map<ComponentCtor, Component>`:
|
|
144
|
+
|
|
145
|
+
| 方法 | 说明 |
|
|
146
|
+
|------|------|
|
|
147
|
+
| `attachComponent(component)` | 附加组件,调用 `onAttach`,处理 `RequiredComponent` 依赖 |
|
|
148
|
+
| `detachComponent(ctor)` | 分离组件,调用 `onDetach` |
|
|
149
|
+
| `getComponent<T>(ctor)` | 获取组件,不存在则报错 |
|
|
150
|
+
| `getComponentUnsafe<T>(ctor)` | 安全获取,返回 `Optional<T>` |
|
|
151
|
+
| `getOrCreate<T>(ctor, ...args)` | 获取或创建 |
|
|
152
|
+
| `has(ctor)` | 检查组件是否存在 |
|
|
153
|
+
| `clear()` | 清除所有组件 |
|
|
154
|
+
| `handleTicks(en, dt)` | 遍历所有组件调用 `onTick`,支持 before/after 队列 |
|
|
155
|
+
| `update()` | 触发组件重附加(用于依赖变更后刷新) |
|
|
156
|
+
|
|
157
|
+
**全局单例**:`ComponentManager.global` 用于存放全局(非实体相关)组件。
|
|
158
|
+
|
|
159
|
+
**性能分析**:`ComponentManager.profilerEnable = true` 可启用 tick 耗时日志。
|
|
160
|
+
|
|
161
|
+
### 2.5 RequiredComponent (依赖注入)
|
|
162
|
+
|
|
163
|
+
`RequireComponents(...params)` 是一个 mixin 工厂,创建一个带依赖的 Component 类:
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
// 用法示例
|
|
167
|
+
const MyComponent = RequireComponents(
|
|
168
|
+
HealthComponent,
|
|
169
|
+
[MovementComponent, 1.0], // 带构造参数的依赖
|
|
170
|
+
TransformComponent
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
class MyComponent extends BaseComponent<Entity> {
|
|
174
|
+
// 通过 this.getComponent(HealthComponent) 访问依赖
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
每次 `attachComponent` 时,如果组件有 `REQUIRED_COMPONENTS` 符号属性,这些依赖会被自动构造并附加。
|
|
179
|
+
|
|
180
|
+
### 2.6 lazyGet
|
|
181
|
+
|
|
182
|
+
`lazyGet(component, ctor)` 返回一个 **Proxy**,在首次访问属性时才通过 manager 查找目标组件。用于组件间交叉引用,避免循环依赖问题。
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 3. Level 与 Scheduler (`level.ts`)
|
|
187
|
+
|
|
188
|
+
### 3.1 Level
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
abstract class Level<T> {
|
|
192
|
+
protected table: Map<string, ComponentManager<T>>
|
|
193
|
+
|
|
194
|
+
addEntity(id: string): ComponentManager<T>
|
|
195
|
+
removeEntity(id: string): void
|
|
196
|
+
getManager(id: string): ComponentManager<T>
|
|
197
|
+
|
|
198
|
+
abstract getScheduler(): Scheduler<T>
|
|
199
|
+
|
|
200
|
+
start() // → scheduler.start(table)
|
|
201
|
+
stop() // → scheduler.stop()
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
- 实体用 string ID 标识
|
|
206
|
+
- `getManager(id)` 在不存在时自动创建新 manager
|
|
207
|
+
- `start()` / `stop()` 委托给 scheduler
|
|
208
|
+
|
|
209
|
+
### 3.2 Scheduler
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
interface Scheduler<Actor> {
|
|
213
|
+
currentTick: number
|
|
214
|
+
timeDilation: number
|
|
215
|
+
|
|
216
|
+
start(table: Map<string, ComponentManager<Actor>>): void
|
|
217
|
+
stop(): void
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
`timeDilation` 属性可加速/减速游戏时间(默认 1.0)。
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## 4. Minecraft 集成
|
|
226
|
+
|
|
227
|
+
### 4.1 整体启动流程
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
Minecraft 启动
|
|
231
|
+
│
|
|
232
|
+
▼
|
|
233
|
+
@MinecraftMain 装饰器
|
|
234
|
+
→ GameInstance(target) 注册游戏类
|
|
235
|
+
→ utils.start() 挂载生命周期钩子
|
|
236
|
+
│
|
|
237
|
+
▼
|
|
238
|
+
system.beforeEvents.startup 触发
|
|
239
|
+
│
|
|
240
|
+
▼
|
|
241
|
+
initialize(GameInstanceClass)
|
|
242
|
+
→ new GameInstanceClass()
|
|
243
|
+
→ instance.onStart()
|
|
244
|
+
│
|
|
245
|
+
▼
|
|
246
|
+
MinecraftGameInstance.onStart()
|
|
247
|
+
→ 创建 MinecraftLevel
|
|
248
|
+
→ 创建 MinecraftTickingScheduler
|
|
249
|
+
→ level.start() → scheduler.start(table)
|
|
250
|
+
→ 安排 afterStart() 下一 tick 执行
|
|
251
|
+
│
|
|
252
|
+
▼
|
|
253
|
+
每游戏 tick:
|
|
254
|
+
scheduler.executeTick(table)
|
|
255
|
+
→ 遍历每个实体的 ComponentManager
|
|
256
|
+
→ manager.handleTicks(entity, dt)
|
|
257
|
+
→ 每个 Component.onTick(dt)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### 4.2 MinecraftTickingScheduler
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
@Minecraft
|
|
264
|
+
class MinecraftTickingScheduler implements Scheduler<string> {
|
|
265
|
+
currentTick = 0
|
|
266
|
+
timeDilation = 1
|
|
267
|
+
|
|
268
|
+
start(table: Map<string, ComponentManager<Entity>>) {
|
|
269
|
+
system.runInterval(() => {
|
|
270
|
+
this.executeTick(table)
|
|
271
|
+
})
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
executeTick(table) {
|
|
275
|
+
this.currentTick++
|
|
276
|
+
const dt = this._timeStamp * this.timeDilation
|
|
277
|
+
for (const [id, manager] of table) {
|
|
278
|
+
// 使用 world.getEntity(id) 获取 Minecraft Entity 对象
|
|
279
|
+
manager.handleTicks(entity, dt)
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### 4.3 MinecraftGameInstance
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
abstract class MinecraftGameInstance implements GameInstance {
|
|
289
|
+
onStart() {
|
|
290
|
+
const level = new MinecraftLevel()
|
|
291
|
+
this.setLevel(level)
|
|
292
|
+
level.start()
|
|
293
|
+
system.run(() => this.afterStart?.()) // 下一 tick 执行 afterStart
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
用户自定义游戏类 extends `MinecraftGameInstance`,实现 `onStart()` 和可选的 `afterStart()`。
|
|
299
|
+
|
|
300
|
+
### 4.4 装饰器
|
|
301
|
+
|
|
302
|
+
#### 类装饰器
|
|
303
|
+
|
|
304
|
+
| 装饰器 | 说明 |
|
|
305
|
+
|--------|------|
|
|
306
|
+
| `@Minecraft` | 代理构造,确保在 Minecraft 环境中运行 |
|
|
307
|
+
| `@MinecraftMain` | 标记为主游戏类,注册 GameInstance + 挂载生命周期 + 设置生成监听 |
|
|
308
|
+
| `@PlayerSpawned(...deps)` | 标记在玩家生成时自动附加的 Component |
|
|
309
|
+
| `@EntitySpawned(...deps)` | 标记在实体生成时自动附加的 Component |
|
|
310
|
+
| `@ActorSpawned(...deps)` | 同时监听玩家和实体生成 |
|
|
311
|
+
| `@SpawnFilter(filter)` | 条件过滤:`(entity) => boolean`,返回 false 则不附加 |
|
|
312
|
+
|
|
313
|
+
#### 方法装饰器
|
|
314
|
+
|
|
315
|
+
| 装饰器 | 说明 |
|
|
316
|
+
|--------|------|
|
|
317
|
+
| `@MinecraftMethod` | 包装方法,执行前断言 Minecraft 环境 |
|
|
318
|
+
| `@ScriptEvent(eventId)` | 绑定方法到 Minecraft script event |
|
|
319
|
+
|
|
320
|
+
### 4.5 生成时自动附加
|
|
321
|
+
|
|
322
|
+
`@MinecraftMain` 内部逻辑:
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
world.afterEvents.playerSpawn
|
|
326
|
+
→ 查找所有 @PlayerSpawned 装饰的类
|
|
327
|
+
→ 构造实例 + attachComponent(playerManager)
|
|
328
|
+
→ @SpawnFilter 可过滤
|
|
329
|
+
|
|
330
|
+
world.afterEvents.entitySpawn (排除 player)
|
|
331
|
+
→ 查找所有 @EntitySpawned 装饰的类
|
|
332
|
+
→ 构造实例 + attachComponent(entityManager)
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### 4.6 ScriptEvent 桥接
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
// 方法装饰器用法
|
|
339
|
+
class MySystem {
|
|
340
|
+
@ScriptEvent('myAddon:event')
|
|
341
|
+
handleEvent(ev: ScriptEventCommandMessageAfterEvent) {
|
|
342
|
+
console.log('event received', ev)
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// 编程式用法
|
|
347
|
+
ScriptEvent.on('myAddon:event', handler)
|
|
348
|
+
ScriptEvent.off('myAddon:event', handler)
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
内部通过 `EventEmitter` + `system.afterEvents.scriptEventReceive` 实现。
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## 5. 输入系统 (`input/`)
|
|
356
|
+
|
|
357
|
+
### 5.1 PlayerInputComponent
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
class PlayerInputComponent<Actor> extends BaseComponent<Actor> {
|
|
361
|
+
// 按键状态管理
|
|
362
|
+
inputKey(key: string, pressing: boolean): void
|
|
363
|
+
getKeyState(key: string): IKeyState
|
|
364
|
+
getKeyPressing(key: string): boolean
|
|
365
|
+
getKeyPressTimes(key: string): number
|
|
366
|
+
exhaust(key: string): void // 重置按键次数
|
|
367
|
+
|
|
368
|
+
// 轴输入
|
|
369
|
+
inputAxis(key: string, value: AxisValue): void
|
|
370
|
+
getAxis(key: string): AxisValue
|
|
371
|
+
|
|
372
|
+
// 轴工具
|
|
373
|
+
combineAxis(axis1, axis2): AxisValue
|
|
374
|
+
splitAxis(axis, nDim1, nDim2?): [AxisValue, AxisValue]
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
**IKeyState**:`{ pressing: boolean, times: number, consume(): void }`
|
|
379
|
+
|
|
380
|
+
**AxisValue**:`number | [number, number] | [number, number, number]`
|
|
381
|
+
|
|
382
|
+
### 5.2 MinecraftPlayerInputComponent
|
|
383
|
+
|
|
384
|
+
extends `PlayerInputComponent<Entity>`。集成 Minecraft 玩家的按钮输入和摇杆轴:
|
|
385
|
+
|
|
386
|
+
- 通过 `world.afterEvents.playerButtonInput` 监听按钮事件
|
|
387
|
+
- 每 tick 通过 `player.inputInfo.getMovementVector()` 获取移动轴
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## 6. 动作系统 (`action/`)
|
|
392
|
+
|
|
393
|
+
| 导出 | 说明 |
|
|
394
|
+
|------|------|
|
|
395
|
+
| `ActionValueType` | 枚举:`BOOL(0)`, `VEC(1)`, `VEC2(2)`, `VEC3(3)` |
|
|
396
|
+
| `IActionModifier<E, V>` | 变换函数:`(type: E, value: V) => V` |
|
|
397
|
+
| `IActionTrigger` | 触发判定:`(type, value, ticks) => boolean` |
|
|
398
|
+
| `IAction<E>` | 组合:`valueType + modifiers[] + triggers[]` |
|
|
399
|
+
| `ActionTriggers` | 工厂:`pressed()`, `released()`, `hold(act, hold)`, `holdThenRelease(act)` |
|
|
400
|
+
| `ActionModifiers` | 工厂:`negate(mask)`, `scale(scalar)` |
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
## 7. 事件系统 (`events/`)
|
|
405
|
+
|
|
406
|
+
### EventEmitter
|
|
407
|
+
|
|
408
|
+
自定义实现,使用**双向链表**实现 O(1) 的添加/移除:
|
|
409
|
+
|
|
410
|
+
| 方法 | 说明 |
|
|
411
|
+
|------|------|
|
|
412
|
+
| `on(type, handler)` | 订阅事件 |
|
|
413
|
+
| `once(type, handler)` | 一次性订阅 |
|
|
414
|
+
| `prependListener(type, handler)` | 插到监听器列表头部 |
|
|
415
|
+
| `prependOnceListener(type, handler)` | 一次性插到头部 |
|
|
416
|
+
| `off(type, handler)` / `removeListener(type, handler)` | 取消订阅 |
|
|
417
|
+
| `removeAllListeners(type)` | 移除所有 |
|
|
418
|
+
| `emit(type, ...args)` | 触发事件(使用 bind thisArg) |
|
|
419
|
+
| `emitNone(type, ...args)` | 触发事件(不使用 thisArg) |
|
|
420
|
+
| `listenerCount(type)` | 监听器数量 |
|
|
421
|
+
| `listeners(type)` / `rawListeners(type)` | 获取监听器列表 |
|
|
422
|
+
| `eventNames()` | 获取所有事件名 |
|
|
423
|
+
| `setMaxListeners(n)` | 设置最大监听器数 |
|
|
424
|
+
|
|
425
|
+
特点:
|
|
426
|
+
- 双向链表实现,O(1) add/remove
|
|
427
|
+
- 支持 `captureRejections`(捕获 Promise 返回的 handler 的 rejection)
|
|
428
|
+
- emit 过程中的错误会被捕获并重定向到 `'error'` 事件
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## 8. 数学库 (`math/`)
|
|
433
|
+
|
|
434
|
+
### Vec3 / Vec4 / Matrix
|
|
435
|
+
|
|
436
|
+
向量和矩阵继承自 `ArrayEx`(extends `Array`):
|
|
437
|
+
|
|
438
|
+
**Vec3**:`fromXYZ()`、`normalize()`、`dot()`、`cross()`、`add()`、`sub()`、`mul()`、`div()`
|
|
439
|
+
|
|
440
|
+
**Vec4**:同上 + `multiply(Matrix)`(矩阵乘法)
|
|
441
|
+
|
|
442
|
+
**Matrix**(4x4):
|
|
443
|
+
- 静态:`identity()`、`translate()`、`perspective()`、`orthographic()`、`lookAt()`
|
|
444
|
+
- 实例:`setIdentity()`、`setTranslation()`、`setRotation()`、`setRotationX/Y/Z()`、`setScale()`、`transpose()`、`multiply()`
|
|
445
|
+
|
|
446
|
+
### SplineInterpolator
|
|
447
|
+
|
|
448
|
+
五点平滑样条插值(quincunx 算法):
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
const spline = SplineInterpolator.fromPoints(points, lambda)
|
|
452
|
+
spline.interpolate(x) // 插值
|
|
453
|
+
spline.max(step) // 找最大值
|
|
454
|
+
spline.min(step) // 找最小值
|
|
455
|
+
spline.curve(nInterval) // 获取曲线点集
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
## 9. HUD 系统 (`ui/`)
|
|
461
|
+
|
|
462
|
+
### HudComponent
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
abstract class HudComponent<Actor, Hud> extends CustomComponent<Actor> {
|
|
466
|
+
hud: Hud
|
|
467
|
+
actor: Optional<Actor>
|
|
468
|
+
|
|
469
|
+
abstract render(): Hud
|
|
470
|
+
abstract draw(): void
|
|
471
|
+
|
|
472
|
+
onTick(dt: number) {
|
|
473
|
+
this.hud = this.render()
|
|
474
|
+
this.draw()
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
每 tick 调用 `render()` 生成 HUD 数据,然后调用 `draw()` 渲染。
|
|
480
|
+
|
|
481
|
+
### PlayerHudComponent
|
|
482
|
+
|
|
483
|
+
extends `HudComponent<Player, string>`。使用 `/title @s actionbar` 命令将字符串渲染到玩家动作栏。
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
## 10. 物理组件
|
|
488
|
+
|
|
489
|
+
### EasyKinematicsComponent
|
|
490
|
+
|
|
491
|
+
```typescript
|
|
492
|
+
class EasyKinematicsComponent extends CustomComponent<Entity> {
|
|
493
|
+
mass = 1
|
|
494
|
+
allowRotation = false
|
|
495
|
+
simulated = false // false=applyImpulse, true=tryTeleport
|
|
496
|
+
force: Vec3
|
|
497
|
+
angularVelocity: Vec3
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
每 tick 应用力和角速度。`simulated = false` 时使用 `applyImpulse`,`true` 时使用 `tryTeleport` + 牛顿运动学公式。
|
|
502
|
+
|
|
503
|
+
### EasyVehicleComponent
|
|
504
|
+
|
|
505
|
+
载具物理数据:
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
class EasyVehicleComponent extends CustomComponent<Entity> {
|
|
509
|
+
width = 2, height = 1.5, length = 4.5
|
|
510
|
+
mass = 1500
|
|
511
|
+
inertia: Vec3 // 由 _calcInertia() 自动计算(矩形棱柱公式)
|
|
512
|
+
}
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
## 11. Optional Monad (`optional.ts`)
|
|
518
|
+
|
|
519
|
+
```typescript
|
|
520
|
+
class Optional<T> {
|
|
521
|
+
static none<T>(): Optional<T> // 空值
|
|
522
|
+
static some<T>(value?: T): Optional<T> // 包装值
|
|
523
|
+
|
|
524
|
+
unwrap(): T // 取值,空则抛错
|
|
525
|
+
isEmpty(): boolean // 是否为 null/undefined
|
|
526
|
+
orElse(other: T): T // 空则返回默认值
|
|
527
|
+
use<R>(fn, self?): R | Optional<R> // monadic bind (flatMap)
|
|
528
|
+
}
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
`use()` 是 flatMap 操作:非空时调用 `fn(value)`,如果返回值是 Optional 则直接返回,否则用 `Optional.some()` 包装;空时返回 `Optional.none()`。
|
|
532
|
+
|
|
533
|
+
---
|
|
534
|
+
|
|
535
|
+
## 12. 类继承关系总图
|
|
536
|
+
|
|
537
|
+
```
|
|
538
|
+
Array (原生)
|
|
539
|
+
└── ArrayEx
|
|
540
|
+
├── Vec3
|
|
541
|
+
├── Vec4
|
|
542
|
+
└── Matrix
|
|
543
|
+
|
|
544
|
+
CustomComponent<Actor> (抽象)
|
|
545
|
+
├── BaseComponent<Actor>
|
|
546
|
+
│ ├── PlayerInputComponent<Actor>
|
|
547
|
+
│ │ └── MinecraftPlayerInputComponent
|
|
548
|
+
│ ├── HudComponent<Actor, Hud> (抽象)
|
|
549
|
+
│ │ └── PlayerHudComponent (抽象)
|
|
550
|
+
│ └── RequireComponents() 生成的类
|
|
551
|
+
├── EasyKinematicsComponent
|
|
552
|
+
└── EasyVehicleComponent
|
|
553
|
+
|
|
554
|
+
Level<T> (抽象)
|
|
555
|
+
└── MinecraftLevel
|
|
556
|
+
|
|
557
|
+
Scheduler<Actor> (接口)
|
|
558
|
+
└── MinecraftTickingScheduler
|
|
559
|
+
|
|
560
|
+
GameInstance<A> (接口)
|
|
561
|
+
└── MinecraftGameInstance (抽象)
|
|
562
|
+
|
|
563
|
+
EventEmitter (独立)
|
|
564
|
+
Optional<T> (独立)
|
|
565
|
+
SplineInterpolator (独立)
|
|
566
|
+
KeyState (独立)
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
---
|
|
570
|
+
|
|
571
|
+
## 13. 包入口 (`index.ts`)
|
|
572
|
+
|
|
573
|
+
```typescript
|
|
574
|
+
// src/oc/index.ts
|
|
575
|
+
export * from './core.js' // Component 体系 + ComponentManager
|
|
576
|
+
export * from './optional.js' // Optional<T>
|
|
577
|
+
export * from './input/base.js' // PlayerInputComponent
|
|
578
|
+
export * from './arch.js' // GameInstance, initialize
|
|
579
|
+
export * from './math/index.js' // Vec3, Vec4, Matrix, Spline, MathExt
|
|
580
|
+
export * from './minecraft/index.js' // Minecraft 集成全部
|
|
581
|
+
export * from './ui/hud.js' // HudComponent
|
|
582
|
+
```
|