Skip to content

Commit

Permalink
Merge #273
Browse files Browse the repository at this point in the history
273: Add Two Phase Updates feature r=pathunstrom a=astronouth7303

Felt like writing a two-phase update system.

This is so much easier to use with #263 that it's a soft requirement.

This assumes that events signaled are queued and handled after all of the current event handlers are called.

This skipped our usual discussion phase of things, where we debate designs first. Given that, feel free to completely rip this apart.

Co-authored-by: Jamie Bliss <jamie@ivyleav.es>
  • Loading branch information
bors[bot] and AstraLuma committed Jul 14, 2019
2 parents 965cf4a + c4dc2d7 commit 4bea37e
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 1 deletion.
3 changes: 2 additions & 1 deletion docs/reference/features/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ tools to have when making games.
:maxdepth: 2
:caption: Included Features

animation
animation
twophase
12 changes: 12 additions & 0 deletions docs/reference/features/twophase.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Two Phase Updates
=================

.. automodule:: ppb.features.twophase


.. autoclass:: Commit

.. autoclass:: TwoPhaseMixin
:members:

.. autoclass:: TwoPhaseSystem
49 changes: 49 additions & 0 deletions examples/three_body.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
A simple rendition of the three-body problem. Also demonstrates the use of
two-phase updates.
The three body problem comes from astrophysics. Basically, applying the
gravitaional to three celestial bodies produces a problem that does not have a
perfect mathematical solution, and must be solved through simulations.
"""

import ppb
from ppb import BaseSprite, Vector
from ppb.features.twophase import TwoPhaseMixin, TwoPhaseSystem


class Planet(BaseSprite, TwoPhaseMixin):
#: A constant to apply to gravity
G_CONST = 1

velocity = Vector(0, 0)

def get_bodies(self, scene):
for planet in scene.get(kind=Planet):
yield planet, (planet.position - self.position)

def on_update(self, event, signal):
# This assumes all planets have the same mass
force = sum(
(
delta / (len(delta) ** 2)
for planet, delta in self.get_bodies(event.scene)
),
Vector(0, 0)
)

self.velocity += force * self.G_CONST * event.time_delta

self.stage_changes(
position=self.position + self.velocity * event.time_delta
)


def setup(scene):
scene.add(Planet(position=(3, 0), velocity=Vector(0, 1)))
scene.add(Planet(position=(-3, 3), velocity=Vector(1, -1)))
scene.add(Planet(position=(-3, -3), velocity=Vector(-1, 0)))


if __name__ == "__main__":
ppb.run(setup, systems=[TwoPhaseSystem])
51 changes: 51 additions & 0 deletions ppb/features/twophase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
A system for two phase updates: Update, and Commit.
"""
from dataclasses import dataclass
from ppb.systems import System
from ppb.events import EventMixin

__all__ = 'Commit',


@dataclass
class Commit:
"""
Fired after Update.
"""


class TwoPhaseSystem(System):
"""
Produces the Commit event.
"""

def on_update(self, event, signal):
signal(Commit())


class TwoPhaseMixin(EventMixin):
"""
Mixin to apply to objects to handle two phase updates.
"""

__staged_changes = None

def stage_changes(self, **kwargs):
"""
Stage changes for the next commit.
These are just properties on the current object to update.
"""
if self.__staged_changes is None:
self.__staged_changes = {}
self.__staged_changes.update(kwargs)

def on_commit(self, event, signal):
"""
Commit changes previously staged.
"""
changes, self.__staged_changes = self.__staged_changes, {}
if changes:
for name, value in changes.items():
setattr(self, name, value)
28 changes: 28 additions & 0 deletions tests/test_twophase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from ppb import GameEngine, BaseScene
from ppb.testutils import Quitter
from ppb.events import Update
from ppb.features.twophase import TwoPhaseMixin, TwoPhaseSystem, Commit


def test_twophase():
events = []

class TestScene(BaseScene, TwoPhaseMixin):
flag = False

def on_update(self, event, signal):
nonlocal events
self.stage_changes(flag=True)
events.append(type(event))

def on_commit(self, event, signal):
nonlocal events
super().on_commit(event, signal)
events.append(type(event))

with GameEngine(TestScene, basic_systems=[TwoPhaseSystem, Quitter]) as engine:
engine.signal(Update(time_delta=0.1))
engine.run()

assert engine.current_scene.flag
assert events == [Update, Commit]

0 comments on commit 4bea37e

Please sign in to comment.