@combos-fun/engine 0.0.53 → 0.1.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/agent-skill.md CHANGED
@@ -8,7 +8,7 @@ Read when starting an engine task or resolving an engine API/lifecycle question.
8
8
 
9
9
  ## Microkernel model
10
10
 
11
- - Core: `Game`, `Scene`, `GameObject`, `Component`, `System`, `Transform`, `resource`, `decorators`; rendering, physics, and audio come from plugins.
11
+ - Core: `Game`, `Scene`, `GameObject`, `Component`, `System`, `resource`, `decorators`. Hierarchy is `GameObject.parent` / `children` / `addChild`. 2D pose is `Transform2D` in `@combos-fun/plugin-renderer`; 3D pose is `Transform3D` in `@combos-fun/plugin-renderer-3d`.
12
12
  - Register an instance with `await game.addSystem(new XxxSystem(...))`; `Game({ systems })` awaits each system's `init`. Avoid `addSystem(XxxSystem, [params])`: runtime passes the tuple as one constructor argument.
13
13
  - Observe components with `@decorators.componentObserver({ Name: ['prop'] })`, then drain `componentObserver.clear()` in `update()`.
14
14
  - Frame order: all `Component.update` → all `Component.lateUpdate` → all `System.update` → all `System.lateUpdate`. `start()` runs on the first update tick.
@@ -12,7 +12,6 @@
12
12
  "GameObject",
13
13
  "Component",
14
14
  "System",
15
- "Transform",
16
15
  "resource",
17
16
  "decorators",
18
17
  "OBSERVER_TYPE",
@@ -2,8 +2,6 @@
2
2
 
3
3
  var EventEmitter = require('eventemitter3');
4
4
  var lodashEs = require('lodash-es');
5
- var tslib = require('tslib');
6
- var inspectorDecorator = require('@combos-fun/inspector-decorator');
7
5
  var resourceLoader$1 = require('resource-loader');
8
6
 
9
7
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -16,10 +14,10 @@ var EventEmitter__default = /*#__PURE__*/_interopDefault(EventEmitter);
16
14
  * @returns component' name
17
15
  * @example
18
16
  * ```typescript
19
- * import { Transform } from '@combos-fun/engine'
17
+ * import { Component } from '@combos-fun/engine'
20
18
  *
21
- * assert(getComponentName(Transform) === 'Transform')
22
- * assert(getComponentName(new Transform()) === 'Transform')
19
+ * assert(getComponentName(MyComponent) === 'MyComponent')
20
+ * assert(getComponentName(new MyComponent()) === 'MyComponent')
23
21
  * ```
24
22
  */
25
23
  function getComponentName(component) {
@@ -114,14 +112,38 @@ function removeObjectCache(component) {
114
112
  * @param {pureObserverProp} param0.prop - pure observer prop
115
113
  * @param {ObserverType} param0.type - observer type
116
114
  */
117
- function addObserver({ systemName, componentName, component, prop, type }) {
115
+ function addObserver({ systemName, componentName, component, prop, type, gameObject }) {
118
116
  systemInstance[systemName]?.componentObserver?.add({
119
117
  component,
120
118
  prop,
121
119
  type,
122
120
  componentName,
121
+ gameObject,
123
122
  });
124
123
  }
124
+ const GAME_OBJECT_OBSERVER = 'GameObject';
125
+ /**
126
+ * Push a GameObject field change (parent / inScene) to systems that observe `GameObject`.
127
+ * Hierarchy is not a Component; callers must notify explicitly from addChild / scene.
128
+ */
129
+ function notifyGameObjectProp(gameObject, propName, type = exports.OBSERVER_TYPE.CHANGE) {
130
+ if (!gameObject)
131
+ return;
132
+ const prop = { prop: [propName], deep: false };
133
+ for (const systemName in observerInfos) {
134
+ const info = observerInfos[systemName]?.[GAME_OBJECT_OBSERVER];
135
+ if (!info?.some(p => p.prop[0] === propName))
136
+ continue;
137
+ addObserver({
138
+ systemName,
139
+ componentName: GAME_OBJECT_OBSERVER,
140
+ component: undefined,
141
+ gameObject,
142
+ prop,
143
+ type,
144
+ });
145
+ }
146
+ }
125
147
  function pushToQueue({ prop, component, componentName, }) {
126
148
  for (const systemName in observerInfos) {
127
149
  const observerInfo = observerInfos[systemName] || {};
@@ -297,154 +319,6 @@ function setSystemObserver(system, S) {
297
319
  systemInstance[S.systemName] = system;
298
320
  }
299
321
 
300
- /** Basic component for gameObject, See {@link TransformParams} */
301
- class Transform extends Component {
302
- constructor() {
303
- super(...arguments);
304
- this.name = 'Transform';
305
- this._parent = null;
306
- /** Whether this transform in a scene object */
307
- this.inScene = false;
308
- /** Child transform components */
309
- this.children = [];
310
- this.position = { x: 0, y: 0 };
311
- this.size = { width: 0, height: 0 };
312
- this.origin = { x: 0, y: 0 };
313
- this.anchor = { x: 0, y: 0 };
314
- this.scale = { x: 1, y: 1 };
315
- this.skew = { x: 0, y: 0 };
316
- this.rotation = 0;
317
- }
318
- /**
319
- * component's name
320
- * @readonly
321
- */
322
- static { this.componentName = 'Transform'; }
323
- /**
324
- * Init component
325
- * @param params - Transform init data
326
- */
327
- init(params = {}) {
328
- const props = ['position', 'size', 'origin', 'anchor', 'scale', 'skew'];
329
- for (const key of props) {
330
- Object.assign(this[key], params[key]);
331
- }
332
- this.rotation = params.rotation ?? this.rotation;
333
- }
334
- set parent(val) {
335
- if (val) {
336
- val.addChild(this);
337
- }
338
- else if (this.parent) {
339
- this.parent.removeChild(this);
340
- }
341
- }
342
- /**
343
- * Get parent of this component
344
- */
345
- get parent() {
346
- return this._parent;
347
- }
348
- /**
349
- * Add Child Transform
350
- * @remarks
351
- * If `child` is already a child of this component, `child` will removed to the last of children list
352
- * If `child` is already a child of other component, `child` will removed from its parent first
353
- * @param child - child gameObject's transform component
354
- */
355
- addChild(child) {
356
- if (child.parent === this) {
357
- const index = this.children.findIndex(item => item === child);
358
- this.children.splice(index, 1);
359
- }
360
- else if (child.parent) {
361
- child.parent.removeChild(child);
362
- }
363
- child._parent = this;
364
- this.children.push(child);
365
- }
366
- /**
367
- * Remove child transform
368
- * @param child - child gameObject's transform component
369
- */
370
- removeChild(child) {
371
- const index = this.children.findIndex(item => item === child);
372
- if (index > -1) {
373
- this.children.splice(index, 1);
374
- child._parent = null;
375
- }
376
- }
377
- /** Clear all child transform */
378
- clearChildren() {
379
- this.children.length = 0;
380
- }
381
- }
382
- tslib.__decorate([
383
- inspectorDecorator.Field({
384
- type: 'vector2',
385
- step: 1,
386
- group: 'Transform',
387
- label: 'Position',
388
- description: 'Position on the design canvas (x right, y down).',
389
- })
390
- ], Transform.prototype, "position", void 0);
391
- tslib.__decorate([
392
- inspectorDecorator.Field({
393
- type: 'size',
394
- step: 1,
395
- min: 0,
396
- group: 'Transform',
397
- label: 'Size',
398
- description: 'Width and height of the object in design pixels.',
399
- })
400
- ], Transform.prototype, "size", void 0);
401
- tslib.__decorate([
402
- inspectorDecorator.Field({
403
- type: 'vector2',
404
- step: 0.1,
405
- group: 'Transform',
406
- label: 'Origin',
407
- description: 'Local origin used for layout math.',
408
- })
409
- ], Transform.prototype, "origin", void 0);
410
- tslib.__decorate([
411
- inspectorDecorator.Field({
412
- type: 'vector2',
413
- step: 0.1,
414
- group: 'Transform',
415
- label: 'Anchor',
416
- description: 'Anchor point (0–1) used when positioning and scaling.',
417
- })
418
- ], Transform.prototype, "anchor", void 0);
419
- tslib.__decorate([
420
- inspectorDecorator.Field({
421
- type: 'vector2',
422
- step: 0.1,
423
- group: 'Transform',
424
- label: 'Scale',
425
- description: 'Scale multipliers on X and Y (1 = original size).',
426
- })
427
- ], Transform.prototype, "scale", void 0);
428
- tslib.__decorate([
429
- inspectorDecorator.Field({
430
- type: 'vector2',
431
- step: 0.1,
432
- group: 'Transform',
433
- label: 'Skew',
434
- description: 'Skew amounts on X and Y.',
435
- })
436
- ], Transform.prototype, "skew", void 0);
437
- tslib.__decorate([
438
- inspectorDecorator.Field({
439
- type: 'number',
440
- step: 0.1,
441
- group: 'Transform',
442
- label: 'Rotation',
443
- description: 'Rotation in radians.',
444
- editor: 'number-stepper',
445
- })
446
- ], Transform.prototype, "rotation", void 0);
447
-
448
322
  let _id = 0;
449
323
  /** Generate unique id for gameObject */
450
324
  function getId() {
@@ -456,28 +330,23 @@ function getId() {
456
330
  */
457
331
  class GameObject {
458
332
  /**
459
- * Consruct a new gameObject
333
+ * Construct a new gameObject
460
334
  * @param name - the name of this gameObject
461
- * @param obj - optional transform parameters for default Transform component
462
335
  */
463
- constructor(name, obj) {
336
+ constructor(name) {
337
+ this._parent = null;
464
338
  /** A key-value map for components on this gameObject */
465
339
  this._componentCache = {};
340
+ /** Child gameObjects in sibling order */
341
+ this.children = [];
342
+ /** Whether this gameObject is registered on a scene */
343
+ this.inScene = false;
466
344
  /** Components apply to this gameObject */
467
345
  this.components = [];
468
346
  /** GameObject has been destroyed */
469
347
  this.destroyed = false;
470
348
  this._name = name;
471
349
  this.id = getId();
472
- this.addComponent(Transform, obj);
473
- }
474
- /**
475
- * Get default transform component
476
- * @returns transform component on this gameObject
477
- * @readonly
478
- */
479
- get transform() {
480
- return this.getComponent(Transform);
481
350
  }
482
351
  /**
483
352
  * Get parent gameObject
@@ -485,7 +354,7 @@ class GameObject {
485
354
  * @readonly
486
355
  */
487
356
  get parent() {
488
- return this.transform && this.transform.parent && this.transform.parent.gameObject;
357
+ return this._parent;
489
358
  }
490
359
  /**
491
360
  * Get the name of this gameObject
@@ -499,10 +368,8 @@ class GameObject {
499
368
  return;
500
369
  const scene = this._scene;
501
370
  this._scene = val;
502
- if (this.transform && this.transform.children) {
503
- for (const child of this.transform.children) {
504
- child.gameObject.scene = val;
505
- }
371
+ for (const child of this.children) {
372
+ child.scene = val;
506
373
  }
507
374
  if (val) {
508
375
  val.addGameObject(this);
@@ -524,16 +391,28 @@ class GameObject {
524
391
  * @param gameObject - child gameobject
525
392
  */
526
393
  addChild(gameObject) {
527
- if (!gameObject || !gameObject.transform || gameObject === this)
394
+ if (!gameObject || gameObject === this)
528
395
  return;
529
396
  if (!(gameObject instanceof GameObject)) {
530
397
  throw new Error('addChild only receive GameObject');
531
398
  }
532
- if (!this.transform) {
399
+ if (this.destroyed) {
533
400
  throw new Error(`gameObject '${this.name}' has been destroy`);
534
401
  }
535
- gameObject.transform.parent = this.transform;
402
+ if (gameObject.parent === this) {
403
+ const index = this.children.indexOf(gameObject);
404
+ if (index > -1)
405
+ this.children.splice(index, 1);
406
+ this.children.push(gameObject);
407
+ return;
408
+ }
409
+ if (gameObject.parent) {
410
+ gameObject.parent.removeChild(gameObject);
411
+ }
412
+ gameObject._parent = this;
413
+ this.children.push(gameObject);
536
414
  gameObject.scene = this.scene;
415
+ notifyGameObjectProp(gameObject, 'parent', exports.OBSERVER_TYPE.CHANGE);
537
416
  }
538
417
  /**
539
418
  * Remove child gameObject
@@ -543,8 +422,12 @@ class GameObject {
543
422
  if (!(gameObject instanceof GameObject) || !gameObject.parent || gameObject.parent !== this) {
544
423
  return gameObject;
545
424
  }
546
- gameObject.transform.parent = null;
425
+ const index = this.children.indexOf(gameObject);
426
+ if (index > -1)
427
+ this.children.splice(index, 1);
428
+ gameObject._parent = null;
547
429
  gameObject.scene = null;
430
+ notifyGameObjectProp(gameObject, 'parent', exports.OBSERVER_TYPE.CHANGE);
548
431
  return gameObject;
549
432
  }
550
433
  addComponent(C, obj) {
@@ -586,9 +469,6 @@ class GameObject {
586
469
  else if (c.componentName) {
587
470
  componentName = c.componentName;
588
471
  }
589
- if (componentName === 'Transform') {
590
- throw new Error("Transform can't be removed");
591
- }
592
472
  return this._removeComponent(componentName);
593
473
  }
594
474
  _removeComponent(componentName) {
@@ -631,15 +511,15 @@ class GameObject {
631
511
  }
632
512
  /** Destroy this gameObject */
633
513
  destroy() {
634
- if (!this.transform) {
514
+ if (this.destroyed) {
635
515
  console.error('Cannot destroy gameObject that have already been destroyed.');
636
516
  return;
637
517
  }
638
- Array.from(this.transform.children).forEach(({ gameObject }) => {
639
- gameObject.destroy();
518
+ Array.from(this.children).forEach(child => {
519
+ child.destroy();
640
520
  });
641
521
  this.remove();
642
- this.transform.clearChildren();
522
+ this.children.length = 0;
643
523
  for (const key in this._componentCache) {
644
524
  this._removeComponent(key);
645
525
  }
@@ -671,22 +551,30 @@ class ComponentObserver {
671
551
  * @param type - change event type
672
552
  * @param componentName - `component.name` this parameter will deprecated
673
553
  */
674
- add({ component, prop, type, componentName }) {
554
+ add({ component, prop, type, componentName, gameObject }) {
555
+ const go = gameObject ?? component?.gameObject;
675
556
  if (type === exports.OBSERVER_TYPE.REMOVE) {
676
- if (this.events.find((changed) => changed.component === component && changed.type === exports.OBSERVER_TYPE.ADD)) {
677
- this.events = this.events.filter(changed => changed.component !== component);
557
+ const sameTarget = (changed) => component
558
+ ? changed.component === component
559
+ : changed.gameObject === go && changed.componentName === componentName;
560
+ if (this.events.find(changed => sameTarget(changed) && changed.type === exports.OBSERVER_TYPE.ADD)) {
561
+ this.events = this.events.filter(changed => !sameTarget(changed));
678
562
  return;
679
563
  }
680
- this.events = this.events.filter(changed => changed.component !== component);
564
+ this.events = this.events.filter(changed => !sameTarget(changed));
681
565
  }
682
- const index = this.events.findIndex(changed => changed.component === component && lodashEs.isEqual(changed.prop, prop) && changed.type === type);
566
+ const index = this.events.findIndex(changed => changed.gameObject === go &&
567
+ changed.component === component &&
568
+ changed.componentName === componentName &&
569
+ lodashEs.isEqual(changed.prop, prop) &&
570
+ changed.type === type);
683
571
  if (index > -1) {
684
572
  this.events.splice(index, 1);
685
573
  }
686
574
  this.events.push({
687
- gameObject: component.gameObject,
575
+ gameObject: go,
688
576
  component,
689
- prop: prop,
577
+ prop,
690
578
  type,
691
579
  componentName,
692
580
  });
@@ -961,8 +849,8 @@ class Ticker {
961
849
  * Scene is a gameObject container
962
850
  */
963
851
  class Scene extends GameObject {
964
- constructor(name, obj) {
965
- super(name, obj);
852
+ constructor(name) {
853
+ super(name);
966
854
  this.gameObjects = [];
967
855
  this.scene = this; // gameObject.scene = this
968
856
  }
@@ -972,9 +860,10 @@ class Scene extends GameObject {
972
860
  */
973
861
  addGameObject(gameObject) {
974
862
  this.gameObjects.push(gameObject);
975
- if (gameObject.transform) {
976
- gameObject.transform.inScene = true;
977
- }
863
+ if (gameObject.inScene)
864
+ return;
865
+ gameObject.inScene = true;
866
+ notifyGameObjectProp(gameObject, 'inScene', exports.OBSERVER_TYPE.CHANGE);
978
867
  }
979
868
  /**
980
869
  * Remove gameObject
@@ -984,8 +873,9 @@ class Scene extends GameObject {
984
873
  const index = this.gameObjects.indexOf(gameObject);
985
874
  if (index === -1)
986
875
  return;
987
- if (gameObject.transform) {
988
- gameObject.transform.inScene = false;
876
+ if (gameObject.inScene) {
877
+ gameObject.inScene = false;
878
+ notifyGameObjectProp(gameObject, 'inScene', exports.OBSERVER_TYPE.CHANGE);
989
879
  }
990
880
  this.gameObjects.splice(index, 1);
991
881
  }
@@ -1001,7 +891,7 @@ class Scene extends GameObject {
1001
891
  }
1002
892
 
1003
893
  /** Generated at build from package.json */
1004
- const version = "0.0.53";
894
+ const version = "0.1.0";
1005
895
 
1006
896
  /**
1007
897
  * Sent to `window.parent` after each `System.init` completes during `Game.addSystem`
@@ -2188,7 +2078,6 @@ exports.IDEProp = IDEProp;
2188
2078
  exports.RESOURCE_TYPE_STRATEGY = RESOURCE_TYPE_STRATEGY;
2189
2079
  exports.Scene = Scene;
2190
2080
  exports.System = System;
2191
- exports.Transform = Transform;
2192
2081
  exports.componentObserver = componentObserver;
2193
2082
  exports.decorators = decorators;
2194
2083
  exports.isAllowedMessageOrigin = isAllowedMessageOrigin;