koota 0.5.0 → 0.5.2
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 +70 -25
- package/dist/{chunk-KLYFWWYD.js → chunk-BQ2FKEYY.js} +590 -579
- package/dist/index.cjs +590 -579
- package/dist/index.d.cts +14 -10
- package/dist/index.d.ts +14 -10
- package/dist/index.js +1 -1
- package/dist/react.cjs +561 -567
- package/dist/react.d.cts +3 -3
- package/dist/react.d.ts +3 -3
- package/dist/react.js +1 -1
- package/dist/{world-DzIKt3Qf.d.cts → world-DfBlN538.d.cts} +151 -140
- package/dist/{world-DzIKt3Qf.d.ts → world-DfBlN538.d.ts} +151 -140
- package/package.json +8 -8
- package/react/index.cjs +561 -567
- package/react/index.d.cts +3 -3
- package/react/index.d.ts +3 -3
- package/react/index.js +1 -1
package/README.md
CHANGED
|
@@ -364,6 +364,26 @@ world
|
|
|
364
364
|
.updateEach(([position, velocity]) => {}, { changeDetection: 'always' })
|
|
365
365
|
```
|
|
366
366
|
|
|
367
|
+
Changed detection shallowly compares the scalar values just like React. This means objects and arrays will only be detected as changed if a new object or array is committed to the store. While immutable state is a great design pattern, it creates memory pressure and reduces performance so instead you can mutate and manually flag that a changed has occured.
|
|
368
|
+
|
|
369
|
+
```js
|
|
370
|
+
// ❌ This change will not be detected since the array is mutated and will pass the comparison
|
|
371
|
+
world.query(Inventory).updateEach(([inventory]) => {
|
|
372
|
+
inventory.items.push(item)
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
// ✅ This change will be detected since a new array is created and the comparison will fail
|
|
376
|
+
world.query(Inventory).updateEach(([inventory]) => {
|
|
377
|
+
inventory.items = [...inventory.items, item]
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
// ✅ This change is manually flagged and we still get to mutate for performance
|
|
381
|
+
world.query(Inventory).updateEach(([inventory], entity) => {
|
|
382
|
+
inventory.items.push(item)
|
|
383
|
+
entity.changed()
|
|
384
|
+
})
|
|
385
|
+
```
|
|
386
|
+
|
|
367
387
|
### World traits
|
|
368
388
|
|
|
369
389
|
For global data like time, these can be traits added to the world. **World traits do not appear in queries.**
|
|
@@ -483,8 +503,8 @@ world.remove(Time)
|
|
|
483
503
|
// Return boolean
|
|
484
504
|
const result = world.has(Time)
|
|
485
505
|
|
|
486
|
-
//
|
|
487
|
-
// Return
|
|
506
|
+
// Returns the trait record for the world
|
|
507
|
+
// Return TraitRecord
|
|
488
508
|
const time = world.get(Time)
|
|
489
509
|
|
|
490
510
|
// Sets the trait and triggers a change event
|
|
@@ -539,8 +559,8 @@ entity.remove(Position)
|
|
|
539
559
|
// Return boolean
|
|
540
560
|
const result = entity.has(Position)
|
|
541
561
|
|
|
542
|
-
// Gets
|
|
543
|
-
// Return
|
|
562
|
+
// Gets the trait record for an entity
|
|
563
|
+
// Return TraitRecord
|
|
544
564
|
const position = entity.get(Position)
|
|
545
565
|
|
|
546
566
|
// Sets the trait and triggers a change event
|
|
@@ -658,38 +678,63 @@ const store = [
|
|
|
658
678
|
const Mesh = trait(() => new THREE.Mesh())
|
|
659
679
|
```
|
|
660
680
|
|
|
681
|
+
#### Trait record
|
|
682
|
+
|
|
683
|
+
The state of a given entity-trait pair is called a trait record and is like the row of a table in a database. When the trait store is SoA the record returned is a snapshot of the state while when it is AoS the record is a ref to the object inserted there.
|
|
684
|
+
|
|
685
|
+
```js
|
|
686
|
+
// SoA store
|
|
687
|
+
const Position = trait({ x: 0, y: 0, z: 0 })
|
|
688
|
+
entity.add(Position)
|
|
689
|
+
// Returns a snapshot of the arrays
|
|
690
|
+
const position = entity.get(Position)
|
|
691
|
+
// position !== position2
|
|
692
|
+
const position2 = entity.get(Position)
|
|
693
|
+
|
|
694
|
+
// AoS store
|
|
695
|
+
const Velocity = trait(() => ({ x: 0, y: 0, z: 0 }))
|
|
696
|
+
entity.add(Velocity)
|
|
697
|
+
// Returns a ref to the object inserted
|
|
698
|
+
const velocity = entity.get(Velocity)
|
|
699
|
+
// velocity === velocity2
|
|
700
|
+
const velocity2 = entity.get(Velocity)
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
Use `TraitRecord` to type this state.
|
|
704
|
+
|
|
705
|
+
```ts
|
|
706
|
+
const PositionRecord = TraitRecord<typeof Position>
|
|
707
|
+
```
|
|
708
|
+
|
|
661
709
|
#### Typing traits
|
|
662
710
|
|
|
663
711
|
Traits can have a schema type passed into its generic. This can be useful if the inferred type is not good enough.
|
|
664
712
|
|
|
665
|
-
```
|
|
713
|
+
```ts
|
|
666
714
|
type AttackerSchema = {
|
|
667
|
-
continueCombo: boolean | null
|
|
668
|
-
currentStageIndex: number | null
|
|
669
|
-
stages: Array<AttackStage> | null
|
|
670
|
-
startedAt: number | null
|
|
715
|
+
continueCombo: boolean | null
|
|
716
|
+
currentStageIndex: number | null
|
|
717
|
+
stages: Array<AttackStage> | null
|
|
718
|
+
startedAt: number | null
|
|
671
719
|
}
|
|
672
720
|
|
|
673
|
-
const Attacker =
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
stages: null,
|
|
680
|
-
startedAt: null,
|
|
681
|
-
}
|
|
721
|
+
const Attacker = trait<AttackerSchema>({
|
|
722
|
+
continueCombo: null,
|
|
723
|
+
currentStageIndex: null,
|
|
724
|
+
stages: null,
|
|
725
|
+
startedAt: null,
|
|
726
|
+
})
|
|
682
727
|
```
|
|
683
728
|
|
|
684
729
|
However, this will not work with interfaces without a workaround due to intended behavior in TypeScript: https://github.com/microsoft/TypeScript/issues/15300
|
|
685
730
|
Interfaces can be used with `Pick` to convert the key signatures into something our type code can understand.
|
|
686
731
|
|
|
687
|
-
```
|
|
732
|
+
```ts
|
|
688
733
|
interface AttackerSchema {
|
|
689
|
-
continueCombo: boolean | null
|
|
690
|
-
currentStageIndex: number | null
|
|
691
|
-
stages: Array<AttackStage> | null
|
|
692
|
-
startedAt: number | null
|
|
734
|
+
continueCombo: boolean | null
|
|
735
|
+
currentStageIndex: number | null
|
|
736
|
+
stages: Array<AttackStage> | null
|
|
737
|
+
startedAt: number | null
|
|
693
738
|
}
|
|
694
739
|
|
|
695
740
|
// Pick is required to not get type errors
|
|
@@ -781,7 +826,7 @@ return (
|
|
|
781
826
|
)
|
|
782
827
|
```
|
|
783
828
|
|
|
784
|
-
### `
|
|
829
|
+
### `useQueryFirst`
|
|
785
830
|
|
|
786
831
|
Works like `useQuery` but only returns the first result. Can either be an entity of undefined.
|
|
787
832
|
|
|
@@ -870,7 +915,7 @@ return (
|
|
|
870
915
|
|
|
871
916
|
### `useTraitEffect`
|
|
872
917
|
|
|
873
|
-
Subscribes a callback to a trait on an entity. This callback fires as an effect
|
|
918
|
+
Subscribes a callback to a trait on an entity. This callback fires as an effect whenever it is added, removed or changes value without rerendering.
|
|
874
919
|
|
|
875
920
|
```js
|
|
876
921
|
// Subscribe to position changes on an entity and update a ref without causing a rerender
|