tinyecs 0.3.5__tar.gz → 0.3.6__tar.gz
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.
- {tinyecs-0.3.5/src/tinyecs.egg-info → tinyecs-0.3.6}/PKG-INFO +1 -1
- {tinyecs-0.3.5 → tinyecs-0.3.6}/pyproject.toml +1 -1
- {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/__init__.py +72 -35
- {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/tutorial.py +5 -0
- {tinyecs-0.3.5 → tinyecs-0.3.6/src/tinyecs.egg-info}/PKG-INFO +1 -1
- {tinyecs-0.3.5 → tinyecs-0.3.6}/tests/test_tinyecs.py +13 -0
- {tinyecs-0.3.5 → tinyecs-0.3.6}/LICENSE +0 -0
- {tinyecs-0.3.5 → tinyecs-0.3.6}/README.rst +0 -0
- {tinyecs-0.3.5 → tinyecs-0.3.6}/setup.cfg +0 -0
- {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/components.py +0 -0
- {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/compsys.py +0 -0
- {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/demo.py +0 -0
- {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/demos/__init__.py +0 -0
- {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/demos/background.py +0 -0
- {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/demos/bouncing_sprites.py +0 -0
- {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/demos/example.py +0 -0
- {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/demos/homing-missiles.py +0 -0
- {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/demos/marquee.py +0 -0
- {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs.egg-info/SOURCES.txt +0 -0
- {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs.egg-info/dependency_links.txt +0 -0
- {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs.egg-info/entry_points.txt +0 -0
- {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs.egg-info/requires.txt +0 -0
- {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs.egg-info/top_level.txt +0 -0
|
@@ -282,16 +282,16 @@ def remove_component(eid: EntityID, *cids: ComponentID) -> None:
|
|
|
282
282
|
obj.shutdown_()
|
|
283
283
|
|
|
284
284
|
|
|
285
|
-
def add_system(
|
|
285
|
+
def add_system(fn: SystemFunction, *cids: ComponentID) -> None:
|
|
286
286
|
r"""Add a system for the specifiied cids.
|
|
287
287
|
|
|
288
|
-
:param
|
|
288
|
+
:param fn: The system function
|
|
289
289
|
:param cids: The component ids that are required for this system
|
|
290
290
|
:return: None
|
|
291
291
|
|
|
292
292
|
The prototype for the function is::
|
|
293
293
|
|
|
294
|
-
|
|
294
|
+
fn(delta_time, eid, *comps)
|
|
295
295
|
|
|
296
296
|
where delta_time is e.g. the miliseconds from a pygame tick. eid is the id
|
|
297
297
|
of the entity that matches, and \*comps are all requested components for
|
|
@@ -304,13 +304,13 @@ def add_system(fkt: SystemFunction, *cids: ComponentID) -> None:
|
|
|
304
304
|
"""
|
|
305
305
|
|
|
306
306
|
create_archetype(*cids)
|
|
307
|
-
sidx[
|
|
307
|
+
sidx[fn] = cids
|
|
308
308
|
|
|
309
309
|
|
|
310
|
-
def remove_system(
|
|
310
|
+
def remove_system(fn: SystemFunction) -> None:
|
|
311
311
|
"""Remove the given function from the registry.
|
|
312
312
|
|
|
313
|
-
:param
|
|
313
|
+
:param fn: The system function
|
|
314
314
|
:return: None
|
|
315
315
|
|
|
316
316
|
Remove the match for this function from the registry
|
|
@@ -319,11 +319,11 @@ def remove_system(fkt: SystemFunction) -> None:
|
|
|
319
319
|
"""
|
|
320
320
|
|
|
321
321
|
for domain in didx:
|
|
322
|
-
remove_system_from_domain(domain,
|
|
322
|
+
remove_system_from_domain(domain, fn)
|
|
323
323
|
|
|
324
324
|
# Ignore unregistered systems, since we're removing anyways
|
|
325
325
|
try:
|
|
326
|
-
del sidx[
|
|
326
|
+
del sidx[fn]
|
|
327
327
|
except KeyError:
|
|
328
328
|
pass
|
|
329
329
|
|
|
@@ -519,43 +519,80 @@ def cid_of_comp(eid: EntityID, comp: Component) -> ComponentID:
|
|
|
519
519
|
raise UnknownComponentError(f'Component {comp} not found in entity {eid}')
|
|
520
520
|
|
|
521
521
|
|
|
522
|
+
def _create_call_list(cids: tuple[ComponentID],
|
|
523
|
+
has_properties: _OptionalProperties) -> list[tuple[EntityID, Component]]:
|
|
524
|
+
at = tuple(cids)
|
|
525
|
+
if at not in archetype:
|
|
526
|
+
create_archetype(*cids)
|
|
527
|
+
|
|
528
|
+
adict = archetype[at]
|
|
529
|
+
# need to get call_list upfront, since kill_system could modify the dict
|
|
530
|
+
# Also: not running in the if clause is vastly faster than checking an
|
|
531
|
+
# empty set.
|
|
532
|
+
if has_properties:
|
|
533
|
+
property_filter = set(has_properties)
|
|
534
|
+
call_list = [(eid, *parms) for eid, parms in adict.items()
|
|
535
|
+
if property_filter <= plist[eid]]
|
|
536
|
+
else:
|
|
537
|
+
call_list = [(eid, *parms) for eid, parms in adict.items()]
|
|
538
|
+
return call_list
|
|
539
|
+
|
|
540
|
+
|
|
522
541
|
def run_system(dt: float,
|
|
523
|
-
|
|
542
|
+
fn: SystemFunction,
|
|
524
543
|
*cids: ComponentID,
|
|
525
544
|
has_properties: _OptionalProperties = None,
|
|
526
545
|
**kwargs: dict[str, Any]) -> _RunSystemResult:
|
|
527
546
|
"""Run the system for the matching cids.
|
|
528
547
|
|
|
529
548
|
:param dt: delta time since the last frame (miliseconds)
|
|
530
|
-
:param
|
|
549
|
+
:param fn: the actual system function
|
|
531
550
|
:param cids: the components to run on
|
|
532
551
|
:param has_properties: set of required properties
|
|
533
552
|
:return: A dictionary with entity IDs as key and the function result as value
|
|
534
553
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
554
|
+
For every entity that has all the listed components, ``fn`` is run. The
|
|
555
|
+
function prototype of ``fn`` is::
|
|
556
|
+
|
|
557
|
+
def callback(dt: float, eid: EntityID, *comps: Component) -> Any
|
|
558
|
+
|
|
559
|
+
The list can further be narrowed down my filtering for given properties.
|
|
539
560
|
|
|
540
561
|
This function is a direct call. Alternatively, you can use add_system
|
|
541
562
|
combined with run_all_systems or run_domain below.
|
|
542
563
|
"""
|
|
543
564
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
create_archetype(*cids)
|
|
565
|
+
call_list = _create_call_list(cids, has_properties)
|
|
566
|
+
return {eid: fn(dt, eid, *parms, **kwargs) for eid, *parms in call_list}
|
|
547
567
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
568
|
+
|
|
569
|
+
def run_bulk_system(dt: float,
|
|
570
|
+
fn: SystemFunction,
|
|
571
|
+
*cids: ComponentID,
|
|
572
|
+
has_properties: _OptionalProperties = None,
|
|
573
|
+
**kwargs: dict[str, Any]) -> Any:
|
|
574
|
+
"""Run the bulk system for all entities with matching cids.
|
|
575
|
+
|
|
576
|
+
:param dt: delta time since the last frame (miliseconds)
|
|
577
|
+
:param fn: the actual system function
|
|
578
|
+
:param cids: the components to run on
|
|
579
|
+
:param has_properties: set of required properties
|
|
580
|
+
:return: A dictionary with entity IDs as key and the function result as value
|
|
581
|
+
|
|
582
|
+
The fn is called with a list of entity ID and components tuples of all
|
|
583
|
+
entities that contain the listed components. The list can further be
|
|
584
|
+
narrowed down my filtering for given properties.
|
|
585
|
+
|
|
586
|
+
``fn`` is called given a list of ``(EntityID, (components,...))``. In
|
|
587
|
+
contrast to ``run_system``, the user is responsible to loop over the list.
|
|
588
|
+
|
|
589
|
+
The prototype of ``fn`` is::
|
|
590
|
+
|
|
591
|
+
def callback(dt: call_list: list[tuple[EntityID, list[Component]]]) -> Any
|
|
592
|
+
"""
|
|
593
|
+
|
|
594
|
+
call_list = _create_call_list(cids, has_properties)
|
|
595
|
+
return fn(dt, call_list, **kwargs)
|
|
559
596
|
|
|
560
597
|
|
|
561
598
|
def run_all_systems(dt: float) -> dict[SystemFunction, _RunSystemResult]:
|
|
@@ -568,8 +605,8 @@ def run_all_systems(dt: float) -> dict[SystemFunction, _RunSystemResult]:
|
|
|
568
605
|
appropriate components.
|
|
569
606
|
"""
|
|
570
607
|
|
|
571
|
-
return {
|
|
572
|
-
for
|
|
608
|
+
return {fn: run_system(dt, fn, *comps)
|
|
609
|
+
for fn, comps in sidx.items()}
|
|
573
610
|
|
|
574
611
|
|
|
575
612
|
def run_domain(dt: float, domain: DomainID) -> dict[SystemFunction, _RunSystemResult]:
|
|
@@ -584,14 +621,14 @@ def run_domain(dt: float, domain: DomainID) -> dict[SystemFunction, _RunSystemRe
|
|
|
584
621
|
if domain not in didx:
|
|
585
622
|
return {}
|
|
586
623
|
|
|
587
|
-
return {
|
|
588
|
-
for
|
|
624
|
+
return {fn: run_system(dt, fn, *sidx[fn])
|
|
625
|
+
for fn in didx[domain]}
|
|
589
626
|
|
|
590
627
|
|
|
591
628
|
def create_archetype(*cids: ComponentID) -> None:
|
|
592
629
|
"""Create an archetype from the provided cids.
|
|
593
630
|
|
|
594
|
-
:param cids The list of cids that define the archetype.
|
|
631
|
+
:param cids: The list of cids that define the archetype.
|
|
595
632
|
:return: None
|
|
596
633
|
|
|
597
634
|
An archetype is a fixed combination of components. Each time a component
|
|
@@ -672,8 +709,8 @@ def remove_from_archetype(eid: EntityID, cid: ComponentID | None = None) -> None
|
|
|
672
709
|
def comps_of_archetype(*cids: ComponentID, has_properties: _OptionalProperties = None) -> list[_EntityComponentsBundle]:
|
|
673
710
|
"""Return the given archetype.
|
|
674
711
|
|
|
675
|
-
:param cids The cids that define the archetype.
|
|
676
|
-
:param has_properties Optional set of required properties
|
|
712
|
+
:param cids: The cids that define the archetype.
|
|
713
|
+
:param has_properties: Optional set of required properties
|
|
677
714
|
:return: A list of tuples of (eid, components)
|
|
678
715
|
:raises UnknownArchetypeError: If the given archetype doesn't exist.
|
|
679
716
|
|
|
@@ -204,6 +204,11 @@ ecs.run_system(dt, deadzone_system, 'position', deadzone=WORLD)
|
|
|
204
204
|
|
|
205
205
|
So the system is basically the `update(dt)` function in an OO driven game.
|
|
206
206
|
|
|
207
|
+
If you prefer to loop over the list of entities yourself, instead of your
|
|
208
|
+
system being called for each entity individually, theren is `run_bulk_system`,
|
|
209
|
+
which gets the same arguments as ``run_system``, but will pass the full list
|
|
210
|
+
to the system.
|
|
211
|
+
|
|
207
212
|
At this point you might get the feeling, that you will have a very large list
|
|
208
213
|
of systems in a big block in your game loop, and that's exactly right. You
|
|
209
214
|
either hate that, which is fine, so the option is either to go back to an OO
|
|
@@ -175,6 +175,19 @@ def test_run_system():
|
|
|
175
175
|
assert worked
|
|
176
176
|
|
|
177
177
|
|
|
178
|
+
def test_run_bulk_system():
|
|
179
|
+
ecs.reset()
|
|
180
|
+
for i in range(10):
|
|
181
|
+
eid = ecs.create_entity()
|
|
182
|
+
ecs.add_component(eid, 'number', i)
|
|
183
|
+
|
|
184
|
+
def bulk_runner(dt, call_list):
|
|
185
|
+
assert len(call_list) == 10
|
|
186
|
+
return sum([i for eid, i in call_list])
|
|
187
|
+
|
|
188
|
+
assert ecs.run_bulk_system(0, bulk_runner, 'number') == 45
|
|
189
|
+
|
|
190
|
+
|
|
178
191
|
def test_run_all_systems():
|
|
179
192
|
e1, e2 = setup()
|
|
180
193
|
ecs.add_system(move_system, 'pos', 'velocity')
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|