microecs 0.2.4__tar.gz → 0.3.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: microecs
3
- Version: 0.2.4
3
+ Version: 0.3.0
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
@@ -43,7 +43,7 @@ from microecs import World, Component
43
43
 
44
44
  # components
45
45
  class HasPosition(Component):
46
- position: np.ndarray = field(metadata={"shape": (2, ), "dtype": "float32"})
46
+ position: np.ndarray = field(metadata={"shape": (2, ), "dtype": "float32"}) # 'shape' + 'dtype' are always required
47
47
  class HasVelocity(Component):
48
48
  velocity: np.ndarray = field(metadata={"shape": (2, ), "dtype": "float32"})
49
49
  class HasColor(Component):
@@ -51,14 +51,14 @@ class HasColor(Component):
51
51
 
52
52
  # systems: Note they are a convention!
53
53
  class RenderSystem:
54
- def __call__(self, world: World): # must override
55
- query_result = world.query_and((HasPosition, HasColor)) # contiguous-like view of all entities matching
54
+ def __call__(self, world: World):
55
+ query_result = world.query(HasPosition, HasColor, exclude=[]) # contiguous-like view of all entities matching
56
56
  for position, color in zip(query_result.position, query_result.color): # draw each entity
57
57
  DrawEntity(position, color)
58
58
 
59
59
  class MotionSystem:
60
- def __call__(self, world: World): # must override
61
- qr = world.query_and((HasPosition, HasVelocity))
60
+ def __call__(self, world: World):
61
+ qr = world.query(HasPosition, HasVelocity) # 'exclude' is optional
62
62
  qr.position[:] = qr.position + qr.velocity * DT # writes back to all the underlying pools using numpy's rules
63
63
  # Alternative for per-pool update. Less ergonomic, but maybe faster in extreme cases as it avoids the _Field obj
64
64
  for pool in qr.pool_list:
@@ -31,7 +31,7 @@ from microecs import World, Component
31
31
 
32
32
  # components
33
33
  class HasPosition(Component):
34
- position: np.ndarray = field(metadata={"shape": (2, ), "dtype": "float32"})
34
+ position: np.ndarray = field(metadata={"shape": (2, ), "dtype": "float32"}) # 'shape' + 'dtype' are always required
35
35
  class HasVelocity(Component):
36
36
  velocity: np.ndarray = field(metadata={"shape": (2, ), "dtype": "float32"})
37
37
  class HasColor(Component):
@@ -39,14 +39,14 @@ class HasColor(Component):
39
39
 
40
40
  # systems: Note they are a convention!
41
41
  class RenderSystem:
42
- def __call__(self, world: World): # must override
43
- query_result = world.query_and((HasPosition, HasColor)) # contiguous-like view of all entities matching
42
+ def __call__(self, world: World):
43
+ query_result = world.query(HasPosition, HasColor, exclude=[]) # contiguous-like view of all entities matching
44
44
  for position, color in zip(query_result.position, query_result.color): # draw each entity
45
45
  DrawEntity(position, color)
46
46
 
47
47
  class MotionSystem:
48
- def __call__(self, world: World): # must override
49
- qr = world.query_and((HasPosition, HasVelocity))
48
+ def __call__(self, world: World):
49
+ qr = world.query(HasPosition, HasVelocity) # 'exclude' is optional
50
50
  qr.position[:] = qr.position + qr.velocity * DT # writes back to all the underlying pools using numpy's rules
51
51
  # Alternative for per-pool update. Less ergonomic, but maybe faster in extreme cases as it avoids the _Field obj
52
52
  for pool in qr.pool_list:
@@ -36,7 +36,7 @@ class World:
36
36
  self._live_ids: set[EntityId] = set() # 'eager' mode ids so e.g. calling remove_entity twice before update fails
37
37
  # command buffer management. {add/remove}_{entity/component} are lazy. Taken into account after update().
38
38
  self._command_buffer: list[Callable] = []
39
- self._cache: dict[PoolKey, QueryResult] = {}
39
+ self._cache: dict[tuple[PoolKey, PoolKey], QueryResult] = {} # include+exclude key
40
40
  logger.debug(f"Created scene with components: {self.component_names}")
41
41
 
42
42
  # public api
@@ -85,20 +85,34 @@ class World:
85
85
  assert component in self.component_types, f"Component '{component}' not in {self.component_types}"
86
86
  self._command_buffer.append(partial(self._do_remove_component, entity_id=entity_id, component=component))
87
87
 
88
- def query_and(self, component_types: list[ComponentType]) -> QueryResult:
89
- """returns A QueryResult object with the entities that have all the requested components (entity ids too)."""
88
+ def query(self, *include: ComponentType, exclude: list[ComponentType] | None = None) -> QueryResult:
89
+ """
90
+ Queries the world for entities that match the include set of components.
91
+ Syntax: `world.query(A, B, exclude=[C, D])` is, in logic form, a chain of 'ands': `A & B & ~C & ~D`.
92
+ Return: A `QueryResult` object with the entities that have all the requested components. Has `EntityIds` too.
93
+ """
94
+
90
95
  # Note: we can cache the queries. The only time it can get invalidated (via public API) is at update().
91
- if (key := self._make_key(component_types)) in self._cache:
96
+ include_key = self._make_key(include)
97
+ exclude_key = self._make_key(exclude or [])
98
+ if (key := (include_key, exclude_key)) in self._cache:
92
99
  return self._cache[key]
93
100
 
101
+ # archetype_key = (1 0 0 1 1) &
102
+ # key = (1 0 0 0 1)
103
+ # -> (1 0 0 0 1) OK
104
+ # but
105
+ # archetype_key = (1 0 0 1 1) &
106
+ # key = (1 0 1 0 0)
107
+ # -> (1 0 0 0 0) NOT OK
94
108
  res = []
95
109
  for archetype_key, archetype_pool in self.pools.items():
96
- if (archetype_key & key) == key: # key is subset of archetype_key
110
+ if (archetype_key & include_key) == include_key and (archetype_key & exclude_key) == 0:
97
111
  res.append(archetype_pool)
98
112
 
99
- field_names = sum([self.component_to_field_names[c] for c in component_types], [])
100
- field_shapes = dict(zip(field_names, sum([self.component_to_shapes[c] for c in component_types], [])))
101
- field_dtypes = dict(zip(field_names, sum([self.component_to_dtypes[c] for c in component_types], [])))
113
+ field_names = sum([self.component_to_field_names[c] for c in include], [])
114
+ field_shapes = dict(zip(field_names, sum([self.component_to_shapes[c] for c in include], [])))
115
+ field_dtypes = dict(zip(field_names, sum([self.component_to_dtypes[c] for c in include], [])))
102
116
  entity_ids = np.array(sum((self._pool_ids[p] for p in res), []), dtype="int64")
103
117
 
104
118
  self._cache[key] = QueryResult(res, field_shapes=field_shapes, field_dtypes=field_dtypes, entity_ids=entity_ids)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: microecs
3
- Version: 0.2.4
3
+ Version: 0.3.0
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
@@ -43,7 +43,7 @@ from microecs import World, Component
43
43
 
44
44
  # components
45
45
  class HasPosition(Component):
46
- position: np.ndarray = field(metadata={"shape": (2, ), "dtype": "float32"})
46
+ position: np.ndarray = field(metadata={"shape": (2, ), "dtype": "float32"}) # 'shape' + 'dtype' are always required
47
47
  class HasVelocity(Component):
48
48
  velocity: np.ndarray = field(metadata={"shape": (2, ), "dtype": "float32"})
49
49
  class HasColor(Component):
@@ -51,14 +51,14 @@ class HasColor(Component):
51
51
 
52
52
  # systems: Note they are a convention!
53
53
  class RenderSystem:
54
- def __call__(self, world: World): # must override
55
- query_result = world.query_and((HasPosition, HasColor)) # contiguous-like view of all entities matching
54
+ def __call__(self, world: World):
55
+ query_result = world.query(HasPosition, HasColor, exclude=[]) # contiguous-like view of all entities matching
56
56
  for position, color in zip(query_result.position, query_result.color): # draw each entity
57
57
  DrawEntity(position, color)
58
58
 
59
59
  class MotionSystem:
60
- def __call__(self, world: World): # must override
61
- qr = world.query_and((HasPosition, HasVelocity))
60
+ def __call__(self, world: World):
61
+ qr = world.query(HasPosition, HasVelocity) # 'exclude' is optional
62
62
  qr.position[:] = qr.position + qr.velocity * DT # writes back to all the underlying pools using numpy's rules
63
63
  # Alternative for per-pool update. Less ergonomic, but maybe faster in extreme cases as it avoids the _Field obj
64
64
  for pool in qr.pool_list:
@@ -3,7 +3,7 @@ from pathlib import Path
3
3
  from setuptools import setup, find_packages
4
4
 
5
5
  NAME = "microecs"
6
- VERSION = "0.2.4"
6
+ VERSION = "0.3.0"
7
7
  DESCRIPTION = "MicroECS: Minimal Entity Component System (ECS) in python and numpy"
8
8
  URL = "https://gitlab.com/meehai/microecs"
9
9
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes