microecs 0.2.3__tar.gz → 0.2.4__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.
- {microecs-0.2.3 → microecs-0.2.4}/PKG-INFO +17 -16
- {microecs-0.2.3 → microecs-0.2.4}/README.md +16 -15
- {microecs-0.2.3 → microecs-0.2.4}/microecs/__init__.py +1 -2
- {microecs-0.2.3 → microecs-0.2.4}/microecs/world.py +9 -3
- {microecs-0.2.3 → microecs-0.2.4}/microecs.egg-info/PKG-INFO +17 -16
- {microecs-0.2.3 → microecs-0.2.4}/microecs.egg-info/SOURCES.txt +0 -1
- {microecs-0.2.3 → microecs-0.2.4}/setup.py +1 -1
- microecs-0.2.3/microecs/system.py +0 -10
- {microecs-0.2.3 → microecs-0.2.4}/LICENSE.TXT +0 -0
- {microecs-0.2.3 → microecs-0.2.4}/microecs/component.py +0 -0
- {microecs-0.2.3 → microecs-0.2.4}/microecs/pool.py +0 -0
- {microecs-0.2.3 → microecs-0.2.4}/microecs/query_result.py +0 -0
- {microecs-0.2.3 → microecs-0.2.4}/microecs/utils.py +0 -0
- {microecs-0.2.3 → microecs-0.2.4}/microecs.egg-info/dependency_links.txt +0 -0
- {microecs-0.2.3 → microecs-0.2.4}/microecs.egg-info/requires.txt +0 -0
- {microecs-0.2.3 → microecs-0.2.4}/microecs.egg-info/top_level.txt +0 -0
- {microecs-0.2.3 → microecs-0.2.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: microecs
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: MicroECS: Minimal Entity Component System (ECS) in python and numpy
|
|
5
5
|
Home-page: https://gitlab.com/meehai/microecs
|
|
6
6
|
License: MIT
|
|
@@ -20,25 +20,26 @@ Usage:
|
|
|
20
20
|
- Sandbox: `python main.py`
|
|
21
21
|
- Tests: `pytest test/`
|
|
22
22
|
|
|
23
|
-
There are only
|
|
23
|
+
There are only 4 primitives (bottom up): `Component`, `Pool`, `QueryResult`, `World`:
|
|
24
24
|
|
|
25
|
-
- `Component` is a simple python dataclass holding only data. All entries must be numpy arrays with metadata fields: shape and dtype. We support 5 dtypes only: `int32`, `float32`, `bool`, `str` and `object`.
|
|
25
|
+
- `Component` is a simple python dataclass holding only data. All entries must be numpy arrays with metadata fields: shape and dtype. We support 5 dtypes only: `int32`, `float32`, `bool`, `str` and `object`. A component with no fields is a valid **tag** for querying (e.g. `class Frozen(Component): pass`).
|
|
26
26
|
- `Pool` is a simple 'archetype' dynamic array, holding entities of the same type (same set of components). Usses `Components` metadata to construct contiguous arrays for all entities of the same type.
|
|
27
27
|
- `QueryResult` is a list of pools that match some query on all the entities of the `World`. It acts as a contiguous numpy-like container that implements numpy's `__array_function__` and `__array_ufunc__`. For all intents and purposes it should feel like a `(N, ...)` view over all the entities. If you need a numpy array (not all ops are supported, for e.g. indexing on the first axis), use `QueryResult.numpy()`. It also exposes `entity_ids`: a flat `(N,)` array of the matched entities' ids, in the same pool-by-pool order as the fields, so you can `zip(qr.entity_ids, qr.position)` or feed an id back to `world.get_entity` / `world.remove_entity`.
|
|
28
|
-
- `World` is a manager of `Pools` and has an overview of all the entities in the scene. It also manages the migration of entities from one pool to the other.
|
|
29
|
-
- `System` is an abstract class that queries the `World` for a subset of `Pools` matching some components. It updates the entities in these pools given some logic (e.g. collisions, motion physics or simply calls the drawing functions). They are merely a convention, not tied to `World` per se.
|
|
28
|
+
- `World` is a manager of `Pools` and has an overview of all the entities in the scene. It also manages the migration of entities from one pool to the other. A `World` can also require extra metadata keys on every field via `World(extra_field_metadata=["serializable"])`, to enforce component-level behavior such as field serialization.
|
|
30
29
|
|
|
31
30
|
Few relevant concepts:
|
|
32
31
|
|
|
33
32
|
- `Pool` operates on array indices, while `World` operates on entity IDs (also integers). This allows seamless movement between pools while the high-level systems still working as intended.
|
|
34
33
|
- All mutable operations on `World` are lazy. These are: `add_entity`, `remove_entity`, `add_component`, `remove_component`. They are added to a command buffer which is only executed when calling `world.update()`.
|
|
34
|
+
- `Systems` are a convention, they are not part of this library. They can be defined at application level and act as hooks or callbacks. The `World` object doesn't need to know more than entities and components.
|
|
35
35
|
|
|
36
36
|
Super simplified main loop structure:
|
|
37
37
|
|
|
38
38
|
```python
|
|
39
|
+
from typing import Callable
|
|
39
40
|
import numpy as np
|
|
40
41
|
import raylib as rl
|
|
41
|
-
from microecs import World, Component
|
|
42
|
+
from microecs import World, Component
|
|
42
43
|
|
|
43
44
|
# components
|
|
44
45
|
class HasPosition(Component):
|
|
@@ -48,15 +49,15 @@ class HasVelocity(Component):
|
|
|
48
49
|
class HasColor(Component):
|
|
49
50
|
color: np.ndarray = field(metadata={"shape": (4, ), "dtype": "int32"})
|
|
50
51
|
|
|
51
|
-
# systems
|
|
52
|
-
class RenderSystem
|
|
53
|
-
def
|
|
52
|
+
# systems: Note they are a convention!
|
|
53
|
+
class RenderSystem:
|
|
54
|
+
def __call__(self, world: World): # must override
|
|
54
55
|
query_result = world.query_and((HasPosition, HasColor)) # contiguous-like view of all entities matching
|
|
55
56
|
for position, color in zip(query_result.position, query_result.color): # draw each entity
|
|
56
57
|
DrawEntity(position, color)
|
|
57
58
|
|
|
58
|
-
class MotionSystem
|
|
59
|
-
def
|
|
59
|
+
class MotionSystem:
|
|
60
|
+
def __call__(self, world: World): # must override
|
|
60
61
|
qr = world.query_and((HasPosition, HasVelocity))
|
|
61
62
|
qr.position[:] = qr.position + qr.velocity * DT # writes back to all the underlying pools using numpy's rules
|
|
62
63
|
# Alternative for per-pool update. Less ergonomic, but maybe faster in extreme cases as it avoids the _Field obj
|
|
@@ -64,10 +65,10 @@ class MotionSystem(TickSystem)
|
|
|
64
65
|
pool.position[:] = pool.position + pool.velocity * DT
|
|
65
66
|
|
|
66
67
|
def main():
|
|
67
|
-
render_system = RenderSystem()
|
|
68
|
-
update_systems: list[
|
|
68
|
+
render_system: list[Callable] = RenderSystem()
|
|
69
|
+
update_systems: list[Callable] = [MotionSystem()]
|
|
69
70
|
|
|
70
|
-
world = World(components=[HasPosition, HasColor, HasVelocity])
|
|
71
|
+
world = World(components=[HasPosition, HasColor, HasVelocity], extra_field_metadata=None)
|
|
71
72
|
for _ in range(n_objects):
|
|
72
73
|
# NOTE: world.{add/remove}_{entity/component} are lazy. They take effect after the first world.update() call.
|
|
73
74
|
world.add_entity(components=(HasPosition, HasVelocity, HasColor), # tuple of components (types)
|
|
@@ -78,11 +79,11 @@ def main():
|
|
|
78
79
|
while not rl.WindowShouldClose():
|
|
79
80
|
world.update() # must be called at each tick so the lazy methods are processed and entities are updated
|
|
80
81
|
# update stuff...
|
|
81
|
-
_ = [system
|
|
82
|
+
_ = [system(world=world) for system in update_systems]
|
|
82
83
|
# draw stuff, e.g. using raylib
|
|
83
84
|
rl.BeginDrawing()
|
|
84
85
|
rl.ClearBackground(rl.RAYWHITE)
|
|
85
86
|
rl.DrawFPS(rl.GetScreenWidth() - 100, 0)
|
|
86
|
-
render_system
|
|
87
|
+
render_system(world=world)
|
|
87
88
|
rl.EndDrawing()
|
|
88
89
|
```
|
|
@@ -8,25 +8,26 @@ Usage:
|
|
|
8
8
|
- Sandbox: `python main.py`
|
|
9
9
|
- Tests: `pytest test/`
|
|
10
10
|
|
|
11
|
-
There are only
|
|
11
|
+
There are only 4 primitives (bottom up): `Component`, `Pool`, `QueryResult`, `World`:
|
|
12
12
|
|
|
13
|
-
- `Component` is a simple python dataclass holding only data. All entries must be numpy arrays with metadata fields: shape and dtype. We support 5 dtypes only: `int32`, `float32`, `bool`, `str` and `object`.
|
|
13
|
+
- `Component` is a simple python dataclass holding only data. All entries must be numpy arrays with metadata fields: shape and dtype. We support 5 dtypes only: `int32`, `float32`, `bool`, `str` and `object`. A component with no fields is a valid **tag** for querying (e.g. `class Frozen(Component): pass`).
|
|
14
14
|
- `Pool` is a simple 'archetype' dynamic array, holding entities of the same type (same set of components). Usses `Components` metadata to construct contiguous arrays for all entities of the same type.
|
|
15
15
|
- `QueryResult` is a list of pools that match some query on all the entities of the `World`. It acts as a contiguous numpy-like container that implements numpy's `__array_function__` and `__array_ufunc__`. For all intents and purposes it should feel like a `(N, ...)` view over all the entities. If you need a numpy array (not all ops are supported, for e.g. indexing on the first axis), use `QueryResult.numpy()`. It also exposes `entity_ids`: a flat `(N,)` array of the matched entities' ids, in the same pool-by-pool order as the fields, so you can `zip(qr.entity_ids, qr.position)` or feed an id back to `world.get_entity` / `world.remove_entity`.
|
|
16
|
-
- `World` is a manager of `Pools` and has an overview of all the entities in the scene. It also manages the migration of entities from one pool to the other.
|
|
17
|
-
- `System` is an abstract class that queries the `World` for a subset of `Pools` matching some components. It updates the entities in these pools given some logic (e.g. collisions, motion physics or simply calls the drawing functions). They are merely a convention, not tied to `World` per se.
|
|
16
|
+
- `World` is a manager of `Pools` and has an overview of all the entities in the scene. It also manages the migration of entities from one pool to the other. A `World` can also require extra metadata keys on every field via `World(extra_field_metadata=["serializable"])`, to enforce component-level behavior such as field serialization.
|
|
18
17
|
|
|
19
18
|
Few relevant concepts:
|
|
20
19
|
|
|
21
20
|
- `Pool` operates on array indices, while `World` operates on entity IDs (also integers). This allows seamless movement between pools while the high-level systems still working as intended.
|
|
22
21
|
- All mutable operations on `World` are lazy. These are: `add_entity`, `remove_entity`, `add_component`, `remove_component`. They are added to a command buffer which is only executed when calling `world.update()`.
|
|
22
|
+
- `Systems` are a convention, they are not part of this library. They can be defined at application level and act as hooks or callbacks. The `World` object doesn't need to know more than entities and components.
|
|
23
23
|
|
|
24
24
|
Super simplified main loop structure:
|
|
25
25
|
|
|
26
26
|
```python
|
|
27
|
+
from typing import Callable
|
|
27
28
|
import numpy as np
|
|
28
29
|
import raylib as rl
|
|
29
|
-
from microecs import World, Component
|
|
30
|
+
from microecs import World, Component
|
|
30
31
|
|
|
31
32
|
# components
|
|
32
33
|
class HasPosition(Component):
|
|
@@ -36,15 +37,15 @@ class HasVelocity(Component):
|
|
|
36
37
|
class HasColor(Component):
|
|
37
38
|
color: np.ndarray = field(metadata={"shape": (4, ), "dtype": "int32"})
|
|
38
39
|
|
|
39
|
-
# systems
|
|
40
|
-
class RenderSystem
|
|
41
|
-
def
|
|
40
|
+
# systems: Note they are a convention!
|
|
41
|
+
class RenderSystem:
|
|
42
|
+
def __call__(self, world: World): # must override
|
|
42
43
|
query_result = world.query_and((HasPosition, HasColor)) # contiguous-like view of all entities matching
|
|
43
44
|
for position, color in zip(query_result.position, query_result.color): # draw each entity
|
|
44
45
|
DrawEntity(position, color)
|
|
45
46
|
|
|
46
|
-
class MotionSystem
|
|
47
|
-
def
|
|
47
|
+
class MotionSystem:
|
|
48
|
+
def __call__(self, world: World): # must override
|
|
48
49
|
qr = world.query_and((HasPosition, HasVelocity))
|
|
49
50
|
qr.position[:] = qr.position + qr.velocity * DT # writes back to all the underlying pools using numpy's rules
|
|
50
51
|
# Alternative for per-pool update. Less ergonomic, but maybe faster in extreme cases as it avoids the _Field obj
|
|
@@ -52,10 +53,10 @@ class MotionSystem(TickSystem)
|
|
|
52
53
|
pool.position[:] = pool.position + pool.velocity * DT
|
|
53
54
|
|
|
54
55
|
def main():
|
|
55
|
-
render_system = RenderSystem()
|
|
56
|
-
update_systems: list[
|
|
56
|
+
render_system: list[Callable] = RenderSystem()
|
|
57
|
+
update_systems: list[Callable] = [MotionSystem()]
|
|
57
58
|
|
|
58
|
-
world = World(components=[HasPosition, HasColor, HasVelocity])
|
|
59
|
+
world = World(components=[HasPosition, HasColor, HasVelocity], extra_field_metadata=None)
|
|
59
60
|
for _ in range(n_objects):
|
|
60
61
|
# NOTE: world.{add/remove}_{entity/component} are lazy. They take effect after the first world.update() call.
|
|
61
62
|
world.add_entity(components=(HasPosition, HasVelocity, HasColor), # tuple of components (types)
|
|
@@ -66,11 +67,11 @@ def main():
|
|
|
66
67
|
while not rl.WindowShouldClose():
|
|
67
68
|
world.update() # must be called at each tick so the lazy methods are processed and entities are updated
|
|
68
69
|
# update stuff...
|
|
69
|
-
_ = [system
|
|
70
|
+
_ = [system(world=world) for system in update_systems]
|
|
70
71
|
# draw stuff, e.g. using raylib
|
|
71
72
|
rl.BeginDrawing()
|
|
72
73
|
rl.ClearBackground(rl.RAYWHITE)
|
|
73
74
|
rl.DrawFPS(rl.GetScreenWidth() - 100, 0)
|
|
74
|
-
render_system
|
|
75
|
+
render_system(world=world)
|
|
75
76
|
rl.EndDrawing()
|
|
76
77
|
```
|
|
@@ -4,6 +4,5 @@ from .component import Component
|
|
|
4
4
|
from .pool import Pool
|
|
5
5
|
from .query_result import QueryResult
|
|
6
6
|
from .world import World
|
|
7
|
-
from .system import TickSystem
|
|
8
7
|
|
|
9
|
-
__all__ = ["Component", "Pool", "QueryResult", "World"
|
|
8
|
+
__all__ = ["Component", "Pool", "QueryResult", "World"]
|
|
@@ -9,8 +9,14 @@ from .component import ComponentType
|
|
|
9
9
|
from .query_result import QueryResult
|
|
10
10
|
|
|
11
11
|
class World:
|
|
12
|
-
"""
|
|
13
|
-
|
|
12
|
+
"""
|
|
13
|
+
Generic container for pools of components. Newly added components are assigned a unique id and go in a pool
|
|
14
|
+
Parameters
|
|
15
|
+
- components The list of components that the world accepts
|
|
16
|
+
- extra_field_metadata The list of required extra metadata for each field besides shape and dtype.
|
|
17
|
+
"""
|
|
18
|
+
def __init__(self, components: list[ComponentType], extra_field_metadata: list[str] | None = None):
|
|
19
|
+
self.extra_field_metadata = extra_field_metadata or []
|
|
14
20
|
self._check_components(components)
|
|
15
21
|
self.pools: dict[PoolKey, Pool] = {}
|
|
16
22
|
self.pool_to_components: dict[Pool, list[ComponentType]] = {}
|
|
@@ -178,7 +184,7 @@ class World:
|
|
|
178
184
|
hints = get_type_hints(component) # make it work with from __future__ import annotations
|
|
179
185
|
for field_name, _field in component.__dataclass_fields__.items():
|
|
180
186
|
assert hints[field_name] is np.ndarray, f"Field '{field_name}' of '{component=}' not an array: {_field}"
|
|
181
|
-
assert _field.metadata.keys() == {"shape", "dtype"}, _field.metadata
|
|
187
|
+
assert _field.metadata.keys() == {"shape", "dtype", *self.extra_field_metadata}, _field.metadata
|
|
182
188
|
assert isinstance(_field.metadata["shape"], tuple), _field.metadata["shape"]
|
|
183
189
|
assert isinstance(fmd := _field.metadata["dtype"], str) and fmd in _dtypes, f"{fmd} not in {_dtypes}"
|
|
184
190
|
assert field_name not in _query_result_reserved_names, f"Field '{field_name}' in {_qres}"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: microecs
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: MicroECS: Minimal Entity Component System (ECS) in python and numpy
|
|
5
5
|
Home-page: https://gitlab.com/meehai/microecs
|
|
6
6
|
License: MIT
|
|
@@ -20,25 +20,26 @@ Usage:
|
|
|
20
20
|
- Sandbox: `python main.py`
|
|
21
21
|
- Tests: `pytest test/`
|
|
22
22
|
|
|
23
|
-
There are only
|
|
23
|
+
There are only 4 primitives (bottom up): `Component`, `Pool`, `QueryResult`, `World`:
|
|
24
24
|
|
|
25
|
-
- `Component` is a simple python dataclass holding only data. All entries must be numpy arrays with metadata fields: shape and dtype. We support 5 dtypes only: `int32`, `float32`, `bool`, `str` and `object`.
|
|
25
|
+
- `Component` is a simple python dataclass holding only data. All entries must be numpy arrays with metadata fields: shape and dtype. We support 5 dtypes only: `int32`, `float32`, `bool`, `str` and `object`. A component with no fields is a valid **tag** for querying (e.g. `class Frozen(Component): pass`).
|
|
26
26
|
- `Pool` is a simple 'archetype' dynamic array, holding entities of the same type (same set of components). Usses `Components` metadata to construct contiguous arrays for all entities of the same type.
|
|
27
27
|
- `QueryResult` is a list of pools that match some query on all the entities of the `World`. It acts as a contiguous numpy-like container that implements numpy's `__array_function__` and `__array_ufunc__`. For all intents and purposes it should feel like a `(N, ...)` view over all the entities. If you need a numpy array (not all ops are supported, for e.g. indexing on the first axis), use `QueryResult.numpy()`. It also exposes `entity_ids`: a flat `(N,)` array of the matched entities' ids, in the same pool-by-pool order as the fields, so you can `zip(qr.entity_ids, qr.position)` or feed an id back to `world.get_entity` / `world.remove_entity`.
|
|
28
|
-
- `World` is a manager of `Pools` and has an overview of all the entities in the scene. It also manages the migration of entities from one pool to the other.
|
|
29
|
-
- `System` is an abstract class that queries the `World` for a subset of `Pools` matching some components. It updates the entities in these pools given some logic (e.g. collisions, motion physics or simply calls the drawing functions). They are merely a convention, not tied to `World` per se.
|
|
28
|
+
- `World` is a manager of `Pools` and has an overview of all the entities in the scene. It also manages the migration of entities from one pool to the other. A `World` can also require extra metadata keys on every field via `World(extra_field_metadata=["serializable"])`, to enforce component-level behavior such as field serialization.
|
|
30
29
|
|
|
31
30
|
Few relevant concepts:
|
|
32
31
|
|
|
33
32
|
- `Pool` operates on array indices, while `World` operates on entity IDs (also integers). This allows seamless movement between pools while the high-level systems still working as intended.
|
|
34
33
|
- All mutable operations on `World` are lazy. These are: `add_entity`, `remove_entity`, `add_component`, `remove_component`. They are added to a command buffer which is only executed when calling `world.update()`.
|
|
34
|
+
- `Systems` are a convention, they are not part of this library. They can be defined at application level and act as hooks or callbacks. The `World` object doesn't need to know more than entities and components.
|
|
35
35
|
|
|
36
36
|
Super simplified main loop structure:
|
|
37
37
|
|
|
38
38
|
```python
|
|
39
|
+
from typing import Callable
|
|
39
40
|
import numpy as np
|
|
40
41
|
import raylib as rl
|
|
41
|
-
from microecs import World, Component
|
|
42
|
+
from microecs import World, Component
|
|
42
43
|
|
|
43
44
|
# components
|
|
44
45
|
class HasPosition(Component):
|
|
@@ -48,15 +49,15 @@ class HasVelocity(Component):
|
|
|
48
49
|
class HasColor(Component):
|
|
49
50
|
color: np.ndarray = field(metadata={"shape": (4, ), "dtype": "int32"})
|
|
50
51
|
|
|
51
|
-
# systems
|
|
52
|
-
class RenderSystem
|
|
53
|
-
def
|
|
52
|
+
# systems: Note they are a convention!
|
|
53
|
+
class RenderSystem:
|
|
54
|
+
def __call__(self, world: World): # must override
|
|
54
55
|
query_result = world.query_and((HasPosition, HasColor)) # contiguous-like view of all entities matching
|
|
55
56
|
for position, color in zip(query_result.position, query_result.color): # draw each entity
|
|
56
57
|
DrawEntity(position, color)
|
|
57
58
|
|
|
58
|
-
class MotionSystem
|
|
59
|
-
def
|
|
59
|
+
class MotionSystem:
|
|
60
|
+
def __call__(self, world: World): # must override
|
|
60
61
|
qr = world.query_and((HasPosition, HasVelocity))
|
|
61
62
|
qr.position[:] = qr.position + qr.velocity * DT # writes back to all the underlying pools using numpy's rules
|
|
62
63
|
# Alternative for per-pool update. Less ergonomic, but maybe faster in extreme cases as it avoids the _Field obj
|
|
@@ -64,10 +65,10 @@ class MotionSystem(TickSystem)
|
|
|
64
65
|
pool.position[:] = pool.position + pool.velocity * DT
|
|
65
66
|
|
|
66
67
|
def main():
|
|
67
|
-
render_system = RenderSystem()
|
|
68
|
-
update_systems: list[
|
|
68
|
+
render_system: list[Callable] = RenderSystem()
|
|
69
|
+
update_systems: list[Callable] = [MotionSystem()]
|
|
69
70
|
|
|
70
|
-
world = World(components=[HasPosition, HasColor, HasVelocity])
|
|
71
|
+
world = World(components=[HasPosition, HasColor, HasVelocity], extra_field_metadata=None)
|
|
71
72
|
for _ in range(n_objects):
|
|
72
73
|
# NOTE: world.{add/remove}_{entity/component} are lazy. They take effect after the first world.update() call.
|
|
73
74
|
world.add_entity(components=(HasPosition, HasVelocity, HasColor), # tuple of components (types)
|
|
@@ -78,11 +79,11 @@ def main():
|
|
|
78
79
|
while not rl.WindowShouldClose():
|
|
79
80
|
world.update() # must be called at each tick so the lazy methods are processed and entities are updated
|
|
80
81
|
# update stuff...
|
|
81
|
-
_ = [system
|
|
82
|
+
_ = [system(world=world) for system in update_systems]
|
|
82
83
|
# draw stuff, e.g. using raylib
|
|
83
84
|
rl.BeginDrawing()
|
|
84
85
|
rl.ClearBackground(rl.RAYWHITE)
|
|
85
86
|
rl.DrawFPS(rl.GetScreenWidth() - 100, 0)
|
|
86
|
-
render_system
|
|
87
|
+
render_system(world=world)
|
|
87
88
|
rl.EndDrawing()
|
|
88
89
|
```
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
"""systems.py - Interfaces for various types of systems in ECS"""
|
|
2
|
-
from abc import ABC, abstractmethod
|
|
3
|
-
|
|
4
|
-
from .world import World
|
|
5
|
-
|
|
6
|
-
class TickSystem(ABC):
|
|
7
|
-
"""Generic tick-level system. Called inside the hot main engine loop on every tick"""
|
|
8
|
-
@abstractmethod
|
|
9
|
-
def on_tick(self, world: World):
|
|
10
|
-
"""callback called on each tick"""
|
|
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
|