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 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
- // Gets a snapshot instance of the trait
487
- // Return TraitInstance
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 a snapshot instance of the trait
543
- // Return TraitInstance
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
- ```js
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
- trait <
675
- AttackerSchema >
676
- {
677
- continueCombo: null,
678
- currentStageIndex: null,
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
- ```js
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
- ### `usQueryFirst`
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 whenenver it is added, removed or changes value without rerendering.
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