changeset 217:d98daba73055

Composition-based renderers.
author Jeremy Thurgood <firxen@gmail.com>
date Wed, 04 Sep 2013 14:53:37 +0200
parents f23ab2dd6ce8
children 9e2ef2f15035
files nagslang/enemies.py nagslang/game_object.py nagslang/protagonist.py nagslang/render.py
diffstat 4 files changed, 120 insertions(+), 130 deletions(-) [+]
line wrap: on
line diff
--- a/nagslang/enemies.py	Wed Sep 04 13:15:48 2013 +0200
+++ b/nagslang/enemies.py	Wed Sep 04 14:53:37 2013 +0200
@@ -54,27 +54,14 @@
         self.angle = 0
 
     def _setup_renderer(self):
-        self.renderer = render.AnimatedFacingImageRenderer(
-            (self._get_image('alien_A_1.png'),
-             self._get_image('alien_A_1.png'),
-             self._get_image('alien_A_1.png'),
-             self._get_image('alien_A_1.png'),
-             self._get_image('alien_A_1.png'),
-             self._get_image('alien_A_1.png'),
-             self._get_image('alien_A_2.png'),
-             self._get_image('alien_A_2.png'),
-             self._get_image('alien_A_2.png')),
-            (self._get_image('alien_A_1.png', FLIP_H),
-             self._get_image('alien_A_1.png', FLIP_H),
-             self._get_image('alien_A_1.png', FLIP_H),
-             self._get_image('alien_A_1.png', FLIP_H),
-             self._get_image('alien_A_1.png', FLIP_H),
-             self._get_image('alien_A_1.png', FLIP_H),
-             self._get_image('alien_A_2.png', FLIP_H),
-             self._get_image('alien_A_2.png', FLIP_H),
-             self._get_image('alien_A_2.png', FLIP_H)))
-        # We're always animated
-        self.renderer.start()
+        self.renderer = render.FacingSelectionRenderer({
+            'left': render.TimedAnimatedRenderer(
+                [self._get_image('alien_A_1.png'),
+                 self._get_image('alien_A_2.png')], 3),
+            'right': render.TimedAnimatedRenderer(
+                [self._get_image('alien_A_1.png', FLIP_H),
+                 self._get_image('alien_A_2.png', FLIP_H)], 3),
+        })
 
     def get_render_angle(self):
         return self.angle
--- a/nagslang/game_object.py	Wed Sep 04 13:15:48 2013 +0200
+++ b/nagslang/game_object.py	Wed Sep 04 14:53:37 2013 +0200
@@ -43,6 +43,9 @@
     def get_angle(self):
         return self.get_shape().body.angle
 
+    def get_velocity(self):
+        return self.get_shape().body.velocity
+
     def _get_position(self):
         return self.get_shape().body.position
 
@@ -146,6 +149,13 @@
     def animate(self):
         self.renderer.animate()
 
+    def is_moving(self):
+        """Returns `True` if this object is moving.
+
+        This is mostly for movement-based animation renderers to look at.
+        """
+        return self.physicser.get_velocity().length > 0
+
     def collide_with_protagonist(self):
         """Called as a `pre_solve` collision callback with the protagonist.
 
--- a/nagslang/protagonist.py	Wed Sep 04 13:15:48 2013 +0200
+++ b/nagslang/protagonist.py	Wed Sep 04 14:53:37 2013 +0200
@@ -81,58 +81,42 @@
     def _setup_renderers(self):
         self.angle = 0
         self._renderers = {
-            self.HUMAN_FORM: render.AnimatedFacingImageRenderer(
-                (self._get_image('human_1.png'),
-                 self._get_image('human_1.png'),
-                 self._get_image('human_1.png'),
-                 self._get_image('human_2.png'),
-                 self._get_image('human_2.png'),
-                 self._get_image('human_2.png')),
-                (self._get_image('human_1.png', FLIP_H),
-                 self._get_image('human_1.png', FLIP_H),
-                 self._get_image('human_1.png', FLIP_H),
-                 self._get_image('human_2.png', FLIP_H),
-                 self._get_image('human_2.png', FLIP_H),
-                 self._get_image('human_2.png', FLIP_H))),
-            self.HUMAN_FORM_BACK: render.AnimatedFacingImageRenderer(
-                (self._get_image('human_back_1.png'),
-                 self._get_image('human_back_1.png'),
-                 self._get_image('human_back_1.png'),
-                 self._get_image('human_back_2.png'),
-                 self._get_image('human_back_2.png'),
-                 self._get_image('human_back_2.png')),
-                (self._get_image('human_back_1.png', FLIP_H),
-                 self._get_image('human_back_1.png', FLIP_H),
-                 self._get_image('human_back_1.png', FLIP_H),
-                 self._get_image('human_back_2.png', FLIP_H),
-                 self._get_image('human_back_2.png', FLIP_H),
-                 self._get_image('human_back_2.png', FLIP_H))),
-            self.WOLF_FORM: render.AnimatedFacingImageRenderer(
-                (self._get_image('werewolf_1.png'),
-                 self._get_image('werewolf_1.png'),
-                 self._get_image('werewolf_1.png'),
-                 self._get_image('werewolf_2.png'),
-                 self._get_image('werewolf_2.png'),
-                 self._get_image('werewolf_2.png')),
-                (self._get_image('werewolf_1.png', FLIP_H),
-                 self._get_image('werewolf_1.png', FLIP_H),
-                 self._get_image('werewolf_1.png', FLIP_H),
-                 self._get_image('werewolf_2.png', FLIP_H),
-                 self._get_image('werewolf_2.png', FLIP_H),
-                 self._get_image('werewolf_2.png', FLIP_H))),
-            self.WOLF_FORM_BACK: render.AnimatedFacingImageRenderer(
-                (self._get_image('werewolf_back_1.png'),
-                 self._get_image('werewolf_back_1.png'),
-                 self._get_image('werewolf_back_1.png'),
-                 self._get_image('werewolf_back_2.png'),
-                 self._get_image('werewolf_back_2.png'),
-                 self._get_image('werewolf_back_2.png')),
-                (self._get_image('werewolf_back_1.png', FLIP_H),
-                 self._get_image('werewolf_back_1.png', FLIP_H),
-                 self._get_image('werewolf_back_1.png', FLIP_H),
-                 self._get_image('werewolf_back_2.png', FLIP_H),
-                 self._get_image('werewolf_back_2.png', FLIP_H),
-                 self._get_image('werewolf_back_2.png', FLIP_H))),
+            self.HUMAN_FORM: render.FacingSelectionRenderer(
+                {
+                    'left': render.MovementAnimatedRenderer(
+                        [self._get_image('human_1.png'),
+                         self._get_image('human_2.png')], 3),
+                    'right': render.MovementAnimatedRenderer(
+                        [self._get_image('human_1.png', FLIP_H),
+                         self._get_image('human_2.png', FLIP_H)], 3),
+                }),
+            self.HUMAN_FORM_BACK: render.FacingSelectionRenderer(
+                {
+                    'left': render.MovementAnimatedRenderer(
+                        [self._get_image('human_back_1.png'),
+                         self._get_image('human_back_2.png')], 3),
+                    'right': render.MovementAnimatedRenderer(
+                        [self._get_image('human_back_1.png', FLIP_H),
+                         self._get_image('human_back_2.png', FLIP_H)], 3),
+                }),
+            self.WOLF_FORM: render.FacingSelectionRenderer(
+                {
+                    'left': render.MovementAnimatedRenderer(
+                        [self._get_image('werewolf_1.png'),
+                         self._get_image('werewolf_2.png')], 3),
+                    'right': render.MovementAnimatedRenderer(
+                        [self._get_image('werewolf_1.png', FLIP_H),
+                         self._get_image('werewolf_2.png', FLIP_H)], 3),
+                }),
+            self.WOLF_FORM_BACK: render.FacingSelectionRenderer(
+                {
+                    'left': render.MovementAnimatedRenderer(
+                        [self._get_image('werewolf_back_1.png'),
+                         self._get_image('werewolf_back_2.png')], 3),
+                    'right': render.MovementAnimatedRenderer(
+                        [self._get_image('werewolf_back_1.png', FLIP_H),
+                         self._get_image('werewolf_back_2.png', FLIP_H)], 3),
+                }),
         }
         for renderer in self._renderers.values():
             renderer.set_game_object(self)
@@ -190,7 +174,6 @@
 
     def set_direction(self, dx, dy):
         if (dx, dy) == (0, 0):
-            self.renderer.stop()
             return
         old_angle = self.angle
         self.angle = pymunk.Vec2d((dx, dy)).angle
@@ -205,7 +188,6 @@
                 self._switch_to_front()
         self.physicser.apply_impulse(
             (dx * self.impulse_factor, dy * self.impulse_factor))
-        self.renderer.start()
 
     def set_position(self, position):
         self.physicser.position = position
@@ -213,6 +195,7 @@
     def copy_state(self, old_protagonist):
         self.physicser.position = old_protagonist.physicser.position
         self.physicser.switch_form(self.form, old_protagonist.form)
+        self.impulse_factor = old_protagonist.impulse_factor
         self.form = old_protagonist.form
         self.angle = old_protagonist.angle
         self.render_form = old_protagonist.render_form
--- a/nagslang/render.py	Wed Sep 04 13:15:48 2013 +0200
+++ b/nagslang/render.py	Wed Sep 04 14:53:37 2013 +0200
@@ -71,12 +71,69 @@
         return self._state_images[self.game_object.puzzler.get_state()]
 
 
-class FacingImageRenderer(ImageRenderer):
-    def __init__(self, left_image, right_image):
-        self._images = {
-            'left': left_image,
-            'right': right_image,
-        }
+class TimedAnimatedRenderer(ImageRenderer):
+    def __init__(self, images, frame_ticks=1):
+        self._images = images
+        self._frame_ticks = frame_ticks
+        self._frame_tick = 0
+        self._frame = 0
+
+    def advance_tick(self):
+        self._frame_tick += 1
+        if self._frame_tick > self._frame_ticks:
+            self._frame_tick = 0
+            self._frame += 1
+        if self._frame >= len(self._images):
+            self._frame = 0
+
+    def reset(self):
+        self._frame_tick = 0
+        self._frame = 0
+
+    def get_image(self):
+        return self._images[self._frame]
+
+    def animate(self):
+        self.advance_tick()
+
+
+class MovementAnimatedRenderer(TimedAnimatedRenderer):
+    def animate(self):
+        if self.game_object.is_moving():
+            self.advance_tick()
+        else:
+            self.reset()
+
+
+class RendererSelectionRenderer(Renderer):
+    def __init__(self, renderers):
+        self._renderers = renderers
+
+    def set_game_object(self, game_object):
+        self.game_object = game_object
+        for renderer in self._renderers.values():
+            renderer.set_game_object(game_object)
+
+    @property
+    def renderer(self):
+        return self._renderers[self.select_renderer()]
+
+    def render(self, surface):
+        return self.renderer.render(surface)
+
+    def animate(self):
+        return self.renderer.animate()
+
+    def select_renderer(self):
+        raise NotImplementedError()
+
+
+class FacingSelectionRenderer(RendererSelectionRenderer):
+    def __init__(self, renderers):
+        for renderer in renderers.values():
+            # TODO: Unhack this somehow.
+            renderer.rotate_image = self.rotate_image
+        super(FacingSelectionRenderer, self).__init__(renderers)
         self._face = 'left'
 
     def _update_facing(self, angle):
@@ -89,57 +146,10 @@
         # Facing images don't get rotated.
         return image
 
-    def get_facing_image(self):
-        return self._images[self._face]
-
-    def get_image(self):
+    def select_renderer(self):
         angle = self.game_object.get_render_angle()
         self._update_facing(angle)
-        return self.get_facing_image()
-
-
-class AnimatedFacingImageRenderer(FacingImageRenderer):
-    def __init__(self, left_images, right_images):
-        self._images = {
-            'left': left_images,
-            'right': right_images,
-        }
-        self._frame = 0
-        self._moving = False
-        self._face = 'left'
-
-    def get_facing_image(self):
-        if self._frame >= len(self._images[self._face]):
-            self._frame = 0
-        return self._images[self._face][self._frame]
-
-    def animate(self):
-        if self._moving:
-            self._frame += 1
-        else:
-            self._frame = 0
-
-    def start(self):
-        self._moving = True
-
-    def stop(self):
-        self._moving = False
-
-
-class TimedAnimatedRenderer(ImageRenderer):
-
-    def __init__(self, images):
-        self._images = images
-        self._frame = 0
-        self._image = None
-
-    def get_image(self):
-        if self._frame > len(self._imaages):
-            self._frame = 0
-        return self._images[self._frame]
-
-    def animate(self):
-        self._frame += 1
+        return self._face
 
 
 class ShapeRenderer(Renderer):