koota 0.1.6 → 0.1.8
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 +83 -15
- package/dist/chunk-WCDM6ECE.js +1343 -0
- package/dist/chunk-XQK5JJDD.js +1344 -0
- package/dist/index.cjs +22 -15
- package/dist/index.d.cts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.js +3 -1
- package/dist/react.cjs +42 -34
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +33 -22
- package/dist/world-liWg9VB-.d.cts +245 -0
- package/dist/world-liWg9VB-.d.ts +245 -0
- package/package.json +3 -3
- package/react/index.cjs +42 -34
- package/react/index.d.cts +2 -2
- package/react/index.d.ts +2 -2
- package/react/index.js +33 -22
package/README.md
CHANGED
|
@@ -71,12 +71,12 @@ function RocketRenderer() {
|
|
|
71
71
|
const rockets = useQuery(Position, Velocity)
|
|
72
72
|
return (
|
|
73
73
|
<>
|
|
74
|
-
{rockets.map((entity) => <
|
|
74
|
+
{rockets.map((entity) => <RocketView key={entity} entity={entity} />)}
|
|
75
75
|
</>
|
|
76
76
|
)
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
function
|
|
79
|
+
function RocketView({ entity }) {
|
|
80
80
|
// Observes this entity's position trait and reactively updates when it changes
|
|
81
81
|
const position = useTrait(entity, Position)
|
|
82
82
|
return (
|
|
@@ -322,10 +322,18 @@ world.set(Time, { current: performance.now() });
|
|
|
322
322
|
```
|
|
323
323
|
|
|
324
324
|
### Select traits on queries for updates
|
|
325
|
-
Query filters entity results and `select` is used to choose what traits are fetched for `updateEach` and `useStore`.
|
|
325
|
+
Query filters entity results and `select` is used to choose what traits are fetched for `updateEach` and `useStore`. This can be useful if your query is wider than the data you want to modify.
|
|
326
326
|
|
|
327
327
|
```js
|
|
328
|
-
//
|
|
328
|
+
// The query finds all entities with Position, Velocity and Mass
|
|
329
|
+
world.query(Position, Velocity, Mass)
|
|
330
|
+
// And then select only Mass for updates
|
|
331
|
+
.select(Mass)
|
|
332
|
+
// Only mass will be used in the loop
|
|
333
|
+
.updateEach([mass] => {
|
|
334
|
+
// We are going blackhole
|
|
335
|
+
mass.value += 1
|
|
336
|
+
});
|
|
329
337
|
```
|
|
330
338
|
|
|
331
339
|
### Modifying trait stores direclty
|
|
@@ -473,7 +481,7 @@ Both schema-based and callback-based traits are used similarly, but they have di
|
|
|
473
481
|
|
|
474
482
|
[Learn more about AoS and SoA here](https://en.wikipedia.org/wiki/AoS_and_SoA).
|
|
475
483
|
|
|
476
|
-
|
|
484
|
+
#### Structure of Arrays (SoA) - Schema-based traits
|
|
477
485
|
|
|
478
486
|
When using a schema, each property is stored in its own array. This can lead to better cache locality when accessing a single property across many entities. This is always the fastest option for data that has intensive operations.
|
|
479
487
|
|
|
@@ -488,7 +496,7 @@ const store = {
|
|
|
488
496
|
};
|
|
489
497
|
```
|
|
490
498
|
|
|
491
|
-
|
|
499
|
+
#### Array of Structures (AoS) - Callback-based traits
|
|
492
500
|
|
|
493
501
|
When using a callback, each entity's trait data is stored as an object in an array. This is best used for compatibiilty with third party libraries like Three, or class instnaces in general.
|
|
494
502
|
|
|
@@ -507,6 +515,47 @@ const store = [
|
|
|
507
515
|
const Mesh = trait(() => THREE.Mesh())
|
|
508
516
|
```
|
|
509
517
|
|
|
518
|
+
#### Typing traits
|
|
519
|
+
|
|
520
|
+
Traits can have a schema type passed into its generic. This can be useful if the inferred type is not good enough.
|
|
521
|
+
|
|
522
|
+
```js
|
|
523
|
+
type AttackerSchema = {
|
|
524
|
+
continueCombo: boolean | null,
|
|
525
|
+
currentStageIndex: number | null,
|
|
526
|
+
stages: Array<AttackStage> | null,
|
|
527
|
+
startedAt: number | null,
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const Attacker = trait<AttackerSchema>({
|
|
531
|
+
continueCombo: null,
|
|
532
|
+
currentStageIndex: null,
|
|
533
|
+
stages: null,
|
|
534
|
+
startedAt: null,
|
|
535
|
+
})
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
However, this will not work with interfaces without a workaround due to intended behavior in TypeScript: https://github.com/microsoft/TypeScript/issues/15300
|
|
539
|
+
Interfaces can be used with `Pick` to convert the key signatures into something our type code can understand.
|
|
540
|
+
|
|
541
|
+
```js
|
|
542
|
+
interface AttackerSchema {
|
|
543
|
+
continueCombo: boolean | null,
|
|
544
|
+
currentStageIndex: number | null,
|
|
545
|
+
stages: Array<AttackStage> | null,
|
|
546
|
+
startedAt: number | null,
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Pick is required to not get type errors
|
|
550
|
+
const Attacker = trait<Pick<AttackerSchema, keyof AttackerSchema>>({
|
|
551
|
+
continueCombo: null,
|
|
552
|
+
currentStageIndex: null,
|
|
553
|
+
stages: null,
|
|
554
|
+
startedAt: null,
|
|
555
|
+
})
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
|
|
510
559
|
### React
|
|
511
560
|
|
|
512
561
|
### `useQuery`
|
|
@@ -517,10 +566,10 @@ Reactively updates when entities matching the query changes. Returns a `QueryRes
|
|
|
517
566
|
// Get all entities with Position and Velocity traits
|
|
518
567
|
const entities = useQuery(Position, Velocity);
|
|
519
568
|
|
|
520
|
-
// Render
|
|
569
|
+
// Render a view
|
|
521
570
|
return (
|
|
522
571
|
<>
|
|
523
|
-
{entities.map(entity => <
|
|
572
|
+
{entities.map(entity => <View key={entity.id()} entity={entity} />)}
|
|
524
573
|
</>
|
|
525
574
|
);
|
|
526
575
|
```
|
|
@@ -533,9 +582,9 @@ Works like `useQuery` but only returns the first result. Can either be an entity
|
|
|
533
582
|
// Get the first entity with Player and Position traits
|
|
534
583
|
const player = useQueryFirst(Player, Position);
|
|
535
584
|
|
|
536
|
-
// Render
|
|
585
|
+
// Render a view if an entity is found
|
|
537
586
|
return player ? (
|
|
538
|
-
<
|
|
587
|
+
<View entity={player} />
|
|
539
588
|
) : null;
|
|
540
589
|
|
|
541
590
|
```
|
|
@@ -577,11 +626,10 @@ function App() {
|
|
|
577
626
|
|
|
578
627
|
### `useTrait`
|
|
579
628
|
|
|
580
|
-
Observes an entity, or world, for a given trait and reactively updates when it is added, removed or changes value.
|
|
629
|
+
Observes an entity, or world, for a given trait and reactively updates when it is added, removed or changes value. The returned trait snapshot maybe `undefined` if the trait is no longer on the target. This can be used to conditionally render.
|
|
581
630
|
|
|
582
631
|
```js
|
|
583
|
-
// Get the position trait from an entity and reactively updates
|
|
584
|
-
// when it changes
|
|
632
|
+
// Get the position trait from an entity and reactively updates when it changes
|
|
585
633
|
const position = useTrait(entity, Position);
|
|
586
634
|
|
|
587
635
|
// If position is removed from entity then it will be undefined
|
|
@@ -593,7 +641,28 @@ return (
|
|
|
593
641
|
Position: {position.x}, {position.y}
|
|
594
642
|
</div>
|
|
595
643
|
);
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
The entity passed into `useTrait` can be `undefined` or `null`. This helps with situations where `useTrait` is combined with queries in the same component since hooks cannot be conditionally called. However, this means that result can be `undefined` if the trait is not on the entity or if the target is itself `undefined`. In most cases the distinction will not matter, but if it does you can disambiguate by testing the target.
|
|
596
647
|
|
|
648
|
+
```js
|
|
649
|
+
// The entity may be undefined if there is no valid result
|
|
650
|
+
const entity = useQueryFirst(Position, Velocity)
|
|
651
|
+
// useTrait handles this by returned undefined if the target passed in does not exist
|
|
652
|
+
const position = useTrait(entity, Position);
|
|
653
|
+
|
|
654
|
+
// However, undefined here can mean no entity or no component on entity
|
|
655
|
+
// To make the outcome no longer ambiguous you have to test the entity
|
|
656
|
+
if (!entity) return <div>No entity found!</div>
|
|
657
|
+
|
|
658
|
+
// Now this is narrowed to Position no longer being on the component
|
|
659
|
+
if (!position) return null
|
|
660
|
+
|
|
661
|
+
return (
|
|
662
|
+
<div>
|
|
663
|
+
Position: {position.x}, {position.y}
|
|
664
|
+
</div>
|
|
665
|
+
);
|
|
597
666
|
```
|
|
598
667
|
|
|
599
668
|
### `useTraitEffect`
|
|
@@ -601,8 +670,7 @@ return (
|
|
|
601
670
|
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.
|
|
602
671
|
|
|
603
672
|
```js
|
|
604
|
-
// Subscribe to position changes on an entity and update a ref
|
|
605
|
-
// without causing a rerender
|
|
673
|
+
// Subscribe to position changes on an entity and update a ref without causing a rerender
|
|
606
674
|
useTraitEffect(entity, Position, (position) => {
|
|
607
675
|
if (!position) return;
|
|
608
676
|
meshRef.current.position.copy(position);
|