import pygame.sprite
from pygame import Vector2
-from pacman.ecs.hierarchy import hierachy_plugins
+from pacman.ecs.hierarchy import Children, Parent, hierachy_plugins
from pacman.ecs.schedule import (
MainSchedule,
Schedule,
world.tick(UpdateSchedule)
+PreUpdate = SystemSet("preupdate")
+Update = SystemSet("update")
+PostUpdate = SystemSet("postupdate")
+
+
def schedule_plugins(world: World) -> None:
- world.with_systems(MainSchedule, run_minimal_subschedules)
+ world.with_systems(MainSchedule, run_minimal_subschedules).with_systems(
+ UpdateSchedule, Systems(PreUpdate, Update, PostUpdate).chain()
+ )
-def minimal_plugins(world: World) -> None:
- world.with_plugins(schedule_plugins, hierachy_plugins)
+class CoordAbs2D:
+ vec: Vector2
+ depth: float
+
+ def __init__(self) -> None:
+ self.vec = Vector2(0.0)
+ self.depth = 0.0
@dataclass
-class ScreenCoord2D:
+class Coord2D:
vec: Vector2
depth: float
+def update_coord(world: World) -> None:
+ for abs_coord, coord in world.query(tuple[CoordAbs2D, Coord2D]):
+ abs_coord.vec = coord.vec.copy()
+ abs_coord.depth = coord.depth
+
+
+def update_coord_hierarchy(world: World) -> None:
+ work = []
+ for (children,) in world.query(tuple[Children], without=tuple[Parent]):
+ work.extend(children.children)
+ while work:
+ curr = work.pop()
+ child = world[curr]
+ parent = world[world[curr][Parent].parent]
+ if CoordAbs2D in parent and CoordAbs2D in child:
+ parent_coord = parent[CoordAbs2D]
+ child_coord = child[CoordAbs2D]
+ child_coord.vec += parent_coord.vec
+ child_coord.depth += parent_coord.depth
+ if Children in child:
+ work.extend(child[Children].children)
+
+
+PropagateTransform = SystemSet("propagate-transform")
+
+
+def coord_update_plugins(world: World) -> None:
+ world.with_systems(
+ UpdateSchedule,
+ Systems(update_coord, update_coord_hierarchy)
+ .chain()
+ .in_set(PostUpdate, PropagateTransform),
+ )
+
+
+def minimal_plugins(world: World) -> None:
+ world.with_plugins(
+ schedule_plugins, hierachy_plugins, coord_update_plugins
+ )
+
+
class ShouldRender:
pass
def render_sprite2d(world: World) -> None:
window = world.res_s(Window)
sprites_raw = sorted(
- world[tuple[ScreenCoord2D, Sprite2D, ShouldRender]],
+ world[tuple[CoordAbs2D, Sprite2D, ShouldRender]],
key=lambda e: e[0].depth,
reverse=True,
)
def graphics_plugins(world: World) -> None:
world.with_systems(StartupSchedule, init_window).with_systems(
UpdateSchedule,
- Systems(poll_events, close_on_close, render_sprite2d).chain(),
+ Systems(poll_events, close_on_close, render_sprite2d)
+ .chain()
+ .after(PropagateTransform),
)
+class WiggleSprite:
+ pass
+
+
def move_sprites(world: World) -> None:
import math
time = pygame.time.get_ticks() / 100
- for (coord,) in world[tuple[ScreenCoord2D]]:
+ for coord, _ in world[tuple[Coord2D, WiggleSprite]]:
pos = max(time - coord.depth * 0.3, 0.0)
coord.vec.y = 500.0 + math.sin(pos) * 30.0
+class WiggleRoot:
+ pass
+
+
+def move_root(world: World) -> None:
+ import math
+
+ time = pygame.time.get_ticks() / 100
+ for coord, _ in world[tuple[Coord2D, WiggleRoot]]:
+ coord.vec.x = 30.0 + math.cos(time) * 30.0
+
+
if __name__ == "__main__":
world = (
World()
.with_plugins(minimal_plugins, graphics_plugins)
- .with_systems(UpdateSchedule, move_sprites)
+ .with_systems(UpdateSchedule, move_sprites, move_root)
)
assets = list(Path("./asset").rglob("*.png"))
+ root = Entity()
+ world[root] = (
+ Coord2D(Vector2(100.0, 0.0), 0.0),
+ CoordAbs2D(),
+ WiggleRoot(),
+ )
for i, asset in enumerate(assets):
world[Entity()] = (
- ScreenCoord2D(Vector2(30.0 * i, 500.0), 1.0 * i),
+ Coord2D(Vector2(30.0 * i, 500.0), 1.0 * i),
+ CoordAbs2D(),
Sprite2D(pygame.image.load(asset)),
ShouldRender(),
+ WiggleSprite(),
+ Parent(root),
)
world.run_main()
self.world._entities[self.entity].add(ty)
if ty not in self.world._components:
self.world._components[ty] = {}
+ if self.entity in self.world._components[ty]:
+ del self[ty]
self.world._components[ty][self.entity] = val
+ if ty in self.world._insert_hooks:
+ self.world._insert_hooks[ty](self.world, self.entity, val)
def __delitem__[T](self, ty: type[T]) -> None:
if ty not in self.world._entities[self.entity]:
return
+ if ty in self.world._remove_hooks:
+ self.world._remove_hooks[ty](
+ self.world,
+ self.entity,
+ self.world._components[ty][self.entity],
+ )
self.world._entities[self.entity].remove(ty)
del self.world._components[ty][self.entity]
return
sets: list[dict[Entity, Any]] = sorted(
(
- self._entities if arg is Entity else self._components[arg]
+ self._entities
+ if arg is Entity
+ else self._components.get(arg, {})
for arg in args
if arg is Entity or arg in self._components
),
)
neg_sets: list[dict[Entity, Any]] = sorted(
(
- self._entities if arg is Entity else self._components[arg]
+ self._entities
+ if arg is Entity
+ else self._components.get(arg, {})
for arg in neg_args
if arg is Entity or arg in self._components
),
self.tick(schedule)
def with_systems(
- self, schedule: ScheduleLabel, *systems: "System | Systems"
+ self,
+ schedule: ScheduleLabel,
+ *systems: "System | Systems",
+ sets: set[SystemSet] | None = None,
) -> "World":
+ if sets is None:
+ sets = set()
if schedule not in self._schedules:
self._schedules[schedule] = Schedule()
- Systems(*systems)._apply(self._schedules[schedule])
+ Systems(*systems).in_set(*sets)._apply(self._schedules[schedule])
return self
def with_hook[T](
case "insert":
d = self._insert_hooks
case "remove":
- d = self._insert_hooks
+ d = self._remove_hooks
if ty not in d:
d[ty] = hook
else: