@esengine/spatial 1.0.0
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/LICENSE +21 -0
- package/dist/index.d.ts +843 -0
- package/dist/index.js +1607 -0
- package/dist/index.js.map +1 -0
- package/module.json +23 -0
- package/package.json +41 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1607 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
4
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
|
+
|
|
6
|
+
// src/ISpatialQuery.ts
|
|
7
|
+
function createBounds(minX, minY, maxX, maxY) {
|
|
8
|
+
return {
|
|
9
|
+
minX,
|
|
10
|
+
minY,
|
|
11
|
+
maxX,
|
|
12
|
+
maxY
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
__name(createBounds, "createBounds");
|
|
16
|
+
function createBoundsFromCenter(center, width, height) {
|
|
17
|
+
const halfWidth = width / 2;
|
|
18
|
+
const halfHeight = height / 2;
|
|
19
|
+
return {
|
|
20
|
+
minX: center.x - halfWidth,
|
|
21
|
+
minY: center.y - halfHeight,
|
|
22
|
+
maxX: center.x + halfWidth,
|
|
23
|
+
maxY: center.y + halfHeight
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
__name(createBoundsFromCenter, "createBoundsFromCenter");
|
|
27
|
+
function createBoundsFromCircle(center, radius) {
|
|
28
|
+
return {
|
|
29
|
+
minX: center.x - radius,
|
|
30
|
+
minY: center.y - radius,
|
|
31
|
+
maxX: center.x + radius,
|
|
32
|
+
maxY: center.y + radius
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
__name(createBoundsFromCircle, "createBoundsFromCircle");
|
|
36
|
+
function isPointInBounds(point, bounds) {
|
|
37
|
+
return point.x >= bounds.minX && point.x <= bounds.maxX && point.y >= bounds.minY && point.y <= bounds.maxY;
|
|
38
|
+
}
|
|
39
|
+
__name(isPointInBounds, "isPointInBounds");
|
|
40
|
+
function boundsIntersect(a, b) {
|
|
41
|
+
return a.minX <= b.maxX && a.maxX >= b.minX && a.minY <= b.maxY && a.maxY >= b.minY;
|
|
42
|
+
}
|
|
43
|
+
__name(boundsIntersect, "boundsIntersect");
|
|
44
|
+
function boundsIntersectsCircle(bounds, center, radius) {
|
|
45
|
+
const closestX = Math.max(bounds.minX, Math.min(center.x, bounds.maxX));
|
|
46
|
+
const closestY = Math.max(bounds.minY, Math.min(center.y, bounds.maxY));
|
|
47
|
+
const distanceX = center.x - closestX;
|
|
48
|
+
const distanceY = center.y - closestY;
|
|
49
|
+
return distanceX * distanceX + distanceY * distanceY <= radius * radius;
|
|
50
|
+
}
|
|
51
|
+
__name(boundsIntersectsCircle, "boundsIntersectsCircle");
|
|
52
|
+
function distanceSquared(a, b) {
|
|
53
|
+
const dx = a.x - b.x;
|
|
54
|
+
const dy = a.y - b.y;
|
|
55
|
+
return dx * dx + dy * dy;
|
|
56
|
+
}
|
|
57
|
+
__name(distanceSquared, "distanceSquared");
|
|
58
|
+
function distance(a, b) {
|
|
59
|
+
return Math.sqrt(distanceSquared(a, b));
|
|
60
|
+
}
|
|
61
|
+
__name(distance, "distance");
|
|
62
|
+
|
|
63
|
+
// src/GridSpatialIndex.ts
|
|
64
|
+
var _GridSpatialIndex = class _GridSpatialIndex {
|
|
65
|
+
constructor(config) {
|
|
66
|
+
__publicField(this, "_cellSize");
|
|
67
|
+
__publicField(this, "_cells", /* @__PURE__ */ new Map());
|
|
68
|
+
__publicField(this, "_itemMap", /* @__PURE__ */ new Map());
|
|
69
|
+
this._cellSize = config.cellSize;
|
|
70
|
+
}
|
|
71
|
+
// =========================================================================
|
|
72
|
+
// ISpatialIndex 实现 | ISpatialIndex Implementation
|
|
73
|
+
// =========================================================================
|
|
74
|
+
get count() {
|
|
75
|
+
return this._itemMap.size;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* @zh 插入对象
|
|
79
|
+
* @en Insert object
|
|
80
|
+
*/
|
|
81
|
+
insert(item, position) {
|
|
82
|
+
if (this._itemMap.has(item)) {
|
|
83
|
+
this.update(item, position);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const cellKey = this._getCellKey(position);
|
|
87
|
+
const gridItem = {
|
|
88
|
+
item,
|
|
89
|
+
position: {
|
|
90
|
+
x: position.x,
|
|
91
|
+
y: position.y
|
|
92
|
+
},
|
|
93
|
+
cellKey
|
|
94
|
+
};
|
|
95
|
+
this._itemMap.set(item, gridItem);
|
|
96
|
+
this._addToCell(cellKey, gridItem);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* @zh 移除对象
|
|
100
|
+
* @en Remove object
|
|
101
|
+
*/
|
|
102
|
+
remove(item) {
|
|
103
|
+
const gridItem = this._itemMap.get(item);
|
|
104
|
+
if (!gridItem) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
this._removeFromCell(gridItem.cellKey, gridItem);
|
|
108
|
+
this._itemMap.delete(item);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* @zh 更新对象位置
|
|
113
|
+
* @en Update object position
|
|
114
|
+
*/
|
|
115
|
+
update(item, newPosition) {
|
|
116
|
+
const gridItem = this._itemMap.get(item);
|
|
117
|
+
if (!gridItem) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
const newCellKey = this._getCellKey(newPosition);
|
|
121
|
+
if (newCellKey !== gridItem.cellKey) {
|
|
122
|
+
this._removeFromCell(gridItem.cellKey, gridItem);
|
|
123
|
+
gridItem.cellKey = newCellKey;
|
|
124
|
+
this._addToCell(newCellKey, gridItem);
|
|
125
|
+
}
|
|
126
|
+
gridItem.position = {
|
|
127
|
+
x: newPosition.x,
|
|
128
|
+
y: newPosition.y
|
|
129
|
+
};
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* @zh 清空索引
|
|
134
|
+
* @en Clear index
|
|
135
|
+
*/
|
|
136
|
+
clear() {
|
|
137
|
+
this._cells.clear();
|
|
138
|
+
this._itemMap.clear();
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* @zh 获取所有对象
|
|
142
|
+
* @en Get all objects
|
|
143
|
+
*/
|
|
144
|
+
getAll() {
|
|
145
|
+
return Array.from(this._itemMap.keys());
|
|
146
|
+
}
|
|
147
|
+
// =========================================================================
|
|
148
|
+
// ISpatialQuery 实现 | ISpatialQuery Implementation
|
|
149
|
+
// =========================================================================
|
|
150
|
+
/**
|
|
151
|
+
* @zh 查找半径内的所有对象
|
|
152
|
+
* @en Find all objects within radius
|
|
153
|
+
*/
|
|
154
|
+
findInRadius(center, radius, filter) {
|
|
155
|
+
const results = [];
|
|
156
|
+
const radiusSq = radius * radius;
|
|
157
|
+
const bounds = createBoundsFromCircle(center, radius);
|
|
158
|
+
this._forEachInBounds(bounds, (gridItem) => {
|
|
159
|
+
const distSq = distanceSquared(center, gridItem.position);
|
|
160
|
+
if (distSq <= radiusSq) {
|
|
161
|
+
if (!filter || filter(gridItem.item)) {
|
|
162
|
+
results.push(gridItem.item);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
return results;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* @zh 查找矩形区域内的所有对象
|
|
170
|
+
* @en Find all objects within rectangle
|
|
171
|
+
*/
|
|
172
|
+
findInRect(bounds, filter) {
|
|
173
|
+
const results = [];
|
|
174
|
+
this._forEachInBounds(bounds, (gridItem) => {
|
|
175
|
+
const pos = gridItem.position;
|
|
176
|
+
if (pos.x >= bounds.minX && pos.x <= bounds.maxX && pos.y >= bounds.minY && pos.y <= bounds.maxY) {
|
|
177
|
+
if (!filter || filter(gridItem.item)) {
|
|
178
|
+
results.push(gridItem.item);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
return results;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* @zh 查找最近的对象
|
|
186
|
+
* @en Find nearest object
|
|
187
|
+
*/
|
|
188
|
+
findNearest(center, maxDistance, filter) {
|
|
189
|
+
let nearest = null;
|
|
190
|
+
let nearestDistSq = maxDistance !== void 0 ? maxDistance * maxDistance : Infinity;
|
|
191
|
+
const searchRadius = maxDistance ?? this._cellSize * 10;
|
|
192
|
+
const bounds = createBoundsFromCircle(center, searchRadius);
|
|
193
|
+
this._forEachInBounds(bounds, (gridItem) => {
|
|
194
|
+
const distSq = distanceSquared(center, gridItem.position);
|
|
195
|
+
if (distSq < nearestDistSq) {
|
|
196
|
+
if (!filter || filter(gridItem.item)) {
|
|
197
|
+
nearest = gridItem.item;
|
|
198
|
+
nearestDistSq = distSq;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
return nearest;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* @zh 查找最近的 K 个对象
|
|
206
|
+
* @en Find K nearest objects
|
|
207
|
+
*/
|
|
208
|
+
findKNearest(center, k, maxDistance, filter) {
|
|
209
|
+
if (k <= 0) return [];
|
|
210
|
+
const candidates = [];
|
|
211
|
+
const maxDistSq = maxDistance !== void 0 ? maxDistance * maxDistance : Infinity;
|
|
212
|
+
const searchRadius = maxDistance ?? this._cellSize * 10;
|
|
213
|
+
const bounds = createBoundsFromCircle(center, searchRadius);
|
|
214
|
+
this._forEachInBounds(bounds, (gridItem) => {
|
|
215
|
+
const distSq = distanceSquared(center, gridItem.position);
|
|
216
|
+
if (distSq <= maxDistSq) {
|
|
217
|
+
if (!filter || filter(gridItem.item)) {
|
|
218
|
+
candidates.push({
|
|
219
|
+
item: gridItem.item,
|
|
220
|
+
distSq
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
candidates.sort((a, b) => a.distSq - b.distSq);
|
|
226
|
+
return candidates.slice(0, k).map((c) => c.item);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* @zh 射线检测
|
|
230
|
+
* @en Raycast
|
|
231
|
+
*/
|
|
232
|
+
raycast(origin, direction, maxDistance, filter) {
|
|
233
|
+
const hits = [];
|
|
234
|
+
const rayBounds = this._getRayBounds(origin, direction, maxDistance);
|
|
235
|
+
this._forEachInBounds(rayBounds, (gridItem) => {
|
|
236
|
+
const hit = this._rayIntersectsPoint(origin, direction, gridItem.position, maxDistance);
|
|
237
|
+
if (hit && hit.distance <= maxDistance) {
|
|
238
|
+
if (!filter || filter(gridItem.item)) {
|
|
239
|
+
hits.push({
|
|
240
|
+
target: gridItem.item,
|
|
241
|
+
point: hit.point,
|
|
242
|
+
normal: hit.normal,
|
|
243
|
+
distance: hit.distance
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
hits.sort((a, b) => a.distance - b.distance);
|
|
249
|
+
return hits;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* @zh 射线检测(仅返回第一个命中)
|
|
253
|
+
* @en Raycast (return first hit only)
|
|
254
|
+
*/
|
|
255
|
+
raycastFirst(origin, direction, maxDistance, filter) {
|
|
256
|
+
const hits = this.raycast(origin, direction, maxDistance, filter);
|
|
257
|
+
return hits.length > 0 ? hits[0] : null;
|
|
258
|
+
}
|
|
259
|
+
// =========================================================================
|
|
260
|
+
// 私有方法 | Private Methods
|
|
261
|
+
// =========================================================================
|
|
262
|
+
_getCellKey(position) {
|
|
263
|
+
const cellX = Math.floor(position.x / this._cellSize);
|
|
264
|
+
const cellY = Math.floor(position.y / this._cellSize);
|
|
265
|
+
return `${cellX},${cellY}`;
|
|
266
|
+
}
|
|
267
|
+
_getCellCoords(position) {
|
|
268
|
+
return {
|
|
269
|
+
x: Math.floor(position.x / this._cellSize),
|
|
270
|
+
y: Math.floor(position.y / this._cellSize)
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
_addToCell(cellKey, gridItem) {
|
|
274
|
+
let cell = this._cells.get(cellKey);
|
|
275
|
+
if (!cell) {
|
|
276
|
+
cell = /* @__PURE__ */ new Set();
|
|
277
|
+
this._cells.set(cellKey, cell);
|
|
278
|
+
}
|
|
279
|
+
cell.add(gridItem);
|
|
280
|
+
}
|
|
281
|
+
_removeFromCell(cellKey, gridItem) {
|
|
282
|
+
const cell = this._cells.get(cellKey);
|
|
283
|
+
if (cell) {
|
|
284
|
+
cell.delete(gridItem);
|
|
285
|
+
if (cell.size === 0) {
|
|
286
|
+
this._cells.delete(cellKey);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
_forEachInBounds(bounds, callback) {
|
|
291
|
+
const minCell = this._getCellCoords({
|
|
292
|
+
x: bounds.minX,
|
|
293
|
+
y: bounds.minY
|
|
294
|
+
});
|
|
295
|
+
const maxCell = this._getCellCoords({
|
|
296
|
+
x: bounds.maxX,
|
|
297
|
+
y: bounds.maxY
|
|
298
|
+
});
|
|
299
|
+
for (let x = minCell.x; x <= maxCell.x; x++) {
|
|
300
|
+
for (let y = minCell.y; y <= maxCell.y; y++) {
|
|
301
|
+
const cell = this._cells.get(`${x},${y}`);
|
|
302
|
+
if (cell) {
|
|
303
|
+
for (const gridItem of cell) {
|
|
304
|
+
callback(gridItem);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
_getRayBounds(origin, direction, maxDistance) {
|
|
311
|
+
const endX = origin.x + direction.x * maxDistance;
|
|
312
|
+
const endY = origin.y + direction.y * maxDistance;
|
|
313
|
+
return {
|
|
314
|
+
minX: Math.min(origin.x, endX),
|
|
315
|
+
minY: Math.min(origin.y, endY),
|
|
316
|
+
maxX: Math.max(origin.x, endX),
|
|
317
|
+
maxY: Math.max(origin.y, endY)
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
_rayIntersectsPoint(origin, direction, point, _maxDistance, hitRadius = 0.5) {
|
|
321
|
+
const toPoint = {
|
|
322
|
+
x: point.x - origin.x,
|
|
323
|
+
y: point.y - origin.y
|
|
324
|
+
};
|
|
325
|
+
const projection = toPoint.x * direction.x + toPoint.y * direction.y;
|
|
326
|
+
if (projection < 0) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
const closestX = origin.x + direction.x * projection;
|
|
330
|
+
const closestY = origin.y + direction.y * projection;
|
|
331
|
+
const distToLine = Math.sqrt((point.x - closestX) * (point.x - closestX) + (point.y - closestY) * (point.y - closestY));
|
|
332
|
+
if (distToLine <= hitRadius) {
|
|
333
|
+
const hitDist = projection - Math.sqrt(hitRadius * hitRadius - distToLine * distToLine);
|
|
334
|
+
if (hitDist >= 0) {
|
|
335
|
+
const hitPoint = {
|
|
336
|
+
x: origin.x + direction.x * hitDist,
|
|
337
|
+
y: origin.y + direction.y * hitDist
|
|
338
|
+
};
|
|
339
|
+
const normal = {
|
|
340
|
+
x: hitPoint.x - point.x,
|
|
341
|
+
y: hitPoint.y - point.y
|
|
342
|
+
};
|
|
343
|
+
const normalLen = Math.sqrt(normal.x * normal.x + normal.y * normal.y);
|
|
344
|
+
if (normalLen > 0) {
|
|
345
|
+
normal.x /= normalLen;
|
|
346
|
+
normal.y /= normalLen;
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
point: hitPoint,
|
|
350
|
+
normal,
|
|
351
|
+
distance: hitDist
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
__name(_GridSpatialIndex, "GridSpatialIndex");
|
|
359
|
+
var GridSpatialIndex = _GridSpatialIndex;
|
|
360
|
+
function createGridSpatialIndex(cellSize = 100) {
|
|
361
|
+
return new GridSpatialIndex({
|
|
362
|
+
cellSize
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
__name(createGridSpatialIndex, "createGridSpatialIndex");
|
|
366
|
+
|
|
367
|
+
// src/aoi/GridAOI.ts
|
|
368
|
+
var _GridAOI = class _GridAOI {
|
|
369
|
+
constructor(config) {
|
|
370
|
+
__publicField(this, "_cellSize");
|
|
371
|
+
__publicField(this, "_cells", /* @__PURE__ */ new Map());
|
|
372
|
+
__publicField(this, "_observers", /* @__PURE__ */ new Map());
|
|
373
|
+
__publicField(this, "_globalListeners", /* @__PURE__ */ new Set());
|
|
374
|
+
this._cellSize = config.cellSize;
|
|
375
|
+
}
|
|
376
|
+
// =========================================================================
|
|
377
|
+
// IAOIManager 实现 | IAOIManager Implementation
|
|
378
|
+
// =========================================================================
|
|
379
|
+
get count() {
|
|
380
|
+
return this._observers.size;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* @zh 添加观察者
|
|
384
|
+
* @en Add observer
|
|
385
|
+
*/
|
|
386
|
+
addObserver(entity, position, config) {
|
|
387
|
+
if (this._observers.has(entity)) {
|
|
388
|
+
this.updatePosition(entity, position);
|
|
389
|
+
this.updateViewRange(entity, config.viewRange);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
const cellKey = this._getCellKey(position);
|
|
393
|
+
const data = {
|
|
394
|
+
entity,
|
|
395
|
+
position: {
|
|
396
|
+
x: position.x,
|
|
397
|
+
y: position.y
|
|
398
|
+
},
|
|
399
|
+
viewRange: config.viewRange,
|
|
400
|
+
viewRangeSq: config.viewRange * config.viewRange,
|
|
401
|
+
observable: config.observable !== false,
|
|
402
|
+
cellKey,
|
|
403
|
+
visibleEntities: /* @__PURE__ */ new Set(),
|
|
404
|
+
listeners: /* @__PURE__ */ new Set()
|
|
405
|
+
};
|
|
406
|
+
this._observers.set(entity, data);
|
|
407
|
+
this._addToCell(cellKey, data);
|
|
408
|
+
this._updateVisibility(data);
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* @zh 移除观察者
|
|
412
|
+
* @en Remove observer
|
|
413
|
+
*/
|
|
414
|
+
removeObserver(entity) {
|
|
415
|
+
const data = this._observers.get(entity);
|
|
416
|
+
if (!data) {
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
if (data.observable) {
|
|
420
|
+
for (const [, otherData] of this._observers) {
|
|
421
|
+
if (otherData !== data && otherData.visibleEntities.has(entity)) {
|
|
422
|
+
otherData.visibleEntities.delete(entity);
|
|
423
|
+
this._emitEvent({
|
|
424
|
+
type: "exit",
|
|
425
|
+
observer: otherData.entity,
|
|
426
|
+
target: entity,
|
|
427
|
+
position: data.position
|
|
428
|
+
}, otherData);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
for (const visible of data.visibleEntities) {
|
|
433
|
+
const visibleData = this._observers.get(visible);
|
|
434
|
+
if (visibleData) {
|
|
435
|
+
this._emitEvent({
|
|
436
|
+
type: "exit",
|
|
437
|
+
observer: entity,
|
|
438
|
+
target: visible,
|
|
439
|
+
position: visibleData.position
|
|
440
|
+
}, data);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
this._removeFromCell(data.cellKey, data);
|
|
444
|
+
this._observers.delete(entity);
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* @zh 更新观察者位置
|
|
449
|
+
* @en Update observer position
|
|
450
|
+
*/
|
|
451
|
+
updatePosition(entity, newPosition) {
|
|
452
|
+
const data = this._observers.get(entity);
|
|
453
|
+
if (!data) {
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
const newCellKey = this._getCellKey(newPosition);
|
|
457
|
+
if (newCellKey !== data.cellKey) {
|
|
458
|
+
this._removeFromCell(data.cellKey, data);
|
|
459
|
+
data.cellKey = newCellKey;
|
|
460
|
+
this._addToCell(newCellKey, data);
|
|
461
|
+
}
|
|
462
|
+
data.position = {
|
|
463
|
+
x: newPosition.x,
|
|
464
|
+
y: newPosition.y
|
|
465
|
+
};
|
|
466
|
+
this._updateVisibility(data);
|
|
467
|
+
if (data.observable) {
|
|
468
|
+
this._updateObserversOfEntity(data);
|
|
469
|
+
}
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* @zh 更新观察者视野范围
|
|
474
|
+
* @en Update observer view range
|
|
475
|
+
*/
|
|
476
|
+
updateViewRange(entity, newRange) {
|
|
477
|
+
const data = this._observers.get(entity);
|
|
478
|
+
if (!data) {
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
data.viewRange = newRange;
|
|
482
|
+
data.viewRangeSq = newRange * newRange;
|
|
483
|
+
this._updateVisibility(data);
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* @zh 获取实体视野内的所有对象
|
|
488
|
+
* @en Get all objects within entity's view
|
|
489
|
+
*/
|
|
490
|
+
getEntitiesInView(entity) {
|
|
491
|
+
const data = this._observers.get(entity);
|
|
492
|
+
if (!data) {
|
|
493
|
+
return [];
|
|
494
|
+
}
|
|
495
|
+
return Array.from(data.visibleEntities);
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* @zh 获取能看到指定实体的所有观察者
|
|
499
|
+
* @en Get all observers who can see the specified entity
|
|
500
|
+
*/
|
|
501
|
+
getObserversOf(entity) {
|
|
502
|
+
const data = this._observers.get(entity);
|
|
503
|
+
if (!data || !data.observable) {
|
|
504
|
+
return [];
|
|
505
|
+
}
|
|
506
|
+
const observers = [];
|
|
507
|
+
for (const [, otherData] of this._observers) {
|
|
508
|
+
if (otherData !== data && otherData.visibleEntities.has(entity)) {
|
|
509
|
+
observers.push(otherData.entity);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return observers;
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* @zh 检查观察者是否能看到目标
|
|
516
|
+
* @en Check if observer can see target
|
|
517
|
+
*/
|
|
518
|
+
canSee(observer, target) {
|
|
519
|
+
const data = this._observers.get(observer);
|
|
520
|
+
if (!data) {
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
return data.visibleEntities.has(target);
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* @zh 添加全局事件监听器
|
|
527
|
+
* @en Add global event listener
|
|
528
|
+
*/
|
|
529
|
+
addListener(listener) {
|
|
530
|
+
this._globalListeners.add(listener);
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* @zh 移除全局事件监听器
|
|
534
|
+
* @en Remove global event listener
|
|
535
|
+
*/
|
|
536
|
+
removeListener(listener) {
|
|
537
|
+
this._globalListeners.delete(listener);
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* @zh 为特定观察者添加事件监听器
|
|
541
|
+
* @en Add event listener for specific observer
|
|
542
|
+
*/
|
|
543
|
+
addEntityListener(entity, listener) {
|
|
544
|
+
const data = this._observers.get(entity);
|
|
545
|
+
if (data) {
|
|
546
|
+
data.listeners.add(listener);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* @zh 移除特定观察者的事件监听器
|
|
551
|
+
* @en Remove event listener for specific observer
|
|
552
|
+
*/
|
|
553
|
+
removeEntityListener(entity, listener) {
|
|
554
|
+
const data = this._observers.get(entity);
|
|
555
|
+
if (data) {
|
|
556
|
+
data.listeners.delete(listener);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* @zh 清空所有观察者
|
|
561
|
+
* @en Clear all observers
|
|
562
|
+
*/
|
|
563
|
+
clear() {
|
|
564
|
+
this._cells.clear();
|
|
565
|
+
this._observers.clear();
|
|
566
|
+
}
|
|
567
|
+
// =========================================================================
|
|
568
|
+
// 私有方法 | Private Methods
|
|
569
|
+
// =========================================================================
|
|
570
|
+
_getCellKey(position) {
|
|
571
|
+
const cellX = Math.floor(position.x / this._cellSize);
|
|
572
|
+
const cellY = Math.floor(position.y / this._cellSize);
|
|
573
|
+
return `${cellX},${cellY}`;
|
|
574
|
+
}
|
|
575
|
+
_getCellCoords(position) {
|
|
576
|
+
return {
|
|
577
|
+
x: Math.floor(position.x / this._cellSize),
|
|
578
|
+
y: Math.floor(position.y / this._cellSize)
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
_addToCell(cellKey, data) {
|
|
582
|
+
let cell = this._cells.get(cellKey);
|
|
583
|
+
if (!cell) {
|
|
584
|
+
cell = /* @__PURE__ */ new Set();
|
|
585
|
+
this._cells.set(cellKey, cell);
|
|
586
|
+
}
|
|
587
|
+
cell.add(data);
|
|
588
|
+
}
|
|
589
|
+
_removeFromCell(cellKey, data) {
|
|
590
|
+
const cell = this._cells.get(cellKey);
|
|
591
|
+
if (cell) {
|
|
592
|
+
cell.delete(data);
|
|
593
|
+
if (cell.size === 0) {
|
|
594
|
+
this._cells.delete(cellKey);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* @zh 更新观察者的可见实体列表
|
|
600
|
+
* @en Update observer's visible entities list
|
|
601
|
+
*/
|
|
602
|
+
_updateVisibility(data) {
|
|
603
|
+
const newVisible = /* @__PURE__ */ new Set();
|
|
604
|
+
const cellRadius = Math.ceil(data.viewRange / this._cellSize);
|
|
605
|
+
const centerCell = this._getCellCoords(data.position);
|
|
606
|
+
for (let dx = -cellRadius; dx <= cellRadius; dx++) {
|
|
607
|
+
for (let dy = -cellRadius; dy <= cellRadius; dy++) {
|
|
608
|
+
const cellKey = `${centerCell.x + dx},${centerCell.y + dy}`;
|
|
609
|
+
const cell = this._cells.get(cellKey);
|
|
610
|
+
if (!cell) continue;
|
|
611
|
+
for (const otherData of cell) {
|
|
612
|
+
if (otherData === data) continue;
|
|
613
|
+
if (!otherData.observable) continue;
|
|
614
|
+
const distSq = distanceSquared(data.position, otherData.position);
|
|
615
|
+
if (distSq <= data.viewRangeSq) {
|
|
616
|
+
newVisible.add(otherData.entity);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
for (const entity of newVisible) {
|
|
622
|
+
if (!data.visibleEntities.has(entity)) {
|
|
623
|
+
const targetData = this._observers.get(entity);
|
|
624
|
+
if (targetData) {
|
|
625
|
+
this._emitEvent({
|
|
626
|
+
type: "enter",
|
|
627
|
+
observer: data.entity,
|
|
628
|
+
target: entity,
|
|
629
|
+
position: targetData.position
|
|
630
|
+
}, data);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
for (const entity of data.visibleEntities) {
|
|
635
|
+
if (!newVisible.has(entity)) {
|
|
636
|
+
const targetData = this._observers.get(entity);
|
|
637
|
+
const position = targetData?.position ?? {
|
|
638
|
+
x: 0,
|
|
639
|
+
y: 0
|
|
640
|
+
};
|
|
641
|
+
this._emitEvent({
|
|
642
|
+
type: "exit",
|
|
643
|
+
observer: data.entity,
|
|
644
|
+
target: entity,
|
|
645
|
+
position
|
|
646
|
+
}, data);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
data.visibleEntities = newVisible;
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* @zh 更新其他观察者对于某个实体的可见性
|
|
653
|
+
* @en Update other observers' visibility of an entity
|
|
654
|
+
*/
|
|
655
|
+
_updateObserversOfEntity(movedData) {
|
|
656
|
+
const cellRadius = Math.ceil(this._getMaxViewRange() / this._cellSize) + 1;
|
|
657
|
+
const centerCell = this._getCellCoords(movedData.position);
|
|
658
|
+
for (let dx = -cellRadius; dx <= cellRadius; dx++) {
|
|
659
|
+
for (let dy = -cellRadius; dy <= cellRadius; dy++) {
|
|
660
|
+
const cellKey = `${centerCell.x + dx},${centerCell.y + dy}`;
|
|
661
|
+
const cell = this._cells.get(cellKey);
|
|
662
|
+
if (!cell) continue;
|
|
663
|
+
for (const otherData of cell) {
|
|
664
|
+
if (otherData === movedData) continue;
|
|
665
|
+
const distSq = distanceSquared(otherData.position, movedData.position);
|
|
666
|
+
const wasVisible = otherData.visibleEntities.has(movedData.entity);
|
|
667
|
+
const isVisible = distSq <= otherData.viewRangeSq;
|
|
668
|
+
if (isVisible && !wasVisible) {
|
|
669
|
+
otherData.visibleEntities.add(movedData.entity);
|
|
670
|
+
this._emitEvent({
|
|
671
|
+
type: "enter",
|
|
672
|
+
observer: otherData.entity,
|
|
673
|
+
target: movedData.entity,
|
|
674
|
+
position: movedData.position
|
|
675
|
+
}, otherData);
|
|
676
|
+
} else if (!isVisible && wasVisible) {
|
|
677
|
+
otherData.visibleEntities.delete(movedData.entity);
|
|
678
|
+
this._emitEvent({
|
|
679
|
+
type: "exit",
|
|
680
|
+
observer: otherData.entity,
|
|
681
|
+
target: movedData.entity,
|
|
682
|
+
position: movedData.position
|
|
683
|
+
}, otherData);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* @zh 获取最大视野范围(用于优化搜索)
|
|
691
|
+
* @en Get maximum view range (for search optimization)
|
|
692
|
+
*/
|
|
693
|
+
_getMaxViewRange() {
|
|
694
|
+
let max = 0;
|
|
695
|
+
for (const [, data] of this._observers) {
|
|
696
|
+
if (data.viewRange > max) {
|
|
697
|
+
max = data.viewRange;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
return max;
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* @zh 发送事件
|
|
704
|
+
* @en Emit event
|
|
705
|
+
*/
|
|
706
|
+
_emitEvent(event, observerData) {
|
|
707
|
+
for (const listener of observerData.listeners) {
|
|
708
|
+
try {
|
|
709
|
+
listener(event);
|
|
710
|
+
} catch (e) {
|
|
711
|
+
console.error("AOI entity listener error:", e);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
for (const listener of this._globalListeners) {
|
|
715
|
+
try {
|
|
716
|
+
listener(event);
|
|
717
|
+
} catch (e) {
|
|
718
|
+
console.error("AOI global listener error:", e);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
__name(_GridAOI, "GridAOI");
|
|
724
|
+
var GridAOI = _GridAOI;
|
|
725
|
+
function createGridAOI(cellSize = 100) {
|
|
726
|
+
return new GridAOI({
|
|
727
|
+
cellSize
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
__name(createGridAOI, "createGridAOI");
|
|
731
|
+
|
|
732
|
+
// src/tokens.ts
|
|
733
|
+
import { createServiceToken } from "@esengine/ecs-framework";
|
|
734
|
+
var SpatialIndexToken = createServiceToken("spatialIndex");
|
|
735
|
+
var SpatialQueryToken = createServiceToken("spatialQuery");
|
|
736
|
+
var AOIManagerToken = createServiceToken("aoiManager");
|
|
737
|
+
|
|
738
|
+
// src/nodes/SpatialQueryNodes.ts
|
|
739
|
+
var FindInRadiusTemplate = {
|
|
740
|
+
type: "FindInRadius",
|
|
741
|
+
title: "Find In Radius",
|
|
742
|
+
category: "entity",
|
|
743
|
+
description: "Find all objects within radius / \u67E5\u627E\u534A\u5F84\u5185\u7684\u6240\u6709\u5BF9\u8C61",
|
|
744
|
+
keywords: [
|
|
745
|
+
"spatial",
|
|
746
|
+
"find",
|
|
747
|
+
"radius",
|
|
748
|
+
"range",
|
|
749
|
+
"query"
|
|
750
|
+
],
|
|
751
|
+
menuPath: [
|
|
752
|
+
"Spatial",
|
|
753
|
+
"Find In Radius"
|
|
754
|
+
],
|
|
755
|
+
isPure: true,
|
|
756
|
+
inputs: [
|
|
757
|
+
{
|
|
758
|
+
name: "centerX",
|
|
759
|
+
displayName: "Center X",
|
|
760
|
+
type: "float",
|
|
761
|
+
defaultValue: 0
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
name: "centerY",
|
|
765
|
+
displayName: "Center Y",
|
|
766
|
+
type: "float",
|
|
767
|
+
defaultValue: 0
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
name: "radius",
|
|
771
|
+
displayName: "Radius",
|
|
772
|
+
type: "float",
|
|
773
|
+
defaultValue: 100
|
|
774
|
+
}
|
|
775
|
+
],
|
|
776
|
+
outputs: [
|
|
777
|
+
{
|
|
778
|
+
name: "results",
|
|
779
|
+
displayName: "Results",
|
|
780
|
+
type: "array"
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
name: "count",
|
|
784
|
+
displayName: "Count",
|
|
785
|
+
type: "int"
|
|
786
|
+
}
|
|
787
|
+
],
|
|
788
|
+
color: "#4a9eff"
|
|
789
|
+
};
|
|
790
|
+
var _FindInRadiusExecutor = class _FindInRadiusExecutor {
|
|
791
|
+
execute(node, context) {
|
|
792
|
+
const ctx = context;
|
|
793
|
+
const centerX = ctx.evaluateInput(node.id, "centerX", 0);
|
|
794
|
+
const centerY = ctx.evaluateInput(node.id, "centerY", 0);
|
|
795
|
+
const radius = ctx.evaluateInput(node.id, "radius", 100);
|
|
796
|
+
const results = ctx.spatialQuery?.findInRadius({
|
|
797
|
+
x: centerX,
|
|
798
|
+
y: centerY
|
|
799
|
+
}, radius) ?? [];
|
|
800
|
+
return {
|
|
801
|
+
outputs: {
|
|
802
|
+
results,
|
|
803
|
+
count: results.length
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
__name(_FindInRadiusExecutor, "FindInRadiusExecutor");
|
|
809
|
+
var FindInRadiusExecutor = _FindInRadiusExecutor;
|
|
810
|
+
var FindInRectTemplate = {
|
|
811
|
+
type: "FindInRect",
|
|
812
|
+
title: "Find In Rect",
|
|
813
|
+
category: "entity",
|
|
814
|
+
description: "Find all objects within rectangle / \u67E5\u627E\u77E9\u5F62\u533A\u57DF\u5185\u7684\u6240\u6709\u5BF9\u8C61",
|
|
815
|
+
keywords: [
|
|
816
|
+
"spatial",
|
|
817
|
+
"find",
|
|
818
|
+
"rect",
|
|
819
|
+
"rectangle",
|
|
820
|
+
"area",
|
|
821
|
+
"query"
|
|
822
|
+
],
|
|
823
|
+
menuPath: [
|
|
824
|
+
"Spatial",
|
|
825
|
+
"Find In Rect"
|
|
826
|
+
],
|
|
827
|
+
isPure: true,
|
|
828
|
+
inputs: [
|
|
829
|
+
{
|
|
830
|
+
name: "minX",
|
|
831
|
+
displayName: "Min X",
|
|
832
|
+
type: "float",
|
|
833
|
+
defaultValue: 0
|
|
834
|
+
},
|
|
835
|
+
{
|
|
836
|
+
name: "minY",
|
|
837
|
+
displayName: "Min Y",
|
|
838
|
+
type: "float",
|
|
839
|
+
defaultValue: 0
|
|
840
|
+
},
|
|
841
|
+
{
|
|
842
|
+
name: "maxX",
|
|
843
|
+
displayName: "Max X",
|
|
844
|
+
type: "float",
|
|
845
|
+
defaultValue: 100
|
|
846
|
+
},
|
|
847
|
+
{
|
|
848
|
+
name: "maxY",
|
|
849
|
+
displayName: "Max Y",
|
|
850
|
+
type: "float",
|
|
851
|
+
defaultValue: 100
|
|
852
|
+
}
|
|
853
|
+
],
|
|
854
|
+
outputs: [
|
|
855
|
+
{
|
|
856
|
+
name: "results",
|
|
857
|
+
displayName: "Results",
|
|
858
|
+
type: "array"
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
name: "count",
|
|
862
|
+
displayName: "Count",
|
|
863
|
+
type: "int"
|
|
864
|
+
}
|
|
865
|
+
],
|
|
866
|
+
color: "#4a9eff"
|
|
867
|
+
};
|
|
868
|
+
var _FindInRectExecutor = class _FindInRectExecutor {
|
|
869
|
+
execute(node, context) {
|
|
870
|
+
const ctx = context;
|
|
871
|
+
const minX = ctx.evaluateInput(node.id, "minX", 0);
|
|
872
|
+
const minY = ctx.evaluateInput(node.id, "minY", 0);
|
|
873
|
+
const maxX = ctx.evaluateInput(node.id, "maxX", 100);
|
|
874
|
+
const maxY = ctx.evaluateInput(node.id, "maxY", 100);
|
|
875
|
+
const bounds = {
|
|
876
|
+
minX,
|
|
877
|
+
minY,
|
|
878
|
+
maxX,
|
|
879
|
+
maxY
|
|
880
|
+
};
|
|
881
|
+
const results = ctx.spatialQuery?.findInRect(bounds) ?? [];
|
|
882
|
+
return {
|
|
883
|
+
outputs: {
|
|
884
|
+
results,
|
|
885
|
+
count: results.length
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
};
|
|
890
|
+
__name(_FindInRectExecutor, "FindInRectExecutor");
|
|
891
|
+
var FindInRectExecutor = _FindInRectExecutor;
|
|
892
|
+
var FindNearestTemplate = {
|
|
893
|
+
type: "FindNearest",
|
|
894
|
+
title: "Find Nearest",
|
|
895
|
+
category: "entity",
|
|
896
|
+
description: "Find nearest object to a point / \u67E5\u627E\u8DDD\u79BB\u70B9\u6700\u8FD1\u7684\u5BF9\u8C61",
|
|
897
|
+
keywords: [
|
|
898
|
+
"spatial",
|
|
899
|
+
"find",
|
|
900
|
+
"nearest",
|
|
901
|
+
"closest",
|
|
902
|
+
"query"
|
|
903
|
+
],
|
|
904
|
+
menuPath: [
|
|
905
|
+
"Spatial",
|
|
906
|
+
"Find Nearest"
|
|
907
|
+
],
|
|
908
|
+
isPure: true,
|
|
909
|
+
inputs: [
|
|
910
|
+
{
|
|
911
|
+
name: "centerX",
|
|
912
|
+
displayName: "Center X",
|
|
913
|
+
type: "float",
|
|
914
|
+
defaultValue: 0
|
|
915
|
+
},
|
|
916
|
+
{
|
|
917
|
+
name: "centerY",
|
|
918
|
+
displayName: "Center Y",
|
|
919
|
+
type: "float",
|
|
920
|
+
defaultValue: 0
|
|
921
|
+
},
|
|
922
|
+
{
|
|
923
|
+
name: "maxDistance",
|
|
924
|
+
displayName: "Max Distance",
|
|
925
|
+
type: "float",
|
|
926
|
+
defaultValue: 1e3
|
|
927
|
+
}
|
|
928
|
+
],
|
|
929
|
+
outputs: [
|
|
930
|
+
{
|
|
931
|
+
name: "result",
|
|
932
|
+
displayName: "Result",
|
|
933
|
+
type: "any"
|
|
934
|
+
},
|
|
935
|
+
{
|
|
936
|
+
name: "found",
|
|
937
|
+
displayName: "Found",
|
|
938
|
+
type: "bool"
|
|
939
|
+
}
|
|
940
|
+
],
|
|
941
|
+
color: "#4a9eff"
|
|
942
|
+
};
|
|
943
|
+
var _FindNearestExecutor = class _FindNearestExecutor {
|
|
944
|
+
execute(node, context) {
|
|
945
|
+
const ctx = context;
|
|
946
|
+
const centerX = ctx.evaluateInput(node.id, "centerX", 0);
|
|
947
|
+
const centerY = ctx.evaluateInput(node.id, "centerY", 0);
|
|
948
|
+
const maxDistance = ctx.evaluateInput(node.id, "maxDistance", 1e3);
|
|
949
|
+
const result = ctx.spatialQuery?.findNearest({
|
|
950
|
+
x: centerX,
|
|
951
|
+
y: centerY
|
|
952
|
+
}, maxDistance) ?? null;
|
|
953
|
+
return {
|
|
954
|
+
outputs: {
|
|
955
|
+
result,
|
|
956
|
+
found: result !== null
|
|
957
|
+
}
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
};
|
|
961
|
+
__name(_FindNearestExecutor, "FindNearestExecutor");
|
|
962
|
+
var FindNearestExecutor = _FindNearestExecutor;
|
|
963
|
+
var FindKNearestTemplate = {
|
|
964
|
+
type: "FindKNearest",
|
|
965
|
+
title: "Find K Nearest",
|
|
966
|
+
category: "entity",
|
|
967
|
+
description: "Find K nearest objects to a point / \u67E5\u627E\u8DDD\u79BB\u70B9\u6700\u8FD1\u7684 K \u4E2A\u5BF9\u8C61",
|
|
968
|
+
keywords: [
|
|
969
|
+
"spatial",
|
|
970
|
+
"find",
|
|
971
|
+
"nearest",
|
|
972
|
+
"k",
|
|
973
|
+
"query"
|
|
974
|
+
],
|
|
975
|
+
menuPath: [
|
|
976
|
+
"Spatial",
|
|
977
|
+
"Find K Nearest"
|
|
978
|
+
],
|
|
979
|
+
isPure: true,
|
|
980
|
+
inputs: [
|
|
981
|
+
{
|
|
982
|
+
name: "centerX",
|
|
983
|
+
displayName: "Center X",
|
|
984
|
+
type: "float",
|
|
985
|
+
defaultValue: 0
|
|
986
|
+
},
|
|
987
|
+
{
|
|
988
|
+
name: "centerY",
|
|
989
|
+
displayName: "Center Y",
|
|
990
|
+
type: "float",
|
|
991
|
+
defaultValue: 0
|
|
992
|
+
},
|
|
993
|
+
{
|
|
994
|
+
name: "k",
|
|
995
|
+
displayName: "K",
|
|
996
|
+
type: "int",
|
|
997
|
+
defaultValue: 5
|
|
998
|
+
},
|
|
999
|
+
{
|
|
1000
|
+
name: "maxDistance",
|
|
1001
|
+
displayName: "Max Distance",
|
|
1002
|
+
type: "float",
|
|
1003
|
+
defaultValue: 1e3
|
|
1004
|
+
}
|
|
1005
|
+
],
|
|
1006
|
+
outputs: [
|
|
1007
|
+
{
|
|
1008
|
+
name: "results",
|
|
1009
|
+
displayName: "Results",
|
|
1010
|
+
type: "array"
|
|
1011
|
+
},
|
|
1012
|
+
{
|
|
1013
|
+
name: "count",
|
|
1014
|
+
displayName: "Count",
|
|
1015
|
+
type: "int"
|
|
1016
|
+
}
|
|
1017
|
+
],
|
|
1018
|
+
color: "#4a9eff"
|
|
1019
|
+
};
|
|
1020
|
+
var _FindKNearestExecutor = class _FindKNearestExecutor {
|
|
1021
|
+
execute(node, context) {
|
|
1022
|
+
const ctx = context;
|
|
1023
|
+
const centerX = ctx.evaluateInput(node.id, "centerX", 0);
|
|
1024
|
+
const centerY = ctx.evaluateInput(node.id, "centerY", 0);
|
|
1025
|
+
const k = ctx.evaluateInput(node.id, "k", 5);
|
|
1026
|
+
const maxDistance = ctx.evaluateInput(node.id, "maxDistance", 1e3);
|
|
1027
|
+
const results = ctx.spatialQuery?.findKNearest({
|
|
1028
|
+
x: centerX,
|
|
1029
|
+
y: centerY
|
|
1030
|
+
}, k, maxDistance) ?? [];
|
|
1031
|
+
return {
|
|
1032
|
+
outputs: {
|
|
1033
|
+
results,
|
|
1034
|
+
count: results.length
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
};
|
|
1039
|
+
__name(_FindKNearestExecutor, "FindKNearestExecutor");
|
|
1040
|
+
var FindKNearestExecutor = _FindKNearestExecutor;
|
|
1041
|
+
var RaycastTemplate = {
|
|
1042
|
+
type: "Raycast",
|
|
1043
|
+
title: "Raycast",
|
|
1044
|
+
category: "entity",
|
|
1045
|
+
description: "Cast a ray and get all hits / \u53D1\u5C04\u5C04\u7EBF\u5E76\u83B7\u53D6\u6240\u6709\u547D\u4E2D",
|
|
1046
|
+
keywords: [
|
|
1047
|
+
"spatial",
|
|
1048
|
+
"raycast",
|
|
1049
|
+
"ray",
|
|
1050
|
+
"hit",
|
|
1051
|
+
"query"
|
|
1052
|
+
],
|
|
1053
|
+
menuPath: [
|
|
1054
|
+
"Spatial",
|
|
1055
|
+
"Raycast"
|
|
1056
|
+
],
|
|
1057
|
+
isPure: true,
|
|
1058
|
+
inputs: [
|
|
1059
|
+
{
|
|
1060
|
+
name: "originX",
|
|
1061
|
+
displayName: "Origin X",
|
|
1062
|
+
type: "float",
|
|
1063
|
+
defaultValue: 0
|
|
1064
|
+
},
|
|
1065
|
+
{
|
|
1066
|
+
name: "originY",
|
|
1067
|
+
displayName: "Origin Y",
|
|
1068
|
+
type: "float",
|
|
1069
|
+
defaultValue: 0
|
|
1070
|
+
},
|
|
1071
|
+
{
|
|
1072
|
+
name: "directionX",
|
|
1073
|
+
displayName: "Direction X",
|
|
1074
|
+
type: "float",
|
|
1075
|
+
defaultValue: 1
|
|
1076
|
+
},
|
|
1077
|
+
{
|
|
1078
|
+
name: "directionY",
|
|
1079
|
+
displayName: "Direction Y",
|
|
1080
|
+
type: "float",
|
|
1081
|
+
defaultValue: 0
|
|
1082
|
+
},
|
|
1083
|
+
{
|
|
1084
|
+
name: "maxDistance",
|
|
1085
|
+
displayName: "Max Distance",
|
|
1086
|
+
type: "float",
|
|
1087
|
+
defaultValue: 1e3
|
|
1088
|
+
}
|
|
1089
|
+
],
|
|
1090
|
+
outputs: [
|
|
1091
|
+
{
|
|
1092
|
+
name: "hits",
|
|
1093
|
+
displayName: "Hits",
|
|
1094
|
+
type: "array"
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1097
|
+
name: "hitCount",
|
|
1098
|
+
displayName: "Hit Count",
|
|
1099
|
+
type: "int"
|
|
1100
|
+
},
|
|
1101
|
+
{
|
|
1102
|
+
name: "hasHit",
|
|
1103
|
+
displayName: "Has Hit",
|
|
1104
|
+
type: "bool"
|
|
1105
|
+
}
|
|
1106
|
+
],
|
|
1107
|
+
color: "#4a9eff"
|
|
1108
|
+
};
|
|
1109
|
+
var _RaycastExecutor = class _RaycastExecutor {
|
|
1110
|
+
execute(node, context) {
|
|
1111
|
+
const ctx = context;
|
|
1112
|
+
const originX = ctx.evaluateInput(node.id, "originX", 0);
|
|
1113
|
+
const originY = ctx.evaluateInput(node.id, "originY", 0);
|
|
1114
|
+
const directionX = ctx.evaluateInput(node.id, "directionX", 1);
|
|
1115
|
+
const directionY = ctx.evaluateInput(node.id, "directionY", 0);
|
|
1116
|
+
const maxDistance = ctx.evaluateInput(node.id, "maxDistance", 1e3);
|
|
1117
|
+
const hits = ctx.spatialQuery?.raycast({
|
|
1118
|
+
x: originX,
|
|
1119
|
+
y: originY
|
|
1120
|
+
}, {
|
|
1121
|
+
x: directionX,
|
|
1122
|
+
y: directionY
|
|
1123
|
+
}, maxDistance) ?? [];
|
|
1124
|
+
return {
|
|
1125
|
+
outputs: {
|
|
1126
|
+
hits,
|
|
1127
|
+
hitCount: hits.length,
|
|
1128
|
+
hasHit: hits.length > 0
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
};
|
|
1133
|
+
__name(_RaycastExecutor, "RaycastExecutor");
|
|
1134
|
+
var RaycastExecutor = _RaycastExecutor;
|
|
1135
|
+
var RaycastFirstTemplate = {
|
|
1136
|
+
type: "RaycastFirst",
|
|
1137
|
+
title: "Raycast First",
|
|
1138
|
+
category: "entity",
|
|
1139
|
+
description: "Cast a ray and get first hit / \u53D1\u5C04\u5C04\u7EBF\u5E76\u83B7\u53D6\u7B2C\u4E00\u4E2A\u547D\u4E2D",
|
|
1140
|
+
keywords: [
|
|
1141
|
+
"spatial",
|
|
1142
|
+
"raycast",
|
|
1143
|
+
"ray",
|
|
1144
|
+
"first",
|
|
1145
|
+
"hit",
|
|
1146
|
+
"query"
|
|
1147
|
+
],
|
|
1148
|
+
menuPath: [
|
|
1149
|
+
"Spatial",
|
|
1150
|
+
"Raycast First"
|
|
1151
|
+
],
|
|
1152
|
+
isPure: true,
|
|
1153
|
+
inputs: [
|
|
1154
|
+
{
|
|
1155
|
+
name: "originX",
|
|
1156
|
+
displayName: "Origin X",
|
|
1157
|
+
type: "float",
|
|
1158
|
+
defaultValue: 0
|
|
1159
|
+
},
|
|
1160
|
+
{
|
|
1161
|
+
name: "originY",
|
|
1162
|
+
displayName: "Origin Y",
|
|
1163
|
+
type: "float",
|
|
1164
|
+
defaultValue: 0
|
|
1165
|
+
},
|
|
1166
|
+
{
|
|
1167
|
+
name: "directionX",
|
|
1168
|
+
displayName: "Direction X",
|
|
1169
|
+
type: "float",
|
|
1170
|
+
defaultValue: 1
|
|
1171
|
+
},
|
|
1172
|
+
{
|
|
1173
|
+
name: "directionY",
|
|
1174
|
+
displayName: "Direction Y",
|
|
1175
|
+
type: "float",
|
|
1176
|
+
defaultValue: 0
|
|
1177
|
+
},
|
|
1178
|
+
{
|
|
1179
|
+
name: "maxDistance",
|
|
1180
|
+
displayName: "Max Distance",
|
|
1181
|
+
type: "float",
|
|
1182
|
+
defaultValue: 1e3
|
|
1183
|
+
}
|
|
1184
|
+
],
|
|
1185
|
+
outputs: [
|
|
1186
|
+
{
|
|
1187
|
+
name: "hit",
|
|
1188
|
+
displayName: "Hit",
|
|
1189
|
+
type: "object"
|
|
1190
|
+
},
|
|
1191
|
+
{
|
|
1192
|
+
name: "target",
|
|
1193
|
+
displayName: "Target",
|
|
1194
|
+
type: "any"
|
|
1195
|
+
},
|
|
1196
|
+
{
|
|
1197
|
+
name: "hitPointX",
|
|
1198
|
+
displayName: "Hit Point X",
|
|
1199
|
+
type: "float"
|
|
1200
|
+
},
|
|
1201
|
+
{
|
|
1202
|
+
name: "hitPointY",
|
|
1203
|
+
displayName: "Hit Point Y",
|
|
1204
|
+
type: "float"
|
|
1205
|
+
},
|
|
1206
|
+
{
|
|
1207
|
+
name: "distance",
|
|
1208
|
+
displayName: "Distance",
|
|
1209
|
+
type: "float"
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
name: "hasHit",
|
|
1213
|
+
displayName: "Has Hit",
|
|
1214
|
+
type: "bool"
|
|
1215
|
+
}
|
|
1216
|
+
],
|
|
1217
|
+
color: "#4a9eff"
|
|
1218
|
+
};
|
|
1219
|
+
var _RaycastFirstExecutor = class _RaycastFirstExecutor {
|
|
1220
|
+
execute(node, context) {
|
|
1221
|
+
const ctx = context;
|
|
1222
|
+
const originX = ctx.evaluateInput(node.id, "originX", 0);
|
|
1223
|
+
const originY = ctx.evaluateInput(node.id, "originY", 0);
|
|
1224
|
+
const directionX = ctx.evaluateInput(node.id, "directionX", 1);
|
|
1225
|
+
const directionY = ctx.evaluateInput(node.id, "directionY", 0);
|
|
1226
|
+
const maxDistance = ctx.evaluateInput(node.id, "maxDistance", 1e3);
|
|
1227
|
+
const hit = ctx.spatialQuery?.raycastFirst({
|
|
1228
|
+
x: originX,
|
|
1229
|
+
y: originY
|
|
1230
|
+
}, {
|
|
1231
|
+
x: directionX,
|
|
1232
|
+
y: directionY
|
|
1233
|
+
}, maxDistance) ?? null;
|
|
1234
|
+
return {
|
|
1235
|
+
outputs: {
|
|
1236
|
+
hit,
|
|
1237
|
+
target: hit?.target ?? null,
|
|
1238
|
+
hitPointX: hit?.point.x ?? 0,
|
|
1239
|
+
hitPointY: hit?.point.y ?? 0,
|
|
1240
|
+
distance: hit?.distance ?? 0,
|
|
1241
|
+
hasHit: hit !== null
|
|
1242
|
+
}
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
};
|
|
1246
|
+
__name(_RaycastFirstExecutor, "RaycastFirstExecutor");
|
|
1247
|
+
var RaycastFirstExecutor = _RaycastFirstExecutor;
|
|
1248
|
+
var SpatialQueryNodeDefinitions = [
|
|
1249
|
+
{
|
|
1250
|
+
template: FindInRadiusTemplate,
|
|
1251
|
+
executor: new FindInRadiusExecutor()
|
|
1252
|
+
},
|
|
1253
|
+
{
|
|
1254
|
+
template: FindInRectTemplate,
|
|
1255
|
+
executor: new FindInRectExecutor()
|
|
1256
|
+
},
|
|
1257
|
+
{
|
|
1258
|
+
template: FindNearestTemplate,
|
|
1259
|
+
executor: new FindNearestExecutor()
|
|
1260
|
+
},
|
|
1261
|
+
{
|
|
1262
|
+
template: FindKNearestTemplate,
|
|
1263
|
+
executor: new FindKNearestExecutor()
|
|
1264
|
+
},
|
|
1265
|
+
{
|
|
1266
|
+
template: RaycastTemplate,
|
|
1267
|
+
executor: new RaycastExecutor()
|
|
1268
|
+
},
|
|
1269
|
+
{
|
|
1270
|
+
template: RaycastFirstTemplate,
|
|
1271
|
+
executor: new RaycastFirstExecutor()
|
|
1272
|
+
}
|
|
1273
|
+
];
|
|
1274
|
+
|
|
1275
|
+
// src/aoi/AOINodes.ts
|
|
1276
|
+
var GetEntitiesInViewTemplate = {
|
|
1277
|
+
type: "GetEntitiesInView",
|
|
1278
|
+
title: "Get Entities In View",
|
|
1279
|
+
category: "entity",
|
|
1280
|
+
description: "Get all entities within view range / \u83B7\u53D6\u89C6\u91CE\u8303\u56F4\u5185\u7684\u6240\u6709\u5B9E\u4F53",
|
|
1281
|
+
keywords: [
|
|
1282
|
+
"aoi",
|
|
1283
|
+
"view",
|
|
1284
|
+
"entities",
|
|
1285
|
+
"visible"
|
|
1286
|
+
],
|
|
1287
|
+
menuPath: [
|
|
1288
|
+
"AOI",
|
|
1289
|
+
"Get Entities In View"
|
|
1290
|
+
],
|
|
1291
|
+
isPure: true,
|
|
1292
|
+
inputs: [
|
|
1293
|
+
{
|
|
1294
|
+
name: "observer",
|
|
1295
|
+
displayName: "Observer",
|
|
1296
|
+
type: "object"
|
|
1297
|
+
}
|
|
1298
|
+
],
|
|
1299
|
+
outputs: [
|
|
1300
|
+
{
|
|
1301
|
+
name: "entities",
|
|
1302
|
+
displayName: "Entities",
|
|
1303
|
+
type: "array"
|
|
1304
|
+
},
|
|
1305
|
+
{
|
|
1306
|
+
name: "count",
|
|
1307
|
+
displayName: "Count",
|
|
1308
|
+
type: "int"
|
|
1309
|
+
}
|
|
1310
|
+
],
|
|
1311
|
+
color: "#9c27b0"
|
|
1312
|
+
};
|
|
1313
|
+
var _GetEntitiesInViewExecutor = class _GetEntitiesInViewExecutor {
|
|
1314
|
+
execute(node, context) {
|
|
1315
|
+
const ctx = context;
|
|
1316
|
+
const observer = ctx.evaluateInput(node.id, "observer", ctx.entity);
|
|
1317
|
+
const entities = ctx.aoiManager?.getEntitiesInView(observer) ?? [];
|
|
1318
|
+
return {
|
|
1319
|
+
outputs: {
|
|
1320
|
+
entities,
|
|
1321
|
+
count: entities.length
|
|
1322
|
+
}
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
};
|
|
1326
|
+
__name(_GetEntitiesInViewExecutor, "GetEntitiesInViewExecutor");
|
|
1327
|
+
var GetEntitiesInViewExecutor = _GetEntitiesInViewExecutor;
|
|
1328
|
+
var GetObserversOfTemplate = {
|
|
1329
|
+
type: "GetObserversOf",
|
|
1330
|
+
title: "Get Observers Of",
|
|
1331
|
+
category: "entity",
|
|
1332
|
+
description: "Get all observers who can see the entity / \u83B7\u53D6\u80FD\u770B\u5230\u8BE5\u5B9E\u4F53\u7684\u6240\u6709\u89C2\u5BDF\u8005",
|
|
1333
|
+
keywords: [
|
|
1334
|
+
"aoi",
|
|
1335
|
+
"observers",
|
|
1336
|
+
"watchers",
|
|
1337
|
+
"visible"
|
|
1338
|
+
],
|
|
1339
|
+
menuPath: [
|
|
1340
|
+
"AOI",
|
|
1341
|
+
"Get Observers Of"
|
|
1342
|
+
],
|
|
1343
|
+
isPure: true,
|
|
1344
|
+
inputs: [
|
|
1345
|
+
{
|
|
1346
|
+
name: "target",
|
|
1347
|
+
displayName: "Target",
|
|
1348
|
+
type: "object"
|
|
1349
|
+
}
|
|
1350
|
+
],
|
|
1351
|
+
outputs: [
|
|
1352
|
+
{
|
|
1353
|
+
name: "observers",
|
|
1354
|
+
displayName: "Observers",
|
|
1355
|
+
type: "array"
|
|
1356
|
+
},
|
|
1357
|
+
{
|
|
1358
|
+
name: "count",
|
|
1359
|
+
displayName: "Count",
|
|
1360
|
+
type: "int"
|
|
1361
|
+
}
|
|
1362
|
+
],
|
|
1363
|
+
color: "#9c27b0"
|
|
1364
|
+
};
|
|
1365
|
+
var _GetObserversOfExecutor = class _GetObserversOfExecutor {
|
|
1366
|
+
execute(node, context) {
|
|
1367
|
+
const ctx = context;
|
|
1368
|
+
const target = ctx.evaluateInput(node.id, "target", ctx.entity);
|
|
1369
|
+
const observers = ctx.aoiManager?.getObserversOf(target) ?? [];
|
|
1370
|
+
return {
|
|
1371
|
+
outputs: {
|
|
1372
|
+
observers,
|
|
1373
|
+
count: observers.length
|
|
1374
|
+
}
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
};
|
|
1378
|
+
__name(_GetObserversOfExecutor, "GetObserversOfExecutor");
|
|
1379
|
+
var GetObserversOfExecutor = _GetObserversOfExecutor;
|
|
1380
|
+
var CanSeeTemplate = {
|
|
1381
|
+
type: "CanSee",
|
|
1382
|
+
title: "Can See",
|
|
1383
|
+
category: "entity",
|
|
1384
|
+
description: "Check if observer can see target / \u68C0\u67E5\u89C2\u5BDF\u8005\u662F\u5426\u80FD\u770B\u5230\u76EE\u6807",
|
|
1385
|
+
keywords: [
|
|
1386
|
+
"aoi",
|
|
1387
|
+
"visibility",
|
|
1388
|
+
"can",
|
|
1389
|
+
"see",
|
|
1390
|
+
"check"
|
|
1391
|
+
],
|
|
1392
|
+
menuPath: [
|
|
1393
|
+
"AOI",
|
|
1394
|
+
"Can See"
|
|
1395
|
+
],
|
|
1396
|
+
isPure: true,
|
|
1397
|
+
inputs: [
|
|
1398
|
+
{
|
|
1399
|
+
name: "observer",
|
|
1400
|
+
displayName: "Observer",
|
|
1401
|
+
type: "object"
|
|
1402
|
+
},
|
|
1403
|
+
{
|
|
1404
|
+
name: "target",
|
|
1405
|
+
displayName: "Target",
|
|
1406
|
+
type: "object"
|
|
1407
|
+
}
|
|
1408
|
+
],
|
|
1409
|
+
outputs: [
|
|
1410
|
+
{
|
|
1411
|
+
name: "canSee",
|
|
1412
|
+
displayName: "Can See",
|
|
1413
|
+
type: "bool"
|
|
1414
|
+
}
|
|
1415
|
+
],
|
|
1416
|
+
color: "#9c27b0"
|
|
1417
|
+
};
|
|
1418
|
+
var _CanSeeExecutor = class _CanSeeExecutor {
|
|
1419
|
+
execute(node, context) {
|
|
1420
|
+
const ctx = context;
|
|
1421
|
+
const observer = ctx.evaluateInput(node.id, "observer", ctx.entity);
|
|
1422
|
+
const target = ctx.evaluateInput(node.id, "target", null);
|
|
1423
|
+
const canSee = ctx.aoiManager?.canSee(observer, target) ?? false;
|
|
1424
|
+
return {
|
|
1425
|
+
outputs: {
|
|
1426
|
+
canSee
|
|
1427
|
+
}
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
};
|
|
1431
|
+
__name(_CanSeeExecutor, "CanSeeExecutor");
|
|
1432
|
+
var CanSeeExecutor = _CanSeeExecutor;
|
|
1433
|
+
var OnEntityEnterViewTemplate = {
|
|
1434
|
+
type: "EventEntityEnterView",
|
|
1435
|
+
title: "On Entity Enter View",
|
|
1436
|
+
category: "event",
|
|
1437
|
+
description: "Triggered when an entity enters view / \u5F53\u5B9E\u4F53\u8FDB\u5165\u89C6\u91CE\u65F6\u89E6\u53D1",
|
|
1438
|
+
keywords: [
|
|
1439
|
+
"aoi",
|
|
1440
|
+
"event",
|
|
1441
|
+
"enter",
|
|
1442
|
+
"view",
|
|
1443
|
+
"visible"
|
|
1444
|
+
],
|
|
1445
|
+
menuPath: [
|
|
1446
|
+
"AOI",
|
|
1447
|
+
"Events",
|
|
1448
|
+
"On Entity Enter View"
|
|
1449
|
+
],
|
|
1450
|
+
color: "#e91e63",
|
|
1451
|
+
inputs: [],
|
|
1452
|
+
outputs: [
|
|
1453
|
+
{
|
|
1454
|
+
name: "exec",
|
|
1455
|
+
displayName: "",
|
|
1456
|
+
type: "exec"
|
|
1457
|
+
},
|
|
1458
|
+
{
|
|
1459
|
+
name: "entity",
|
|
1460
|
+
displayName: "Entity",
|
|
1461
|
+
type: "object"
|
|
1462
|
+
},
|
|
1463
|
+
{
|
|
1464
|
+
name: "positionX",
|
|
1465
|
+
displayName: "Position X",
|
|
1466
|
+
type: "float"
|
|
1467
|
+
},
|
|
1468
|
+
{
|
|
1469
|
+
name: "positionY",
|
|
1470
|
+
displayName: "Position Y",
|
|
1471
|
+
type: "float"
|
|
1472
|
+
}
|
|
1473
|
+
]
|
|
1474
|
+
};
|
|
1475
|
+
var _OnEntityEnterViewExecutor = class _OnEntityEnterViewExecutor {
|
|
1476
|
+
execute(_node, _context) {
|
|
1477
|
+
return {
|
|
1478
|
+
nextExec: "exec"
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1482
|
+
__name(_OnEntityEnterViewExecutor, "OnEntityEnterViewExecutor");
|
|
1483
|
+
var OnEntityEnterViewExecutor = _OnEntityEnterViewExecutor;
|
|
1484
|
+
var OnEntityExitViewTemplate = {
|
|
1485
|
+
type: "EventEntityExitView",
|
|
1486
|
+
title: "On Entity Exit View",
|
|
1487
|
+
category: "event",
|
|
1488
|
+
description: "Triggered when an entity exits view / \u5F53\u5B9E\u4F53\u79BB\u5F00\u89C6\u91CE\u65F6\u89E6\u53D1",
|
|
1489
|
+
keywords: [
|
|
1490
|
+
"aoi",
|
|
1491
|
+
"event",
|
|
1492
|
+
"exit",
|
|
1493
|
+
"view",
|
|
1494
|
+
"invisible"
|
|
1495
|
+
],
|
|
1496
|
+
menuPath: [
|
|
1497
|
+
"AOI",
|
|
1498
|
+
"Events",
|
|
1499
|
+
"On Entity Exit View"
|
|
1500
|
+
],
|
|
1501
|
+
color: "#e91e63",
|
|
1502
|
+
inputs: [],
|
|
1503
|
+
outputs: [
|
|
1504
|
+
{
|
|
1505
|
+
name: "exec",
|
|
1506
|
+
displayName: "",
|
|
1507
|
+
type: "exec"
|
|
1508
|
+
},
|
|
1509
|
+
{
|
|
1510
|
+
name: "entity",
|
|
1511
|
+
displayName: "Entity",
|
|
1512
|
+
type: "object"
|
|
1513
|
+
},
|
|
1514
|
+
{
|
|
1515
|
+
name: "positionX",
|
|
1516
|
+
displayName: "Position X",
|
|
1517
|
+
type: "float"
|
|
1518
|
+
},
|
|
1519
|
+
{
|
|
1520
|
+
name: "positionY",
|
|
1521
|
+
displayName: "Position Y",
|
|
1522
|
+
type: "float"
|
|
1523
|
+
}
|
|
1524
|
+
]
|
|
1525
|
+
};
|
|
1526
|
+
var _OnEntityExitViewExecutor = class _OnEntityExitViewExecutor {
|
|
1527
|
+
execute(_node, _context) {
|
|
1528
|
+
return {
|
|
1529
|
+
nextExec: "exec"
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
};
|
|
1533
|
+
__name(_OnEntityExitViewExecutor, "OnEntityExitViewExecutor");
|
|
1534
|
+
var OnEntityExitViewExecutor = _OnEntityExitViewExecutor;
|
|
1535
|
+
var AOINodeDefinitions = {
|
|
1536
|
+
templates: [
|
|
1537
|
+
GetEntitiesInViewTemplate,
|
|
1538
|
+
GetObserversOfTemplate,
|
|
1539
|
+
CanSeeTemplate,
|
|
1540
|
+
OnEntityEnterViewTemplate,
|
|
1541
|
+
OnEntityExitViewTemplate
|
|
1542
|
+
],
|
|
1543
|
+
executors: /* @__PURE__ */ new Map([
|
|
1544
|
+
[
|
|
1545
|
+
"GetEntitiesInView",
|
|
1546
|
+
new GetEntitiesInViewExecutor()
|
|
1547
|
+
],
|
|
1548
|
+
[
|
|
1549
|
+
"GetObserversOf",
|
|
1550
|
+
new GetObserversOfExecutor()
|
|
1551
|
+
],
|
|
1552
|
+
[
|
|
1553
|
+
"CanSee",
|
|
1554
|
+
new CanSeeExecutor()
|
|
1555
|
+
],
|
|
1556
|
+
[
|
|
1557
|
+
"EventEntityEnterView",
|
|
1558
|
+
new OnEntityEnterViewExecutor()
|
|
1559
|
+
],
|
|
1560
|
+
[
|
|
1561
|
+
"EventEntityExitView",
|
|
1562
|
+
new OnEntityExitViewExecutor()
|
|
1563
|
+
]
|
|
1564
|
+
])
|
|
1565
|
+
};
|
|
1566
|
+
export {
|
|
1567
|
+
AOIManagerToken,
|
|
1568
|
+
AOINodeDefinitions,
|
|
1569
|
+
CanSeeExecutor,
|
|
1570
|
+
CanSeeTemplate,
|
|
1571
|
+
FindInRadiusExecutor,
|
|
1572
|
+
FindInRadiusTemplate,
|
|
1573
|
+
FindInRectExecutor,
|
|
1574
|
+
FindInRectTemplate,
|
|
1575
|
+
FindKNearestExecutor,
|
|
1576
|
+
FindKNearestTemplate,
|
|
1577
|
+
FindNearestExecutor,
|
|
1578
|
+
FindNearestTemplate,
|
|
1579
|
+
GetEntitiesInViewExecutor,
|
|
1580
|
+
GetEntitiesInViewTemplate,
|
|
1581
|
+
GetObserversOfExecutor,
|
|
1582
|
+
GetObserversOfTemplate,
|
|
1583
|
+
GridAOI,
|
|
1584
|
+
GridSpatialIndex,
|
|
1585
|
+
OnEntityEnterViewExecutor,
|
|
1586
|
+
OnEntityEnterViewTemplate,
|
|
1587
|
+
OnEntityExitViewExecutor,
|
|
1588
|
+
OnEntityExitViewTemplate,
|
|
1589
|
+
RaycastExecutor,
|
|
1590
|
+
RaycastFirstExecutor,
|
|
1591
|
+
RaycastFirstTemplate,
|
|
1592
|
+
RaycastTemplate,
|
|
1593
|
+
SpatialIndexToken,
|
|
1594
|
+
SpatialQueryNodeDefinitions,
|
|
1595
|
+
SpatialQueryToken,
|
|
1596
|
+
boundsIntersect,
|
|
1597
|
+
boundsIntersectsCircle,
|
|
1598
|
+
createBounds,
|
|
1599
|
+
createBoundsFromCenter,
|
|
1600
|
+
createBoundsFromCircle,
|
|
1601
|
+
createGridAOI,
|
|
1602
|
+
createGridSpatialIndex,
|
|
1603
|
+
distance,
|
|
1604
|
+
distanceSquared,
|
|
1605
|
+
isPointInBounds
|
|
1606
|
+
};
|
|
1607
|
+
//# sourceMappingURL=index.js.map
|