koota 0.0.3

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/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) [year] [fullname]
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,337 @@
1
+ # Koota
2
+
3
+ Koota is an ECS-based state management library optimized for real-time apps, games, and XR experiences. Use as much or as little as you need.
4
+
5
+ ```bash
6
+ npm i koota
7
+ ```
8
+
9
+ ### First, define traits
10
+
11
+ Traits are the building blocks of your state. They represent slices of data with specific meanings.
12
+
13
+ ```js
14
+ import { trait } from 'koota';
15
+
16
+ // Basic trait with default values
17
+ const Position = trait({ x: 0, y: 0 });
18
+ const Velocity = trait({ x: 0, y: 0 });
19
+
20
+ // Trait with a callback for initial value
21
+ const Mesh = trait({ value: () => THREE.Mesh() });
22
+
23
+ // Tag trait (no data)
24
+ const IsActive = trait();
25
+ ```
26
+
27
+ ### Spawn entities
28
+
29
+ Entities are spawned in a world. By adding traits to an entity they gain content.
30
+
31
+ ```js
32
+ import { createWorld } from 'koota';
33
+
34
+ const world = createWorld();
35
+
36
+ const player = world.spawn(Position, Velocity);
37
+ // Initial values can be passed in to the trait by using it as a function
38
+ const goblin = world.spawn(Position({ x: 10, y: 10 }), Velocity, Mesh);
39
+ ```
40
+
41
+ ### Query and update data
42
+
43
+ Queries fetch entities sharing traits (archetypes). Use them to batch update entities efficiently.
44
+
45
+ ```js
46
+ // Run this in a loop
47
+ world.query(Position, Velocity).updateEach(([position, velocity]) => {
48
+ position.x += velocity.x * delta;
49
+ position.y += velocity.y * delta;
50
+ });
51
+ ```
52
+
53
+ ### Use in your React components
54
+
55
+ Traits can be used reactievely inside of React components.
56
+
57
+ ```js
58
+ import { WorldProvider, useQuery, useObserve } from 'koota/react'
59
+
60
+ // Wrap your app in WorldProvider
61
+ createRoot(document.getElementById('root')!).render(
62
+ <WorldProvider world={world}>
63
+ <App />
64
+ </WorldProvider>
65
+ );
66
+
67
+ function RocketRenderer() {
68
+ // Reactively update whenever the query updates with new entities
69
+ const rockets = useQuery(Position, Velocity)
70
+ return (
71
+ <>
72
+ {rockets.map((entity) => <Rocket key={entity} entity={entity} />)}
73
+ </>
74
+ )
75
+ }
76
+
77
+ function Rocket({ entity }) {
78
+ // Observes this entity's position trait and reactively updates when it changes
79
+ const position = useObserve(entity, Position)
80
+ return (
81
+ <div style={{ position: 'absolute', left: position.x ?? 0, top: position.y ?? 0 }}>
82
+ 🚀
83
+ </div>
84
+ )
85
+ }
86
+ ```
87
+
88
+ ### Modify Koota state safely with actions
89
+
90
+ Use actions to safely modify Koota from inside of React in either effects or events.
91
+
92
+ ```js
93
+ import { createActions } from 'koota/react';
94
+
95
+ const useMyActions = createActions((world) => ({
96
+ spawnShip: (position) => world.spawn(Position(position), Velocity),
97
+ destroyAllShips: (world) => {
98
+ world.query(Position, Velocity).forEach((entity) => {
99
+ entity.destroy();
100
+ });
101
+ },
102
+ }));
103
+
104
+ function DoomButton() {
105
+ const { spawnShip, destroyAllShips } = useMyActions();
106
+
107
+ // Spawn three ships on mount
108
+ useEffect(() => {
109
+ spawnShip({ x: 0, y: 1 });
110
+ spawnShip({ x: 1, y: 0 });
111
+ spawnShip({ x: 1, y: 1 });
112
+
113
+ // Destroy all ships during cleanup
114
+ return () => drestroyAllShips();
115
+ }, []);
116
+
117
+ // And destroy all ships on click!
118
+ return <button onClick={destroyAllShips}>Boom!</button>;
119
+ }
120
+ ```
121
+
122
+ Or access world directly and use it.
123
+
124
+ ```js
125
+ const world = useWorld();
126
+
127
+ useEffect(() => {
128
+ const entity = world.spawn(Velocity, Position);
129
+ return () => entity.destroy();
130
+ });
131
+ ```
132
+
133
+ ## Advanced
134
+
135
+ ### Relationships
136
+
137
+ Koota supports relationships between entities using the `relation` function. Relationships allow you to create connections between entities and query them efficiently.
138
+
139
+ ```js
140
+ const ChildOf = relation();
141
+
142
+ const parent = world.spawn();
143
+ const child = world.spawn(ChildOf(parent));
144
+
145
+ const entity = world.queryFirst(ChildOf(parent)); // Returns child
146
+ ```
147
+
148
+ #### With data
149
+
150
+ Relationships can contain data like any trait.
151
+
152
+ ```js
153
+ const Contains = relation({ store: { amount: 0 } });
154
+
155
+ const inventory = world.spawn();
156
+ const gold = world.spawn();
157
+ inventory.add(Contains(gold));
158
+ inventory.set(Contains(gold), { amount: 10 });
159
+ ```
160
+
161
+ #### Auto remove target
162
+
163
+ Relations can automatically remove target entities and their descendants.
164
+
165
+ ```js
166
+ const ChildOf = relation({ autoRemoveTarget: true });
167
+
168
+ const parent = world.spawn();
169
+ const child = world.spawn(ChildOf(parent));
170
+ const grandchild = world.spawn(ChildOf(child));
171
+
172
+ parent.destroy();
173
+
174
+ world.has(child); // False, the child and grandchild are destroyed too
175
+ ```
176
+
177
+ #### Exclusive Relationships
178
+
179
+ Exclusive relationships ensure each entity can only have one target.
180
+
181
+ ```js
182
+ const Targeting = relation({ exclusive: true });
183
+
184
+ const hero = world.spawn();
185
+ const rat = world.spawn();
186
+ const goblin = world.spawn();
187
+
188
+ hero.add(Targeting(rat));
189
+ hero.add(Targeting(goblin));
190
+
191
+ hero.has(Targeting(rat)); // False
192
+ hero.has(Targeting(goblin)); // True
193
+ ```
194
+
195
+ #### Querying relationships
196
+
197
+ Relationships can be queried with specific targets, wildcard targets using `*` and even inverted wildcard searches with `Wildcard` to get all entities with a relationships targeting another entity.
198
+
199
+ ```js
200
+ const gold = world.spawn();
201
+ const silver = world.spawn();
202
+ const inventory = world.spawn(Contains(gold), Contains(silver));
203
+
204
+ const targets = inventory.targetsFor(Contains); // Returns [gold, silver]
205
+
206
+ const chest = world.spawn(Contains(gold));
207
+ const dwarf = world.spawn(Desires(gold));
208
+
209
+ const constainsSilver = world.query(Contains(silver)); // Returns [inventory]
210
+ const containsAnything = world.query(Contains('*')); // Returns [inventory, chest]
211
+ const relatesToGold = world.query(Widlcard(gold)); // Returns [inventory, chest, dwarf]
212
+ ```
213
+
214
+ ### Query modifiers
215
+
216
+ Modifiers are used to filter query results enabling powerful patterns. All modifiers can mixed together.
217
+
218
+ #### Not
219
+
220
+ The `Not` modifier excludes entities that have specific traits from the query results.
221
+
222
+ ```js
223
+ import { Not } from 'koota';
224
+
225
+ const staticEntities = world.query(Position, Not(Velocity));
226
+ ```
227
+
228
+ #### Or
229
+
230
+ By default all query paramters are combined with logical AND. The `Or` modifier enables using logical OR instead.
231
+
232
+ ```js
233
+ import { Or } from 'koota';
234
+
235
+ const movingOrVisible = world.query(Or(Velocity, Renderable));
236
+ ```
237
+
238
+ #### Added
239
+
240
+ The `Added` modifier tracks all entities that have added the specified traits since the last time the query was run. A new instance of the modifier must be created for tracking to be unique.
241
+
242
+ ```js
243
+ import { createAdded } from 'koota';
244
+
245
+ const Added = createAdded();
246
+
247
+ // This query will return entities that have just added the Position trait
248
+ const newPositions = world.query(Added(Position));
249
+
250
+ // After running the query, the Added modifier is reset
251
+ ```
252
+
253
+ #### Removed
254
+
255
+ The `Removed` modifier tracks all entities that have removed the specified traits since the last time the query was run. This includes entities that have been destroyed. A new instance of the modifier must be created for tracking to be unique.
256
+
257
+ ```js
258
+ import { createRemoved } from 'koota';
259
+
260
+ const Removed = createRemoved();
261
+
262
+ // This query will return entities that have just removed the Velocity trait
263
+ const stoppedEntities = world.query(Removed(Velocity));
264
+
265
+ // After running the query, the Removed modifier is reset
266
+ ```
267
+
268
+ #### Changed
269
+
270
+ The `Changed` modifier tracks all entities that have had the specified traits values change since the last time the query was run. A new instance of the modifier must be created for tracking to be unique.
271
+
272
+ ```js
273
+ import { createChanged } from 'koota';
274
+
275
+ const Changed = createChanged();
276
+
277
+ // This query will return entities whose Position has changed
278
+ const movedEntities = world.query(Changed(Position));
279
+
280
+ // After running the query, the Changed modifier is reset
281
+ ```
282
+
283
+ ### Add, remove and change events
284
+
285
+ Koota allows you to subscribe to add, remove, and change events for specific traits.
286
+
287
+ ```js
288
+ // Subscribe to Position changes
289
+ const unsub = world.onChange(Position, (entity) => {
290
+ console.log(`Entity ${entity} changed position`);
291
+ });
292
+
293
+ // Subscribe to Position additions
294
+ const unsub = world.onAdd(Position, (entity) => {
295
+ console.log(`Entity ${entity} added position`);
296
+ });
297
+
298
+ // Subscribe to Position removals
299
+ const unsub = world.onRemove(Position, (entity) => {
300
+ console.log(`Entity ${entity} removed position`);
301
+ });
302
+
303
+ // Trigger events
304
+ const entity = world.spawn(Position);
305
+ entity.set(Position, { x: 10, y: 20 });
306
+ entity.remove(Position);
307
+ ```
308
+
309
+ ### World traits
310
+
311
+ For global data like time, these can be traits added to the world. **World traits do not appear in queries.**
312
+
313
+ ```js
314
+ const Time = trait({ delta: 0, current: 0 });
315
+ world.add(Time);
316
+
317
+ const time = world.get(Time);
318
+ world.set(Time, { current: performance.now() });
319
+ ```
320
+
321
+ ### Modifying trait stores direclty
322
+
323
+ For performance-critical operations, you can modify trait stores directly using the useStore hook. This approach bypasses some of the safety checks and event triggers, so use it with caution. All stores are structure of arrays for performance purposes.
324
+
325
+ ```js
326
+ // Returns the SoA stores
327
+ world.query(Position, Velocity).useStore(([position, store], entities) => {
328
+ // Write our own loop over the stores
329
+ for (let i = 0; i > entities.length; i++) {
330
+ // Get the entity ID to use as the array index
331
+ const eid = entities[i].id();
332
+ // Write to each array in the store
333
+ position.x[eid] += velocity.x[eid] * delta;
334
+ position.y[eid] += velocity.y[eid] * delta;
335
+ }
336
+ });
337
+ ```
@@ -0,0 +1,17 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __typeError = (msg) => {
3
+ throw TypeError(msg);
4
+ };
5
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
7
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
8
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
9
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
10
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
11
+
12
+ export {
13
+ __publicField,
14
+ __privateGet,
15
+ __privateAdd,
16
+ __privateSet
17
+ };