changeset 207:42e8993c31fd

Break out Renderers
author Stefano Rivera <stefano@rivera.za.net>
date Tue, 03 Sep 2013 23:33:44 +0200
parents 42c565c5ce76
children 3d54fe7a2998
files nagslang/enemies.py nagslang/game_object.py nagslang/protagonist.py nagslang/render.py
diffstat 4 files changed, 178 insertions(+), 175 deletions(-) [+]
line wrap: on
line diff
--- a/nagslang/enemies.py	Tue Sep 03 23:32:46 2013 +0200
+++ b/nagslang/enemies.py	Tue Sep 03 23:33:44 2013 +0200
@@ -1,9 +1,9 @@
 import pymunk
 import pymunk.pygame_util
 
+from nagslang import render
 from nagslang.constants import COLLISION_TYPE_ENEMY, ZORDER_MID
-from nagslang.game_object import (
-    GameObject, SingleShapePhysicser, AnimatedFacingImageRenderer, make_body)
+from nagslang.game_object import GameObject, SingleShapePhysicser, make_body
 from nagslang.mutators import FLIP_H
 from nagslang.resources import resources
 
@@ -54,7 +54,7 @@
         self.angle = 0
 
     def _setup_renderer(self):
-        self.renderer = AnimatedFacingImageRenderer(
+        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'),
--- a/nagslang/game_object.py	Tue Sep 03 23:32:46 2013 +0200
+++ b/nagslang/game_object.py	Tue Sep 03 23:33:44 2013 +0200
@@ -1,14 +1,11 @@
-import math
-
-import pygame
 import pymunk
 import pymunk.pygame_util
 
 from nagslang import puzzle
+from nagslang import render
 from nagslang.constants import (
     SWITCH_PUSHERS, COLLISION_TYPE_SWITCH, COLLISION_TYPE_BOX, ZORDER_LOW,
     ZORDER_FLOOR, COLLISION_TYPE_DOOR)
-from nagslang.options import options
 from nagslang.resources import resources
 from nagslang.events import DoorEvent
 from nagslang.widgets.text import LabelWidget
@@ -79,163 +76,6 @@
         return self._shape.body.apply_impulse(j, r)
 
 
-class Renderer(object):
-    def set_game_object(self, game_object):
-        self.game_object = game_object
-
-    def _render_shape(self, surface):
-        shape = self.game_object.get_shape()
-        # Less general that pymunk.pygame_util.draw, but also a lot less noisy.
-        color = getattr(shape, 'color', pygame.color.THECOLORS['lightblue'])
-        # We only explicitly draw Circle and Poly shapes. Everything else we
-        # forward to pymunk.
-        if isinstance(shape, pymunk.Circle):
-            centre = pymunk.pygame_util.to_pygame(shape.body.position, surface)
-            radius = int(shape.radius)
-            pygame.draw.circle(surface, color, centre, radius, 2)
-        elif isinstance(shape, pymunk.Poly):
-            # polygon bounding box
-            points = [pymunk.pygame_util.to_pygame(p, surface)
-                      for p in shape.get_vertices()]
-            pygame.draw.lines(surface, color, True, points, 2)
-        else:
-            pymunk.pygame_util.draw(surface, shape)
-
-    def render(self, surface):
-        if options.debug:
-            self._render_shape(surface)
-
-    def animate(self):
-        # Used by time animatations to advance the clock
-        pass
-
-
-def image_pos(image, pos):
-    return (pos[0] - image.get_width() / 2,
-            pos[1] - image.get_height() / 2)
-
-
-class ImageRenderer(Renderer):
-    def __init__(self, image):
-        self._image = image
-
-    def get_image(self):
-        return self._image
-
-    def rotate_image(self, image):
-        angle = self.game_object.get_render_angle() * 180 / math.pi
-        return pygame.transform.rotate(image, angle)
-
-    def render_image(self, surface, image):
-        image = self.rotate_image(image)
-        pos = self.game_object.get_render_position(surface)
-        surface.blit(image, image_pos(image, pos))
-
-    def render(self, surface):
-        self.render_image(surface, self.get_image())
-        super(ImageRenderer, self).render(surface)
-
-
-class ImageStateRenderer(ImageRenderer):
-    def __init__(self, state_images):
-        self._state_images = state_images
-
-    def get_image(self):
-        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,
-        }
-        self._face = 'left'
-
-    def _update_facing(self, angle):
-        if abs(angle) < math.pi / 2:
-            self._face = 'right'
-        elif abs(angle) > math.pi / 2:
-            self._face = 'left'
-
-    def rotate_image(self, image):
-        # Facing images don't get rotated.
-        return image
-
-    def get_facing_image(self):
-        return self._images[self._face]
-
-    def get_image(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
-
-
-class ShapeRenderer(Renderer):
-    def render(self, surface):
-        self._render_shape(surface)
-        super(ShapeRenderer, self).render(surface)
-
-
-class ShapeStateRenderer(ShapeRenderer):
-    """Renders the shape in a different colour depending on the state.
-
-    Requires the game object it's attached to to have a puzzler.
-    """
-    def render(self, surface):
-        if self.game_object.puzzler.get_state():
-            color = pygame.color.THECOLORS['green']
-        else:
-            color = pygame.color.THECOLORS['red']
-
-        self.game_object.get_shape().color = color
-        super(ShapeStateRenderer, self).render(surface)
-
-
 def damping_velocity_func(body, gravity, damping, dt):
     """Apply custom damping to this body's velocity.
     """
@@ -330,7 +170,7 @@
         self.shape.sensor = True
         super(FloorSwitch, self).__init__(
             SingleShapePhysicser(space, self.shape),
-            ImageStateRenderer({
+            render.ImageStateRenderer({
                 True: resources.get_image('objects', 'sensor_on.png'),
                 False: resources.get_image('objects', 'sensor_off.png'),
             }),
@@ -347,7 +187,7 @@
         self.shape.sensor = True
         super(Note, self).__init__(
             SingleShapePhysicser(space, self.shape),
-            ImageRenderer(resources.get_image('objects', 'note.png')),
+            render.ImageRenderer(resources.get_image('objects', 'note.png')),
             puzzle.CollidePuzzler(),
             TextOverlay(message),
         )
@@ -363,7 +203,7 @@
         self.shape.sensor = True
         super(FloorLight, self).__init__(
             SingleShapePhysicser(space, self.shape),
-            ImageStateRenderer({
+            render.ImageStateRenderer({
                 True: resources.get_image('objects', 'light_on.png'),
                 False: resources.get_image('objects', 'light_off.png'),
             }),
@@ -379,7 +219,7 @@
         self.shape.collision_type = COLLISION_TYPE_BOX
         super(Box, self).__init__(
             SingleShapePhysicser(space, self.shape),
-            ImageRenderer(resources.get_image('objects', 'crate.png')),
+            render.ImageRenderer(resources.get_image('objects', 'crate.png')),
         )
 
 
@@ -400,7 +240,7 @@
             puzzler = puzzle.StateProxyPuzzler(key_state)
         super(Door, self).__init__(
             SingleShapePhysicser(space, self.shape),
-            ImageRenderer(resources.get_image('objects', 'door.png')),
+            render.ImageRenderer(resources.get_image('objects', 'door.png')),
             puzzler,
         )
 
--- a/nagslang/protagonist.py	Tue Sep 03 23:32:46 2013 +0200
+++ b/nagslang/protagonist.py	Tue Sep 03 23:33:44 2013 +0200
@@ -3,9 +3,9 @@
 
 import math
 
+from nagslang import render
 from nagslang.constants import COLLISION_TYPE_PLAYER, ZORDER_MID
-from nagslang.game_object import (
-    GameObject, SingleShapePhysicser, AnimatedFacingImageRenderer, make_body)
+from nagslang.game_object import GameObject, SingleShapePhysicser, make_body
 from nagslang.mutators import FLIP_H
 from nagslang.resources import resources
 
@@ -55,7 +55,7 @@
 
     def _setup_renderers(self):
         self._renderers = {
-            self.HUMAN_FORM: AnimatedFacingImageRenderer(
+            self.HUMAN_FORM: render.AnimatedFacingImageRenderer(
                 (self._get_image('human_1.png'),
                  self._get_image('human_1.png'),
                  self._get_image('human_1.png'),
@@ -68,7 +68,7 @@
                  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: AnimatedFacingImageRenderer(
+            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'),
@@ -81,7 +81,7 @@
                  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: AnimatedFacingImageRenderer(
+            self.WOLF_FORM: render.AnimatedFacingImageRenderer(
                 (self._get_image('werewolf_1.png'),
                  self._get_image('werewolf_1.png'),
                  self._get_image('werewolf_1.png'),
@@ -94,7 +94,7 @@
                  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: AnimatedFacingImageRenderer(
+            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'),
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nagslang/render.py	Tue Sep 03 23:33:44 2013 +0200
@@ -0,0 +1,163 @@
+import math
+
+import pygame
+import pymunk
+
+from nagslang.options import options
+
+
+class Renderer(object):
+    def set_game_object(self, game_object):
+        self.game_object = game_object
+
+    def _render_shape(self, surface):
+        shape = self.game_object.get_shape()
+        # Less general that pymunk.pygame_util.draw, but also a lot less noisy.
+        color = getattr(shape, 'color', pygame.color.THECOLORS['lightblue'])
+        # We only explicitly draw Circle and Poly shapes. Everything else we
+        # forward to pymunk.
+        if isinstance(shape, pymunk.Circle):
+            centre = pymunk.pygame_util.to_pygame(shape.body.position, surface)
+            radius = int(shape.radius)
+            pygame.draw.circle(surface, color, centre, radius, 2)
+        elif isinstance(shape, pymunk.Poly):
+            # polygon bounding box
+            points = [pymunk.pygame_util.to_pygame(p, surface)
+                      for p in shape.get_vertices()]
+            pygame.draw.lines(surface, color, True, points, 2)
+        else:
+            pymunk.pygame_util.draw(surface, shape)
+
+    def render(self, surface):
+        if options.debug:
+            self._render_shape(surface)
+
+    def animate(self):
+        # Used by time animatations to advance the clock
+        pass
+
+
+def image_pos(image, pos):
+    return (pos[0] - image.get_width() / 2,
+            pos[1] - image.get_height() / 2)
+
+
+class ImageRenderer(Renderer):
+    def __init__(self, image):
+        self._image = image
+
+    def get_image(self):
+        return self._image
+
+    def rotate_image(self, image):
+        angle = self.game_object.get_render_angle() * 180 / math.pi
+        return pygame.transform.rotate(image, angle)
+
+    def render_image(self, surface, image):
+        image = self.rotate_image(image)
+        pos = self.game_object.get_render_position(surface)
+        surface.blit(image, image_pos(image, pos))
+
+    def render(self, surface):
+        self.render_image(surface, self.get_image())
+        super(ImageRenderer, self).render(surface)
+
+
+class ImageStateRenderer(ImageRenderer):
+    def __init__(self, state_images):
+        self._state_images = state_images
+
+    def get_image(self):
+        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,
+        }
+        self._face = 'left'
+
+    def _update_facing(self, angle):
+        if abs(angle) < math.pi / 2:
+            self._face = 'right'
+        elif abs(angle) > math.pi / 2:
+            self._face = 'left'
+
+    def rotate_image(self, image):
+        # Facing images don't get rotated.
+        return image
+
+    def get_facing_image(self):
+        return self._images[self._face]
+
+    def get_image(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
+
+
+class ShapeRenderer(Renderer):
+    def render(self, surface):
+        self._render_shape(surface)
+        super(ShapeRenderer, self).render(surface)
+
+
+class ShapeStateRenderer(ShapeRenderer):
+    """Renders the shape in a different colour depending on the state.
+
+    Requires the game object it's attached to to have a puzzler.
+    """
+    def render(self, surface):
+        if self.game_object.puzzler.get_state():
+            color = pygame.color.THECOLORS['green']
+        else:
+            color = pygame.color.THECOLORS['red']
+
+        self.game_object.get_shape().color = color
+        super(ShapeStateRenderer, self).render(surface)