tinyecs 0.3.6__tar.gz → 0.4.0__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.6/src/tinyecs.egg-info → tinyecs-0.4.0}/PKG-INFO +1 -1
- {tinyecs-0.3.6 → tinyecs-0.4.0}/pyproject.toml +1 -1
- {tinyecs-0.3.6 → tinyecs-0.4.0}/src/tinyecs/__init__.py +14 -20
- {tinyecs-0.3.6 → tinyecs-0.4.0}/src/tinyecs/compsys.py +12 -12
- {tinyecs-0.3.6 → tinyecs-0.4.0}/src/tinyecs/demos/background.py +4 -4
- {tinyecs-0.3.6 → tinyecs-0.4.0}/src/tinyecs/demos/bouncing_sprites.py +2 -2
- {tinyecs-0.3.6 → tinyecs-0.4.0}/src/tinyecs/demos/example.py +6 -6
- {tinyecs-0.3.6 → tinyecs-0.4.0}/src/tinyecs/demos/homing-missiles.py +14 -14
- {tinyecs-0.3.6 → tinyecs-0.4.0}/src/tinyecs/demos/marquee.py +2 -2
- {tinyecs-0.3.6 → tinyecs-0.4.0}/src/tinyecs/tutorial.py +37 -36
- {tinyecs-0.3.6 → tinyecs-0.4.0/src/tinyecs.egg-info}/PKG-INFO +1 -1
- {tinyecs-0.3.6 → tinyecs-0.4.0}/tests/test_tinyecs.py +19 -19
- {tinyecs-0.3.6 → tinyecs-0.4.0}/LICENSE +0 -0
- {tinyecs-0.3.6 → tinyecs-0.4.0}/README.rst +0 -0
- {tinyecs-0.3.6 → tinyecs-0.4.0}/setup.cfg +0 -0
- {tinyecs-0.3.6 → tinyecs-0.4.0}/src/tinyecs/components.py +0 -0
- {tinyecs-0.3.6 → tinyecs-0.4.0}/src/tinyecs/demo.py +0 -0
- {tinyecs-0.3.6 → tinyecs-0.4.0}/src/tinyecs/demos/__init__.py +0 -0
- {tinyecs-0.3.6 → tinyecs-0.4.0}/src/tinyecs.egg-info/SOURCES.txt +0 -0
- {tinyecs-0.3.6 → tinyecs-0.4.0}/src/tinyecs.egg-info/dependency_links.txt +0 -0
- {tinyecs-0.3.6 → tinyecs-0.4.0}/src/tinyecs.egg-info/entry_points.txt +0 -0
- {tinyecs-0.3.6 → tinyecs-0.4.0}/src/tinyecs.egg-info/requires.txt +0 -0
- {tinyecs-0.3.6 → tinyecs-0.4.0}/src/tinyecs.egg-info/top_level.txt +0 -0
|
@@ -24,7 +24,7 @@ type DomainID = Hashable
|
|
|
24
24
|
type Property = Hashable
|
|
25
25
|
type Component = object
|
|
26
26
|
|
|
27
|
-
type
|
|
27
|
+
type EntityComponentsBundle = tuple[EntityID, list[Component]]
|
|
28
28
|
type _OptionalProperties = Iterable[Property] | None
|
|
29
29
|
type _RunSystemResult = dict[EntityID, Any]
|
|
30
30
|
|
|
@@ -32,7 +32,7 @@ type _RunSystemResult = dict[EntityID, Any]
|
|
|
32
32
|
# The typehinting system can't work with functions using *args, **kwargs.
|
|
33
33
|
# This solution comes from https://docs.python.org/3/library/typing.html#annotating-callable-objects
|
|
34
34
|
class SystemFunction(Protocol):
|
|
35
|
-
def __call__(self,
|
|
35
|
+
def __call__(self, eid: EntityID, *cids: ComponentID, **kwargs: dict[str, object]) -> Any: ...
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
eidx = {} # entity index
|
|
@@ -480,7 +480,7 @@ def comp_of_eid(eid: EntityID, cid: ComponentID) -> Component:
|
|
|
480
480
|
return comps_of_eid(eid, cid)[0]
|
|
481
481
|
|
|
482
482
|
|
|
483
|
-
def eid_of_comp(comp: Component) -> set
|
|
483
|
+
def eid_of_comp(comp: Component) -> set[EntityID]:
|
|
484
484
|
"""Find the entity id for object comp.
|
|
485
485
|
|
|
486
486
|
:param comp: The component to find the entity of
|
|
@@ -538,14 +538,12 @@ def _create_call_list(cids: tuple[ComponentID],
|
|
|
538
538
|
return call_list
|
|
539
539
|
|
|
540
540
|
|
|
541
|
-
def run_system(
|
|
542
|
-
fn: SystemFunction,
|
|
541
|
+
def run_system(fn: SystemFunction,
|
|
543
542
|
*cids: ComponentID,
|
|
544
543
|
has_properties: _OptionalProperties = None,
|
|
545
544
|
**kwargs: dict[str, Any]) -> _RunSystemResult:
|
|
546
545
|
"""Run the system for the matching cids.
|
|
547
546
|
|
|
548
|
-
:param dt: delta time since the last frame (miliseconds)
|
|
549
547
|
:param fn: the actual system function
|
|
550
548
|
:param cids: the components to run on
|
|
551
549
|
:param has_properties: set of required properties
|
|
@@ -554,7 +552,7 @@ def run_system(dt: float,
|
|
|
554
552
|
For every entity that has all the listed components, ``fn`` is run. The
|
|
555
553
|
function prototype of ``fn`` is::
|
|
556
554
|
|
|
557
|
-
def callback(
|
|
555
|
+
def callback(eid: EntityID, *comps: Component) -> Any
|
|
558
556
|
|
|
559
557
|
The list can further be narrowed down my filtering for given properties.
|
|
560
558
|
|
|
@@ -563,17 +561,15 @@ def run_system(dt: float,
|
|
|
563
561
|
"""
|
|
564
562
|
|
|
565
563
|
call_list = _create_call_list(cids, has_properties)
|
|
566
|
-
return {eid: fn(
|
|
564
|
+
return {eid: fn(eid, *parms, **kwargs) for eid, *parms in call_list}
|
|
567
565
|
|
|
568
566
|
|
|
569
|
-
def run_bulk_system(
|
|
570
|
-
fn: SystemFunction,
|
|
567
|
+
def run_bulk_system(fn: SystemFunction,
|
|
571
568
|
*cids: ComponentID,
|
|
572
569
|
has_properties: _OptionalProperties = None,
|
|
573
570
|
**kwargs: dict[str, Any]) -> Any:
|
|
574
571
|
"""Run the bulk system for all entities with matching cids.
|
|
575
572
|
|
|
576
|
-
:param dt: delta time since the last frame (miliseconds)
|
|
577
573
|
:param fn: the actual system function
|
|
578
574
|
:param cids: the components to run on
|
|
579
575
|
:param has_properties: set of required properties
|
|
@@ -588,31 +584,29 @@ def run_bulk_system(dt: float,
|
|
|
588
584
|
|
|
589
585
|
The prototype of ``fn`` is::
|
|
590
586
|
|
|
591
|
-
def callback(
|
|
587
|
+
def callback(call_list: list[tuple[EntityID, list[Component]]]) -> Any
|
|
592
588
|
"""
|
|
593
589
|
|
|
594
590
|
call_list = _create_call_list(cids, has_properties)
|
|
595
|
-
return fn(
|
|
591
|
+
return fn(call_list, **kwargs) if call_list else None
|
|
596
592
|
|
|
597
593
|
|
|
598
|
-
def run_all_systems(
|
|
594
|
+
def run_all_systems(**kwargs) -> dict[SystemFunction, _RunSystemResult]:
|
|
599
595
|
"""Run all registered systems.
|
|
600
596
|
|
|
601
|
-
:param dt: Delta time
|
|
602
597
|
:return: A dict of function as key, and the result of run_system as value
|
|
603
598
|
|
|
604
599
|
This calls above run_system for all registered systems with their
|
|
605
600
|
appropriate components.
|
|
606
601
|
"""
|
|
607
602
|
|
|
608
|
-
return {fn: run_system(
|
|
603
|
+
return {fn: run_system(fn, *comps, **kwargs)
|
|
609
604
|
for fn, comps in sidx.items()}
|
|
610
605
|
|
|
611
606
|
|
|
612
|
-
def run_domain(
|
|
607
|
+
def run_domain(domain: DomainID, **kwargs) -> dict[SystemFunction, _RunSystemResult]:
|
|
613
608
|
"""Run all systems within domain.
|
|
614
609
|
|
|
615
|
-
:param dt: Delta time
|
|
616
610
|
:return: A dict of function as key, and the result of run_system as value
|
|
617
611
|
|
|
618
612
|
This is the same as run_all_systems, but limited to a specific domain.
|
|
@@ -621,7 +615,7 @@ def run_domain(dt: float, domain: DomainID) -> dict[SystemFunction, _RunSystemRe
|
|
|
621
615
|
if domain not in didx:
|
|
622
616
|
return {}
|
|
623
617
|
|
|
624
|
-
return {fn: run_system(
|
|
618
|
+
return {fn: run_system(fn, *sidx[fn], **kwargs)
|
|
625
619
|
for fn in didx[domain]}
|
|
626
620
|
|
|
627
621
|
|
|
@@ -706,7 +700,7 @@ def remove_from_archetype(eid: EntityID, cid: ComponentID | None = None) -> None
|
|
|
706
700
|
del adict[eid]
|
|
707
701
|
|
|
708
702
|
|
|
709
|
-
def comps_of_archetype(*cids: ComponentID, has_properties: _OptionalProperties = None) -> list[
|
|
703
|
+
def comps_of_archetype(*cids: ComponentID, has_properties: _OptionalProperties = None) -> list[EntityComponentsBundle]:
|
|
710
704
|
"""Return the given archetype.
|
|
711
705
|
|
|
712
706
|
:param cids: The cids that define the archetype.
|
|
@@ -249,12 +249,12 @@ class RSAImage:
|
|
|
249
249
|
self.update()
|
|
250
250
|
|
|
251
251
|
|
|
252
|
-
def dead_system(
|
|
252
|
+
def dead_system(eid, dead, **kwargs):
|
|
253
253
|
"""Reap entities marked with a `dead` component."""
|
|
254
254
|
ecs.remove_entity(eid)
|
|
255
255
|
|
|
256
256
|
|
|
257
|
-
def deadzone_system(
|
|
257
|
+
def deadzone_system(eid, world, position, *, container, **kwargs):
|
|
258
258
|
"""Kill entities moving outside defined boundaries
|
|
259
259
|
|
|
260
260
|
To avoid sprites flying off to infinity, a dead zone can be defined, that
|
|
@@ -284,7 +284,7 @@ def deadzone_system(dt, eid, world, position, *, container):
|
|
|
284
284
|
ecs.remove_entity(eid)
|
|
285
285
|
|
|
286
286
|
|
|
287
|
-
def extension_system(
|
|
287
|
+
def extension_system(eid, extension, **kwargs):
|
|
288
288
|
"""Add additional components.
|
|
289
289
|
|
|
290
290
|
Parameters:
|
|
@@ -318,29 +318,29 @@ def extension_system(dt, eid, extension):
|
|
|
318
318
|
ecs.remove_component(eid, 'extension')
|
|
319
319
|
|
|
320
320
|
|
|
321
|
-
def force_system(
|
|
321
|
+
def force_system(eid, force, momentum, *, dt, **kwargs):
|
|
322
322
|
"""Apply a force to a momentum"""
|
|
323
323
|
momentum += force * dt
|
|
324
324
|
|
|
325
325
|
|
|
326
|
-
def lifetime_system(
|
|
326
|
+
def lifetime_system(eid, lifetime, **kwargs):
|
|
327
327
|
"""Kill an entity after a specified time"""
|
|
328
328
|
if lifetime.cold():
|
|
329
329
|
ecs.remove_entity(eid)
|
|
330
330
|
|
|
331
331
|
|
|
332
|
-
def momentum_system(
|
|
332
|
+
def momentum_system(eid, momentum, position, *, dt):
|
|
333
333
|
"""Apply momentum to a position (a.k.a. "move")."""
|
|
334
334
|
position += momentum * dt
|
|
335
335
|
|
|
336
336
|
|
|
337
|
-
def mouse_system(
|
|
337
|
+
def mouse_system(eid, mouse, position):
|
|
338
338
|
"""Place the position of an entity to the mouse cursor."""
|
|
339
339
|
mp = pygame.mouse.get_pos()
|
|
340
340
|
position.xy = Vector2(mp)
|
|
341
341
|
|
|
342
342
|
|
|
343
|
-
def scale_system(
|
|
343
|
+
def scale_system(eid, scale, momentum, *, dt):
|
|
344
344
|
"""Apply friction to a momentum.
|
|
345
345
|
|
|
346
346
|
In contrast to a force, which adds a directional vector to the momentum,
|
|
@@ -353,7 +353,7 @@ def scale_system(dt, eid, scale, momentum):
|
|
|
353
353
|
friction_system = scale_system
|
|
354
354
|
|
|
355
355
|
|
|
356
|
-
def sprite_system(
|
|
356
|
+
def sprite_system(eid, sprite, position, **kwargs):
|
|
357
357
|
"""Set the rect.center of sprite to position."""
|
|
358
358
|
|
|
359
359
|
if sprite.rect:
|
|
@@ -362,7 +362,7 @@ def sprite_system(dt, eid, sprite, position):
|
|
|
362
362
|
sprite.rect = sprite.image.get_rect(center=position)
|
|
363
363
|
|
|
364
364
|
|
|
365
|
-
def wsad_system(
|
|
365
|
+
def wsad_system(eid, wsad, position, *, dt):
|
|
366
366
|
"""An example system to control a playear with the `wsad` keys.
|
|
367
367
|
|
|
368
368
|
Given the following entity:
|
|
@@ -374,7 +374,7 @@ def wsad_system(dt, eid, wsad, position):
|
|
|
374
374
|
|
|
375
375
|
running this system will modify the position component of the entity.
|
|
376
376
|
|
|
377
|
-
ecs.run_system(
|
|
377
|
+
ecs.run_system('wsad', 'position', 'player', dt=dt)
|
|
378
378
|
|
|
379
379
|
Parameters
|
|
380
380
|
----------
|
|
@@ -396,4 +396,4 @@ def wsad_system(dt, eid, wsad, position):
|
|
|
396
396
|
if keys[pygame.K_d]: v.x += 1
|
|
397
397
|
|
|
398
398
|
# Normalize so diagonals are not faster then axis motion
|
|
399
|
-
if v: position += v.normalize() * wsad
|
|
399
|
+
if v: position += v.normalize() * wsad
|
|
@@ -6,11 +6,11 @@ from types import SimpleNamespace
|
|
|
6
6
|
from pygame import Vector2
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def momentum_system(
|
|
9
|
+
def momentum_system(eid, momentum, position, *, dt):
|
|
10
10
|
position += momentum * dt
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
def border_system(
|
|
13
|
+
def border_system(eid, border, position, momentum, **kwargs):
|
|
14
14
|
if position.x < 0:
|
|
15
15
|
position.x = -position.x
|
|
16
16
|
momentum.x = -momentum.x
|
|
@@ -25,7 +25,7 @@ def border_system(dt, eid, border, position, momentum):
|
|
|
25
25
|
momentum.y = -momentum.y
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
def background_system(
|
|
28
|
+
def background_system(eid, background, position, surface, *, dt):
|
|
29
29
|
def draw_one(angle, offset):
|
|
30
30
|
v0 = Vector2(position)
|
|
31
31
|
v1 = Vector2(2000, 0).rotate(angle)
|
|
@@ -81,7 +81,7 @@ def main():
|
|
|
81
81
|
|
|
82
82
|
screen.fill('black')
|
|
83
83
|
|
|
84
|
-
ecs.run_all_systems(dt)
|
|
84
|
+
ecs.run_all_systems(dt=dt)
|
|
85
85
|
|
|
86
86
|
pygame.display.flip()
|
|
87
87
|
clock.tick(FPS)
|
|
@@ -14,7 +14,7 @@ class Sprite(pygame.sprite.Sprite):
|
|
|
14
14
|
self.rect = self.image.get_rect()
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
def bounding_box_system(
|
|
17
|
+
def bounding_box_system(eid, world, position, momentum, **kwargs):
|
|
18
18
|
if position.x < 0:
|
|
19
19
|
position.x = -position.x
|
|
20
20
|
momentum.x = -momentum.x
|
|
@@ -95,7 +95,7 @@ def main():
|
|
|
95
95
|
screen.fill('black')
|
|
96
96
|
|
|
97
97
|
group.update(dt)
|
|
98
|
-
ecs.run_all_systems(dt)
|
|
98
|
+
ecs.run_all_systems(dt=dt)
|
|
99
99
|
|
|
100
100
|
screen.blit(click, click_rect)
|
|
101
101
|
group.draw(screen)
|
|
@@ -25,7 +25,7 @@ class Velocity:
|
|
|
25
25
|
dy: float
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
def motion_system(
|
|
28
|
+
def motion_system(eid, position, velocity, *, dt):
|
|
29
29
|
# Note that assigning a new Vector to position would not make it back
|
|
30
30
|
# into e.position. But since we don't change the position instance
|
|
31
31
|
# itself, we're safe.
|
|
@@ -33,14 +33,14 @@ def motion_system(dt, eid, position, velocity):
|
|
|
33
33
|
position.y += velocity.dy * dt
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
def lifetime_system(
|
|
36
|
+
def lifetime_system(eid, lifetime, *, dt):
|
|
37
37
|
lifetime.time_left -= 1 * dt
|
|
38
38
|
if lifetime.time_left <= 0:
|
|
39
39
|
print(f'removing entity {eid}')
|
|
40
40
|
ecs.remove_entity(eid)
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
def render_system(
|
|
43
|
+
def render_system(eid, printable):
|
|
44
44
|
# the printable component contains no data at all and can be ignored,
|
|
45
45
|
# but it will still be passed as a parameter.
|
|
46
46
|
name, pos, velocity, lifetime = ecs.comps_of_eid(eid,
|
|
@@ -72,7 +72,7 @@ def main():
|
|
|
72
72
|
|
|
73
73
|
ecs.run_system(1, render_system, Comps.PRINTABLE)
|
|
74
74
|
for i in range(15):
|
|
75
|
-
ecs.run_system(1, motion_system, Comps.POSITION, Comps.VELOCITY)
|
|
75
|
+
ecs.run_system(1, motion_system, Comps.POSITION, Comps.VELOCITY, dt=1)
|
|
76
76
|
ecs.run_system(1, lifetime_system, Comps.LIFETIME)
|
|
77
77
|
ecs.run_system(1, render_system, Comps.PRINTABLE)
|
|
78
78
|
|
|
@@ -105,8 +105,8 @@ def main():
|
|
|
105
105
|
|
|
106
106
|
for i in range(15):
|
|
107
107
|
# ecs.run_all_systems(1)
|
|
108
|
-
ecs.run_domain(
|
|
109
|
-
ecs.run_domain(
|
|
108
|
+
ecs.run_domain('updates', dt=1)
|
|
109
|
+
ecs.run_domain('render')
|
|
110
110
|
|
|
111
111
|
print(f'All entities: {ecs.eidx}')
|
|
112
112
|
print(f'All components: {ecs.cidx}')
|
|
@@ -10,12 +10,12 @@ from pgcooldown import Cooldown
|
|
|
10
10
|
from pygame import Vector2
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
def dead_sprite_system(
|
|
13
|
+
def dead_sprite_system(eid, dead, sprite):
|
|
14
14
|
sprite.kill()
|
|
15
15
|
ecs.remove_entity(eid)
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
def lifetime_sprite_system(
|
|
18
|
+
def lifetime_sprite_system(eid, lifetime, sprite):
|
|
19
19
|
if lifetime.hot():
|
|
20
20
|
return
|
|
21
21
|
sprite.kill()
|
|
@@ -54,7 +54,7 @@ def create_shard(position, image, *groups):
|
|
|
54
54
|
ecs.add_component(e, 'lifetime', Cooldown(random() * 0.25))
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
def homing_missile_system(
|
|
57
|
+
def homing_missile_system(eid, homing_missile, position, momentum, *, dt):
|
|
58
58
|
target_pos = ecs.comp_of_eid(homing_missile.target, 'position')
|
|
59
59
|
|
|
60
60
|
target = target_pos
|
|
@@ -88,14 +88,14 @@ def homing_missile_system(dt, eid, homing_missile, position, momentum):
|
|
|
88
88
|
momentum.rotate_ip(copysign(rotation, delta_phi))
|
|
89
89
|
|
|
90
90
|
|
|
91
|
-
def fire_system(
|
|
91
|
+
def fire_system(eid, fire, position):
|
|
92
92
|
for i in range(fire.count):
|
|
93
93
|
fire.fkt(pos=position)
|
|
94
94
|
|
|
95
95
|
ecs.remove_component(eid, 'fire')
|
|
96
96
|
|
|
97
97
|
|
|
98
|
-
def bounding_box_system(
|
|
98
|
+
def bounding_box_system(eid, world, position, momentum):
|
|
99
99
|
if position.x < 0:
|
|
100
100
|
position.x = -position.x
|
|
101
101
|
momentum.x = -momentum.x
|
|
@@ -206,15 +206,15 @@ def main():
|
|
|
206
206
|
|
|
207
207
|
screen.fill('black')
|
|
208
208
|
|
|
209
|
-
ecs.run_system(
|
|
210
|
-
ecs.run_system(
|
|
211
|
-
ecs.run_system(
|
|
212
|
-
ecs.run_system(
|
|
213
|
-
ecs.run_system(
|
|
214
|
-
ecs.run_system(
|
|
215
|
-
ecs.run_system(
|
|
216
|
-
ecs.run_system(
|
|
217
|
-
ecs.run_system(
|
|
209
|
+
ecs.run_system(ecsc.mouse_system, 'mouse', 'position')
|
|
210
|
+
ecs.run_system(lifetime_sprite_system, 'lifetime', 'sprite')
|
|
211
|
+
ecs.run_system(dead_sprite_system, 'dead', 'sprite')
|
|
212
|
+
ecs.run_system(ecsc.dead_system, 'dead')
|
|
213
|
+
ecs.run_system(ecsc.momentum_system, 'momentum', 'position', dt=dt)
|
|
214
|
+
ecs.run_system(ecsc.sprite_system, 'sprite', 'position')
|
|
215
|
+
ecs.run_system(bounding_box_system, 'world', 'position', 'momentum')
|
|
216
|
+
ecs.run_system(homing_missile_system, 'homing_missile', 'position', 'momentum', dt=dt)
|
|
217
|
+
ecs.run_system(fire_system, 'fire', 'position')
|
|
218
218
|
|
|
219
219
|
screen.blit(click, click_rect)
|
|
220
220
|
group.draw(screen)
|
|
@@ -10,7 +10,7 @@ from pgcooldown import Cooldown
|
|
|
10
10
|
from pygame import Vector2
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
def marquee_system(
|
|
13
|
+
def marquee_system(eid, marquee, border, surface, *, dt):
|
|
14
14
|
def bounce(point, momentum):
|
|
15
15
|
point += momentum * dt
|
|
16
16
|
if point.x < 0:
|
|
@@ -80,7 +80,7 @@ def main():
|
|
|
80
80
|
|
|
81
81
|
screen.fill(BLACK)
|
|
82
82
|
|
|
83
|
-
ecs.run_system(
|
|
83
|
+
ecs.run_system(marquee_system, 'marquee', 'border', 'surface', dt=dt)
|
|
84
84
|
|
|
85
85
|
pygame.display.flip()
|
|
86
86
|
clock.tick(FPS)
|
|
@@ -173,17 +173,18 @@ will be used. To run e.g. the `momentum_system`, put the following into the
|
|
|
173
173
|
game loop:
|
|
174
174
|
|
|
175
175
|
``py
|
|
176
|
-
ecs.run_system(
|
|
176
|
+
ecs.run_system(momentum_system, 'momentum', 'position', dt=deltatime)
|
|
177
177
|
```
|
|
178
178
|
|
|
179
179
|
When the game loop passes this call, `run_system` will find all entities that
|
|
180
180
|
have both components `momentum` and `position` and pass these together with
|
|
181
|
-
|
|
181
|
+
the entity ID into the `momentum_system` function. Remember, that kwargs can
|
|
182
|
+
be used to pass additional arguments from the main scope.
|
|
182
183
|
|
|
183
184
|
Writing the `momentum_system` is easy:
|
|
184
185
|
|
|
185
186
|
```py
|
|
186
|
-
def momentum_system(
|
|
187
|
+
def momentum_system(eid, momentum, position, *, dt):
|
|
187
188
|
position += momentum * dt
|
|
188
189
|
```
|
|
189
190
|
|
|
@@ -199,7 +200,7 @@ passed in after the components. The following will be explained in the actual
|
|
|
199
200
|
script later.
|
|
200
201
|
|
|
201
202
|
```py
|
|
202
|
-
ecs.run_system(
|
|
203
|
+
ecs.run_system(deadzone_system, 'position', deadzone=WORLD)
|
|
203
204
|
```
|
|
204
205
|
|
|
205
206
|
So the system is basically the `update(dt)` function in an OO driven game.
|
|
@@ -221,7 +222,7 @@ There are some alternatives still.
|
|
|
221
222
|
program and call a single function in your game loop:
|
|
222
223
|
|
|
223
224
|
```py
|
|
224
|
-
ecs.run_all_systems(dt)
|
|
225
|
+
ecs.run_all_systems(dt=dt)
|
|
225
226
|
```
|
|
226
227
|
|
|
227
228
|
2. To give you a more fine grained control over what systems run together, the
|
|
@@ -241,7 +242,7 @@ method of your component. You still need that block of `run_system` or above
|
|
|
241
242
|
shortcuts in your game loop though.
|
|
242
243
|
|
|
243
244
|
```py
|
|
244
|
-
def call_update_system(
|
|
245
|
+
def call_update_system(eid, component, dt=dt):
|
|
245
246
|
component.update(dt)
|
|
246
247
|
```
|
|
247
248
|
|
|
@@ -364,15 +365,15 @@ completeness:
|
|
|
364
365
|
# addition.
|
|
365
366
|
WORLD = SCREEN.scale_by(1.25)
|
|
366
367
|
|
|
367
|
-
def momentum_system(
|
|
368
|
+
def momentum_system(eid, momentum, position, *, dt):
|
|
368
369
|
"""Add a delta time scaled momentum to the position."""
|
|
369
370
|
position += momentum * dt
|
|
370
371
|
|
|
371
|
-
def sprite_position_system(
|
|
372
|
+
def sprite_position_system(eid, sprite, position):
|
|
372
373
|
"""Apply the position to the rect of the sprite for the sprite group"""
|
|
373
374
|
sprite.rect.center = position
|
|
374
375
|
|
|
375
|
-
def deadzone_system(
|
|
376
|
+
def deadzone_system(eid, position, *, world):
|
|
376
377
|
"""Kill sprites that move off screen"""
|
|
377
378
|
if world.collidepoint(position):
|
|
378
379
|
return
|
|
@@ -396,7 +397,7 @@ to the entity instead of overwriting the component variable.
|
|
|
396
397
|
|
|
397
398
|
.. code-block::
|
|
398
399
|
|
|
399
|
-
def take_damage_system(
|
|
400
|
+
def take_damage_system(eid, health, damage):
|
|
400
401
|
ecs.add_component(eid, 'health', health - damage)
|
|
401
402
|
|
|
402
403
|
For semantic reasons, tinyecs also provides `update_component`, but that's
|
|
@@ -430,9 +431,9 @@ The lines marked with `>` are additions to the game loop template all above.
|
|
|
430
431
|
#### Running the systems
|
|
431
432
|
|
|
432
433
|
```py
|
|
433
|
-
> ecs.run_system(
|
|
434
|
-
> ecs.run_system(
|
|
435
|
-
> ecs.run_system(
|
|
434
|
+
> ecs.run_system(momentum_system, 'momentum', 'position', dt=dt)
|
|
435
|
+
> ecs.run_system(deadzone_system, 'position', world=WORLD)
|
|
436
|
+
> ecs.run_system(sprite_position_system, 'sprite', 'position')
|
|
436
437
|
|
|
437
438
|
screen.fill('black')
|
|
438
439
|
|
|
@@ -499,17 +500,17 @@ def create_box_entity(position, sprite_group):
|
|
|
499
500
|
ecs.add_component(e, 'sprite', DemoSprite(sprite_group))
|
|
500
501
|
|
|
501
502
|
|
|
502
|
-
def momentum_system(
|
|
503
|
+
def momentum_system(eid, momentum, position, *, dt):
|
|
503
504
|
"""Add a delta time scaled momentum to the position."""
|
|
504
505
|
position += momentum * dt
|
|
505
506
|
|
|
506
507
|
|
|
507
|
-
def sprite_position_system(
|
|
508
|
+
def sprite_position_system(eid, sprite, position):
|
|
508
509
|
"""Apply the position to the rect of the sprite for the sprite group"""
|
|
509
510
|
sprite.rect.center = position
|
|
510
511
|
|
|
511
512
|
|
|
512
|
-
def deadzone_system(
|
|
513
|
+
def deadzone_system(eid, position, *, world):
|
|
513
514
|
"""Kill sprites that move off screen"""
|
|
514
515
|
if world.collidepoint(position):
|
|
515
516
|
return
|
|
@@ -538,9 +539,9 @@ while running:
|
|
|
538
539
|
pos = pygame.mouse.get_pos()
|
|
539
540
|
create_box_entity(pos, group)
|
|
540
541
|
|
|
541
|
-
ecs.run_system(
|
|
542
|
-
ecs.run_system(
|
|
543
|
-
ecs.run_system(
|
|
542
|
+
ecs.run_system(momentum_system, 'momentum', 'position', dt=dt)
|
|
543
|
+
ecs.run_system(deadzone_system, 'position', world=WORLD)
|
|
544
|
+
ecs.run_system(sprite_position_system, 'sprite', 'position')
|
|
544
545
|
|
|
545
546
|
screen.fill('black')
|
|
546
547
|
|
|
@@ -590,12 +591,12 @@ import tinyecs.compsys as ecsc
|
|
|
590
591
|
|
|
591
592
|
UNSTABLE! DON'T RELY ON THIS YET!
|
|
592
593
|
|
|
593
|
-
`def dead_system(
|
|
594
|
+
`def dead_system(eid, dead)`:
|
|
594
595
|
Sometimes it is useful to not remove a sprite immediately from the system.
|
|
595
596
|
Instead, you can add a component tagged e.g. 'dead', and later reap all
|
|
596
597
|
entities marked with that tag.
|
|
597
598
|
|
|
598
|
-
`def deadzone_system(
|
|
599
|
+
`def deadzone_system(eid, world, position, *, container)`:
|
|
599
600
|
|
|
600
601
|
Basically the function created in this tutorial, with one addition.
|
|
601
602
|
|
|
@@ -608,25 +609,25 @@ import tinyecs.compsys as ecsc
|
|
|
608
609
|
|
|
609
610
|
ecs.add_component(e, 'world', True)
|
|
610
611
|
|
|
611
|
-
`def extension_system(
|
|
612
|
+
`def extension_system(eid, extension)`:
|
|
612
613
|
Will be removed. tinyecs installs pgcooldown as a requirement and that
|
|
613
614
|
comes with the `CronD` class which does the same and more.
|
|
614
615
|
|
|
615
|
-
`def force_system(
|
|
616
|
+
`def force_system(eid, force, momentum, *, dt)`:
|
|
616
617
|
Applies (adds) a constant force to a momentum.
|
|
617
618
|
|
|
618
|
-
`def lifetime_system(
|
|
619
|
+
`def lifetime_system(eid, lifetime)`:
|
|
619
620
|
Kills the entity once lifetime has run out. Expects `lifetime` to be an
|
|
620
621
|
instance of `pgcooldown.Cooldown`
|
|
621
622
|
|
|
622
|
-
`def momentum_system(
|
|
623
|
+
`def momentum_system(eid, momentum, position, *, dt)`:
|
|
623
624
|
The same as we wrote in the tutorial above.
|
|
624
625
|
|
|
625
|
-
`def mouse_system(
|
|
626
|
+
`def mouse_system(eid, mouse, position)`:
|
|
626
627
|
Update a position component with the position of the mouse cursor.
|
|
627
628
|
|
|
628
|
-
`def scale_system(
|
|
629
|
-
`def friction_system(
|
|
629
|
+
`def scale_system(eid, scale, momentum, *, dt)`:
|
|
630
|
+
`def friction_system(eid, scale, momentum, *, dt)`:
|
|
630
631
|
Apply friction to a momentum.
|
|
631
632
|
|
|
632
633
|
In contrast to a force_system, which adds a directional vector to the
|
|
@@ -635,10 +636,10 @@ import tinyecs.compsys as ecsc
|
|
|
635
636
|
|
|
636
637
|
`friction_system` is an alias to `scale_system`.
|
|
637
638
|
|
|
638
|
-
`def sprite_system(
|
|
639
|
+
`def sprite_system(eid, sprite, position)`:
|
|
639
640
|
The same as we wrote above, apply the position to `sprite.rect.center`.
|
|
640
641
|
|
|
641
|
-
`def wsad_system(
|
|
642
|
+
`def wsad_system(eid, wsad, position)`:
|
|
642
643
|
An example system to control a playear with the `wsad` keys.
|
|
643
644
|
This was just a proof of concept, but I'm currently using it, so perhaps
|
|
644
645
|
it will stay.
|
|
@@ -695,17 +696,17 @@ def create_box_entity(position, sprite_group):
|
|
|
695
696
|
ecs.add_component(e, 'sprite', DemoSprite(sprite_group))
|
|
696
697
|
|
|
697
698
|
|
|
698
|
-
def momentum_system(
|
|
699
|
+
def momentum_system(eid, momentum, position, *, dt):
|
|
699
700
|
"""Add a delta time scaled momentum to the position."""
|
|
700
701
|
position += momentum * dt
|
|
701
702
|
|
|
702
703
|
|
|
703
|
-
def sprite_position_system(
|
|
704
|
+
def sprite_position_system(eid, sprite, position):
|
|
704
705
|
"""Apply the position to the rect of the sprite for the sprite group"""
|
|
705
706
|
sprite.rect.center = position
|
|
706
707
|
|
|
707
708
|
|
|
708
|
-
def deadzone_system(
|
|
709
|
+
def deadzone_system(eid, position, *, world):
|
|
709
710
|
"""Kill sprites that move off screen"""
|
|
710
711
|
if world.collidepoint(position):
|
|
711
712
|
return
|
|
@@ -735,9 +736,9 @@ def main():
|
|
|
735
736
|
pos = pygame.mouse.get_pos()
|
|
736
737
|
create_box_entity(pos, group)
|
|
737
738
|
|
|
738
|
-
ecs.run_system(
|
|
739
|
-
ecs.run_system(
|
|
740
|
-
ecs.run_system(
|
|
739
|
+
ecs.run_system(momentum_system, 'momentum', 'position', dt=dt)
|
|
740
|
+
ecs.run_system(deadzone_system, 'position', world=WORLD)
|
|
741
|
+
ecs.run_system(sprite_position_system, 'sprite', 'position')
|
|
741
742
|
|
|
742
743
|
screen.fill('black')
|
|
743
744
|
|
|
@@ -28,20 +28,20 @@ class Ping:
|
|
|
28
28
|
ack: bool = False
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
def wounding_system(
|
|
31
|
+
def wounding_system(eid, health, **kwargs):
|
|
32
32
|
health.health -= 100
|
|
33
33
|
return health.health
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
def stats_system(
|
|
37
|
-
return f'{
|
|
36
|
+
def stats_system(eid, name, health, **kwargs):
|
|
37
|
+
return f'{eid=}, {name.name=}, {health.health=}'
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
def ping_system(
|
|
40
|
+
def ping_system(eid, ping):
|
|
41
41
|
ping.ack = True
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
def move_system(
|
|
44
|
+
def move_system(eid, pos, velocity, *, dt, **kwargs):
|
|
45
45
|
pos.x += velocity.dx * dt
|
|
46
46
|
pos.y += velocity.dy * dt
|
|
47
47
|
return (pos.x, pos.y)
|
|
@@ -161,17 +161,17 @@ def test_comps_of_eid():
|
|
|
161
161
|
|
|
162
162
|
def test_run_system():
|
|
163
163
|
e1, e2 = setup()
|
|
164
|
-
ecs.run_system(
|
|
164
|
+
ecs.run_system(wounding_system, 'health')
|
|
165
165
|
assert ecs.eidx[e2]['health'].health == 900
|
|
166
166
|
assert ecs.cidx['health'][e2].health == 900
|
|
167
167
|
|
|
168
168
|
worked = False
|
|
169
169
|
|
|
170
|
-
def sys_no_args(
|
|
170
|
+
def sys_no_args(eid):
|
|
171
171
|
nonlocal worked
|
|
172
172
|
worked = True
|
|
173
173
|
|
|
174
|
-
ecs.run_system(
|
|
174
|
+
ecs.run_system(sys_no_args)
|
|
175
175
|
assert worked
|
|
176
176
|
|
|
177
177
|
|
|
@@ -181,11 +181,11 @@ def test_run_bulk_system():
|
|
|
181
181
|
eid = ecs.create_entity()
|
|
182
182
|
ecs.add_component(eid, 'number', i)
|
|
183
183
|
|
|
184
|
-
def bulk_runner(
|
|
184
|
+
def bulk_runner(call_list):
|
|
185
185
|
assert len(call_list) == 10
|
|
186
186
|
return sum([i for eid, i in call_list])
|
|
187
187
|
|
|
188
|
-
assert ecs.run_bulk_system(
|
|
188
|
+
assert ecs.run_bulk_system(bulk_runner, 'number') == 45
|
|
189
189
|
|
|
190
190
|
|
|
191
191
|
def test_run_all_systems():
|
|
@@ -194,7 +194,7 @@ def test_run_all_systems():
|
|
|
194
194
|
ecs.add_system(wounding_system, 'health')
|
|
195
195
|
ecs.add_component(e2, 'pos', SimpleNamespace(x=100, y=200))
|
|
196
196
|
ecs.add_component(e2, 'velocity', SimpleNamespace(dx=5, dy=7))
|
|
197
|
-
res = ecs.run_all_systems(1)
|
|
197
|
+
res = ecs.run_all_systems(dt=1)
|
|
198
198
|
|
|
199
199
|
assert ecs.eidx[e2]['pos'] == SimpleNamespace(x=105, y=207)
|
|
200
200
|
assert res[move_system][e2] == (105, 207)
|
|
@@ -274,7 +274,7 @@ def test_reset():
|
|
|
274
274
|
|
|
275
275
|
|
|
276
276
|
def test_kill_from_system():
|
|
277
|
-
def kill_system(
|
|
277
|
+
def kill_system(eid, kill):
|
|
278
278
|
ecs.remove_entity(eid)
|
|
279
279
|
|
|
280
280
|
ecs.reset()
|
|
@@ -286,7 +286,7 @@ def test_kill_from_system():
|
|
|
286
286
|
ecs.add_component(e, 'kill', True)
|
|
287
287
|
|
|
288
288
|
assert len(ecs.eidx) == 10
|
|
289
|
-
ecs.run_system(
|
|
289
|
+
ecs.run_system(kill_system, 'kill')
|
|
290
290
|
assert len(ecs.eidx) == 5
|
|
291
291
|
|
|
292
292
|
|
|
@@ -318,11 +318,11 @@ def test_run_domain():
|
|
|
318
318
|
e = ecs.create_entity()
|
|
319
319
|
ecs.add_component(e, 'ping', Ping(False))
|
|
320
320
|
|
|
321
|
-
res = ecs.run_domain(
|
|
321
|
+
res = ecs.run_domain('infra')
|
|
322
322
|
assert ping_system in res
|
|
323
323
|
assert e in res[ping_system]
|
|
324
324
|
|
|
325
|
-
res = ecs.run_domain(
|
|
325
|
+
res = ecs.run_domain('non-existent')
|
|
326
326
|
assert res == {}
|
|
327
327
|
|
|
328
328
|
|
|
@@ -417,12 +417,12 @@ def test_add_to_archetype():
|
|
|
417
417
|
# Also tests remove_from_archetype
|
|
418
418
|
setup()
|
|
419
419
|
|
|
420
|
-
def test_system(
|
|
420
|
+
def test_system(eid, test):
|
|
421
421
|
pass
|
|
422
422
|
|
|
423
423
|
e = ecs.create_entity()
|
|
424
424
|
ecs.add_component(e, 'test', True)
|
|
425
|
-
ecs.run_system(
|
|
425
|
+
ecs.run_system(test_system, 'test')
|
|
426
426
|
assert e in ecs.archetype[('test',)]
|
|
427
427
|
|
|
428
428
|
ecs.remove_component(e, 'test')
|
|
@@ -470,10 +470,10 @@ def test_property_get_entities():
|
|
|
470
470
|
def test_property_run_system():
|
|
471
471
|
setup_property_tests()
|
|
472
472
|
|
|
473
|
-
def flag_system(
|
|
473
|
+
def flag_system(eid, comp1, comp2):
|
|
474
474
|
ecs.add_component(eid, 'flag', True)
|
|
475
475
|
|
|
476
|
-
ecs.run_system(
|
|
476
|
+
ecs.run_system(flag_system, 'comp-1', 'comp-2', has_properties={'is-even'})
|
|
477
477
|
assert len(ecs.eids_by_cids('flag')) == 5
|
|
478
478
|
|
|
479
479
|
|
|
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
|