koota 0.6.0 → 0.6.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 +107 -17
- package/dist/{chunk-VKZPO7LA.js → chunk-URTUZC7P.js} +1314 -929
- package/dist/index.cjs +1137 -747
- package/dist/index.d.cts +44 -27
- package/dist/index.d.ts +44 -27
- package/dist/index.js +15 -5
- package/dist/react.cjs +799 -1236
- package/dist/react.d.cts +6 -2
- package/dist/react.d.ts +6 -2
- package/dist/react.js +112 -24
- package/dist/types-DOpAZa4-.d.cts +425 -0
- package/dist/types-DOpAZa4-.d.ts +425 -0
- package/package.json +3 -3
- package/react/index.cjs +799 -1236
- package/react/index.d.cts +6 -2
- package/react/index.d.ts +6 -2
- package/react/index.js +112 -24
- package/dist/world-qDo1l6Ai.d.cts +0 -285
- package/dist/world-qDo1l6Ai.d.ts +0 -285
package/README.md
CHANGED
|
@@ -72,11 +72,7 @@ createRoot(document.getElementById('root')!).render(
|
|
|
72
72
|
function RocketRenderer() {
|
|
73
73
|
// Reactively update whenever the query updates with new entities
|
|
74
74
|
const rockets = useQuery(Position, Velocity)
|
|
75
|
-
return (
|
|
76
|
-
<>
|
|
77
|
-
{rockets.map((entity) => <RocketView key={entity} entity={entity} />)}
|
|
78
|
-
</>
|
|
79
|
-
)
|
|
75
|
+
return rockets.map((entity) => <RocketView key={entity} entity={entity} />)
|
|
80
76
|
}
|
|
81
77
|
|
|
82
78
|
function RocketView({ entity }) {
|
|
@@ -84,7 +80,7 @@ function RocketView({ entity }) {
|
|
|
84
80
|
const position = useTrait(entity, Position)
|
|
85
81
|
return (
|
|
86
82
|
<div style={{ position: 'absolute', left: position.x ?? 0, top: position.y ?? 0 }}>
|
|
87
|
-
|
|
83
|
+
🚀
|
|
88
84
|
</div>
|
|
89
85
|
)
|
|
90
86
|
}
|
|
@@ -95,10 +91,10 @@ function RocketView({ entity }) {
|
|
|
95
91
|
Use actions to safely modify Koota from inside of React in either effects or events.
|
|
96
92
|
|
|
97
93
|
```js
|
|
98
|
-
import {
|
|
94
|
+
import { defineActions } from 'koota'
|
|
99
95
|
import { useActions } from 'koota/react'
|
|
100
96
|
|
|
101
|
-
const actions =
|
|
97
|
+
const actions = defineActions((world) => ({
|
|
102
98
|
spawnShip: (position) => world.spawn(Position(position), Velocity),
|
|
103
99
|
destroyAllShips: () => {
|
|
104
100
|
world.query(Position, Velocity).forEach((entity) => {
|
|
@@ -205,6 +201,34 @@ hero.has(Targeting(rat)) // False
|
|
|
205
201
|
hero.has(Targeting(goblin)) // True
|
|
206
202
|
```
|
|
207
203
|
|
|
204
|
+
#### Ordered relations
|
|
205
|
+
|
|
206
|
+
> ⚠️ **Experimental**<br>
|
|
207
|
+
> This API is experimental and may change in future versions. Please provide feedback on GitHub or Discord.
|
|
208
|
+
|
|
209
|
+
Ordered relations maintain a list of related entities with bidirectional sync.
|
|
210
|
+
|
|
211
|
+
```js
|
|
212
|
+
import { relation, ordered } from 'koota'
|
|
213
|
+
|
|
214
|
+
const ChildOf = relation()
|
|
215
|
+
const OrderedChildren = ordered(ChildOf)
|
|
216
|
+
|
|
217
|
+
const parent = world.spawn(OrderedChildren)
|
|
218
|
+
const children = parent.get(OrderedChildren)
|
|
219
|
+
|
|
220
|
+
children.push(child1) // adds ChildOf(parent) to child1
|
|
221
|
+
children.splice(0, 1) // removes ChildOf(parent) from child1
|
|
222
|
+
|
|
223
|
+
// Bidirectional sync works both ways
|
|
224
|
+
child2.add(ChildOf(parent)) // child2 automatically added to list
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Ordered relations support array methods like `push()`, `pop()`, `shift()`, `unshift()`, and `splice()`, plus special methods `moveTo()` and `insert()` for precise control. Changes to the list automatically sync with relations, and vice versa.
|
|
228
|
+
|
|
229
|
+
> ⚠️ **Performance note**<br>
|
|
230
|
+
> Ordered relations are more expensive to update than regular relations but enable faster traversal when order matters. Use them only when entity order is essential.
|
|
231
|
+
|
|
208
232
|
#### Querying relations
|
|
209
233
|
|
|
210
234
|
Relations can be queried with specific targets and wildcard targets using `*`.
|
|
@@ -286,6 +310,29 @@ const parent = world.spawn()
|
|
|
286
310
|
const changedChildren = world.query(Changed(ChildOf), ChildOf(parent))
|
|
287
311
|
```
|
|
288
312
|
|
|
313
|
+
#### Relation events
|
|
314
|
+
|
|
315
|
+
Relations emit events per **relation pair**. This makes it easy to know exactly which target was involved.
|
|
316
|
+
|
|
317
|
+
- `onAdd(Relation, (entity, target) => {})` triggers when `entity.add(Relation(target))` is called.
|
|
318
|
+
- `onRemove(Relation, (entity, target) => {})` triggers when `entity.remove(Relation(target))` is called.
|
|
319
|
+
- `onChange(Relation, (entity, target) => {})` triggers when relation **store data** is updated with `entity.set(Relation(target), data)` (only for relations created with a `store`).
|
|
320
|
+
|
|
321
|
+
```js
|
|
322
|
+
const ChildOf = relation({ store: { priority: 0 } })
|
|
323
|
+
|
|
324
|
+
const unsubAdd = world.onAdd(ChildOf, (entity, target) => {})
|
|
325
|
+
const unsubRemove = world.onRemove(ChildOf, (entity, target) => {})
|
|
326
|
+
const unsubChange = world.onChange(ChildOf, (entity, target) => {})
|
|
327
|
+
|
|
328
|
+
const parent = world.spawn()
|
|
329
|
+
const child = world.spawn()
|
|
330
|
+
|
|
331
|
+
child.add(ChildOf(parent)) // onAdd(child, parent)
|
|
332
|
+
child.set(ChildOf(parent), { priority: 1 }) // onChange(child, parent)
|
|
333
|
+
child.remove(ChildOf(parent)) // onRemove(child, parent)
|
|
334
|
+
```
|
|
335
|
+
|
|
289
336
|
### Query modifiers
|
|
290
337
|
|
|
291
338
|
Modifiers are used to filter query results enabling powerful patterns. All modifiers can be mixed together.
|
|
@@ -394,9 +441,14 @@ entity.set(Position, { x: 10, y: 20 })
|
|
|
394
441
|
entity.remove(Position)
|
|
395
442
|
```
|
|
396
443
|
|
|
444
|
+
When subscribing to relations, callbacks receive `(entity, target)` so you know which relation pair changed. Relation `onChange` events are triggered by `entity.set(Relation(target), data)` and only on relations with data via the store prop.
|
|
445
|
+
|
|
397
446
|
```js
|
|
398
|
-
|
|
399
|
-
|
|
447
|
+
const Likes = relation()
|
|
448
|
+
|
|
449
|
+
const unsub = world.onAdd(Likes, (entity, target) => {
|
|
450
|
+
console.log(`Entity ${entity} likes ${target}`)
|
|
451
|
+
})
|
|
400
452
|
```
|
|
401
453
|
|
|
402
454
|
### Change detection with `updateEach`
|
|
@@ -808,7 +860,7 @@ const positions = getStore(world, Position)
|
|
|
808
860
|
|
|
809
861
|
A Koota query is a lot like a database query. Parameters define how to find entities and efficiently process them in batches. Queries are the primary way to update and transform your app state, similar to how you'd use SQL to filter and modify database records.
|
|
810
862
|
|
|
811
|
-
####
|
|
863
|
+
#### Defining queries
|
|
812
864
|
|
|
813
865
|
Inline queries are great for readability and are optimized to be as fast as possible, but there is still some small overhead in hashing the query each time it is called.
|
|
814
866
|
|
|
@@ -820,13 +872,13 @@ function updateMovement(world) {
|
|
|
820
872
|
}
|
|
821
873
|
```
|
|
822
874
|
|
|
823
|
-
While this is not likely to be a bottleneck in your code compared to the actual update function, if you want to save these CPU cycles you can cache the query ahead of time and use the returned
|
|
875
|
+
While this is not likely to be a bottleneck in your code compared to the actual update function, if you want to save these CPU cycles you can cache the query ahead of time and use the returned ref. This will have the additional effect of creating the internal query immediately on all worlds, otherwise it will get created the first time it is run.
|
|
824
876
|
|
|
825
877
|
```js
|
|
826
878
|
// The internal query is created immediately before it is invoked
|
|
827
|
-
const movementQuery =
|
|
879
|
+
const movementQuery = defineQuery(Position, Velocity)
|
|
828
880
|
|
|
829
|
-
//
|
|
881
|
+
// The query ref is used for fast array-based lookup
|
|
830
882
|
function updateMovement(world) {
|
|
831
883
|
world.query(movementQuery).updateEach(([pos, vel]) => {})
|
|
832
884
|
}
|
|
@@ -834,7 +886,7 @@ function updateMovement(world) {
|
|
|
834
886
|
|
|
835
887
|
#### Query all entities
|
|
836
888
|
|
|
837
|
-
To get all queryable entities you simply query with no parameters.
|
|
889
|
+
To get all queryable entities you simply query the world with no parameters.
|
|
838
890
|
|
|
839
891
|
```js
|
|
840
892
|
const allEntities = world.query()
|
|
@@ -1014,13 +1066,51 @@ useTraitEffect(world, GameState, (state) => {
|
|
|
1014
1066
|
})
|
|
1015
1067
|
```
|
|
1016
1068
|
|
|
1069
|
+
### `useTarget`
|
|
1070
|
+
|
|
1071
|
+
Observes an entity, or world, for a relation and reactively returns the first target entity. Returns `undefined` if no target exists.
|
|
1072
|
+
|
|
1073
|
+
```js
|
|
1074
|
+
const ChildOf = relation()
|
|
1075
|
+
|
|
1076
|
+
function ParentDisplay({ entity }) {
|
|
1077
|
+
// Returns the first target of the ChildOf relation
|
|
1078
|
+
const parent = useTarget(entity, ChildOf)
|
|
1079
|
+
|
|
1080
|
+
if (!parent) return <div>No parent</div>
|
|
1081
|
+
|
|
1082
|
+
return <div>Parent: {parent.id()}</div>
|
|
1083
|
+
}
|
|
1084
|
+
```
|
|
1085
|
+
|
|
1086
|
+
### `useTargets`
|
|
1087
|
+
|
|
1088
|
+
Observes an entity, or world, for a relation and reactively returns all target entities as an array. Returns an empty array if no targets exist.
|
|
1089
|
+
|
|
1090
|
+
```js
|
|
1091
|
+
const Contains = relation()
|
|
1092
|
+
|
|
1093
|
+
function InventoryDisplay({ entity }) {
|
|
1094
|
+
// Returns all targets of the Contains relation
|
|
1095
|
+
const items = useTargets(entity, Contains)
|
|
1096
|
+
|
|
1097
|
+
return (
|
|
1098
|
+
<ul>
|
|
1099
|
+
{items.map((item) => (
|
|
1100
|
+
<li key={item.id()}>Item {item.id()}</li>
|
|
1101
|
+
))}
|
|
1102
|
+
</ul>
|
|
1103
|
+
)
|
|
1104
|
+
}
|
|
1105
|
+
```
|
|
1106
|
+
|
|
1017
1107
|
### `useActions`
|
|
1018
1108
|
|
|
1019
|
-
Returns actions bound to the world that is context. Use actions created by `
|
|
1109
|
+
Returns actions bound to the world that is in context. Use actions created by `defineActions`.
|
|
1020
1110
|
|
|
1021
1111
|
```js
|
|
1022
1112
|
// Create actions
|
|
1023
|
-
const actions =
|
|
1113
|
+
const actions = defineActions((world) => ({
|
|
1024
1114
|
spawnPlayer: () => world.spawn(IsPlayer).
|
|
1025
1115
|
destroyAllPlayers: () => {
|
|
1026
1116
|
world.query(IsPlayer).forEach((player) => {
|