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.
Files changed (23) hide show
  1. {tinyecs-0.3.5/src/tinyecs.egg-info → tinyecs-0.3.6}/PKG-INFO +1 -1
  2. {tinyecs-0.3.5 → tinyecs-0.3.6}/pyproject.toml +1 -1
  3. {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/__init__.py +72 -35
  4. {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/tutorial.py +5 -0
  5. {tinyecs-0.3.5 → tinyecs-0.3.6/src/tinyecs.egg-info}/PKG-INFO +1 -1
  6. {tinyecs-0.3.5 → tinyecs-0.3.6}/tests/test_tinyecs.py +13 -0
  7. {tinyecs-0.3.5 → tinyecs-0.3.6}/LICENSE +0 -0
  8. {tinyecs-0.3.5 → tinyecs-0.3.6}/README.rst +0 -0
  9. {tinyecs-0.3.5 → tinyecs-0.3.6}/setup.cfg +0 -0
  10. {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/components.py +0 -0
  11. {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/compsys.py +0 -0
  12. {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/demo.py +0 -0
  13. {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/demos/__init__.py +0 -0
  14. {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/demos/background.py +0 -0
  15. {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/demos/bouncing_sprites.py +0 -0
  16. {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/demos/example.py +0 -0
  17. {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/demos/homing-missiles.py +0 -0
  18. {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs/demos/marquee.py +0 -0
  19. {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs.egg-info/SOURCES.txt +0 -0
  20. {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs.egg-info/dependency_links.txt +0 -0
  21. {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs.egg-info/entry_points.txt +0 -0
  22. {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs.egg-info/requires.txt +0 -0
  23. {tinyecs-0.3.5 → tinyecs-0.3.6}/src/tinyecs.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tinyecs
3
- Version: 0.3.5
3
+ Version: 0.3.6
4
4
  Summary: The teeniest, tiniest ECS system
5
5
  Author-email: Michael Lamertz <michael.lamertz@gmail.com>
6
6
  License-Expression: MIT
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "tinyecs"
3
3
  description = "The teeniest, tiniest ECS system"
4
- version = "0.3.5"
4
+ version = "0.3.6"
5
5
  readme = "README.rst"
6
6
 
7
7
  authors = [
@@ -282,16 +282,16 @@ def remove_component(eid: EntityID, *cids: ComponentID) -> None:
282
282
  obj.shutdown_()
283
283
 
284
284
 
285
- def add_system(fkt: SystemFunction, *cids: ComponentID) -> None:
285
+ def add_system(fn: SystemFunction, *cids: ComponentID) -> None:
286
286
  r"""Add a system for the specifiied cids.
287
287
 
288
- :param fkt: The system function
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
- fkt(delta_time, eid, *comps)
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[fkt] = cids
307
+ sidx[fn] = cids
308
308
 
309
309
 
310
- def remove_system(fkt: SystemFunction) -> None:
310
+ def remove_system(fn: SystemFunction) -> None:
311
311
  """Remove the given function from the registry.
312
312
 
313
- :param fkt: The system function
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, fkt)
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[fkt]
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
- fkt: SystemFunction,
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 fkt: the actual system function
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
- The fkt gets the list of all entities that contain the listed
536
- components. The list can further be narrowed down my filtering for given
537
- properties. Then it runs the function for every entity and the requested
538
- components, passing dt as heartbeat.
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
- at = tuple(cids)
545
- if at not in archetype:
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
- adict = archetype[at]
549
- # need to get call_list upfront, since kill_system could modify the dict
550
- # Also: not running in the if clause is vastly faster than checking an
551
- # empty set.
552
- if has_properties:
553
- property_filter = set(has_properties)
554
- call_list = [(eid, *parms) for eid, parms in adict.items()
555
- if property_filter <= plist[eid]]
556
- else:
557
- call_list = [(eid, *parms) for eid, parms in adict.items()]
558
- return {eid: fkt(dt, eid, *parms, **kwargs) for eid, *parms in call_list}
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 {fkt: run_system(dt, fkt, *comps)
572
- for fkt, comps in sidx.items()}
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 {fkt: run_system(dt, fkt, *sidx[fkt])
588
- for fkt in didx[domain]}
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tinyecs
3
- Version: 0.3.5
3
+ Version: 0.3.6
4
4
  Summary: The teeniest, tiniest ECS system
5
5
  Author-email: Michael Lamertz <michael.lamertz@gmail.com>
6
6
  License-Expression: MIT
@@ -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