Skip to content

Commit

Permalink
Implement entity size and collision with map edges.
Browse files Browse the repository at this point in the history
  • Loading branch information
eevee committed Jun 25, 2012
1 parent 4deb4d0 commit 221168b
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 103 deletions.
13 changes: 7 additions & 6 deletions flora/data/spritesheets/characters.yaml
Expand Up @@ -2,23 +2,24 @@
cagroo:
filepath: characters/cagroo
scale: 0.25
radius: 40
poses:
standing:
DOWN:
anchor: [116, 53]
anchor: [116, 170]
frames:
- front3.png
LEFT:
anchor: [385, 74]
anchor: [385, 134]
frames:
- side3.png
UP:
anchor: [141, 197]
anchor: [141, 257]
frames:
- back3.png
walking:
DOWN:
anchor: [116, 53]
anchor: [116, 170]
frames:
- front4.png
- front5.png
Expand All @@ -27,7 +28,7 @@ cagroo:
- front2.png
- front3.png
LEFT:
anchor: [385, 74]
anchor: [385, 134]
frames:
- side4.png
- side5.png
Expand All @@ -36,7 +37,7 @@ cagroo:
- side2.png
- side3.png
UP:
anchor: [141, 197]
anchor: [141, 257]
frames:
- back4.png
- back5.png
Expand Down
25 changes: 24 additions & 1 deletion flora/model/map.py
Expand Up @@ -11,6 +11,7 @@

import flora
from flora.view.plane import Direction, UP, DOWN, LEFT, RIGHT
from flora.model.spritesheet import Spritesheet


class MapLayout(object):
Expand Down Expand Up @@ -121,5 +122,27 @@ class MapEntity(object):
"""

def __init__(self, entity_data):
self.spritesheet_name = entity_data['sprite']
sprite_name = entity_data['sprite']
self.sprite = Spritesheet.load(sprite_name)

self.initial_position = tuple(entity_data['position'])
scale2 = entity_data.get('scale', 1)
self.scale = self.sprite.scale * scale2
self.radius = self.sprite.radius * scale2

def register_view(self, entity_view):
self.view = entity_view

# TODO seems weird to have these live on the view.

@property
def position(self):
return Point2(*self.view.position)

@property
def size(self):
return Point2(self.view.width, self.view.height)

@property
def anchor(self):
return Point2(*self.view.image_anchor) * self.scale
128 changes: 54 additions & 74 deletions flora/model/spritesheet.py
Expand Up @@ -19,54 +19,67 @@ class Spritesheet(object):
facing.
"""

_loaded = {}

# Defaults
scale = 1

@classmethod
def load(cls, name):
if name in cls._loaded:
return cls._loaded[name]

# TODO put this in __init__
# TODO require a default pose
# TODO default angle to DOWN
# TODO simpler way to support a single sprite, like a flower
character_defs = yaml.load(pyglet.resource.file('spritesheets/characters.yaml'))
sprite_def = character_defs[name]

self = cls()
for pose, posedata in sprite_def['poses'].iteritems():
for anglename, angledata in posedata.iteritems():
angle = Direction._instances[anglename]
# TODO pull this out, i think? or maybe this whole thing
# should merge with add_pose
frames = [os.path.join('sprites', sprite_def['filepath'], frame) for frame in angledata['frames']]

self.add_pose(frames, pose, angle, anchor=angledata['anchor'])

if 'LEFT' in posedata and 'RIGHT' not in posedata:
self.flip_pose(pose, LEFT, RIGHT)
elif 'RIGHT' in posedata and 'LEFT' not in posedata:
self.flip_pose(pose, RIGHT, LEFT)

sprite_path = os.path.join('sprites', sprite_def['filepath'])
for pose_name, pose_data in sprite_def['poses'].iteritems():
for angle_name, angle_data in pose_data.iteritems():
pose = self._views.setdefault(pose_name, {})
if angle_name in pose:
raise ValueError

frame_textures = []
for frame_path in angle_data['frames']:
frame_textures.append(pyglet.resource.texture(
os.path.join(sprite_path, frame_path)))

animation = pyglet.image.Animation.from_image_sequence(
sequence=frame_textures,
period=0.1,
)

if 'anchor' in angle_data:
anchor = angle_data['anchor']
else:
anchor = animation.get_max_width() / 2, animation.get_max_height() / 2

angle = Direction._instances[angle_name]
pose[angle] = animation, anchor

if 'LEFT' in pose_data and 'RIGHT' not in pose_data:
self.flip_pose(pose_name, LEFT, RIGHT)
elif 'RIGHT' in pose_data and 'LEFT' not in pose_data:
self.flip_pose(pose_name, RIGHT, LEFT)

if 'scale' in sprite_def:
self.scale = sprite_def['scale']

# TODO is this part of an entity? seems like it... should be? should
# entity types be another middle layer? (yes)
self.radius = sprite_def['radius']

cls._loaded[name] = self
return self


def __init__(self, *args, **kwargs):
self._views = {}
# XXX don't hard-code this. also, what's a good size
self._txbin = pyglet.image.atlas.TextureBin(
texture_width=2048, texture_height=2048)
self._current_pose = None
self._current_angle = DOWN

def add_pose(self, filenames, view_name, angle, anchor=None):
# TODO support angle=ALL, and make it default?
# TODO support reflecting left/right
view = self._views.setdefault(view_name, {})
if angle in view:
raise ValueError

animation = pyglet.image.Animation.from_image_sequence(
sequence=[pyglet.resource.texture(fn) for fn in filenames],
period=0.1,
)
#animation.add_to_texture_bin(self._txbin)

if not anchor:
anchor = animation.get_max_width() / 2, animation.get_max_height() / 2

view[angle] = animation, anchor

def flip_pose(self, view_name, angle_from, angle_to):
# copies with a horizontal flip
Expand All @@ -80,41 +93,8 @@ def flip_pose(self, view_name, angle_from, angle_to):
view[angle_to] = new_animation, new_anchor


@property
def pose(self):
return self._current_pose

@pose.setter
def pose(self, value):
if value not in self._views:
raise KeyError

self._current_pose = value

@property
def angle(self):
return self._current_angle

@angle.setter
def angle(self, value):
if value not in (UP, DOWN, LEFT, RIGHT):
raise KeyError

self._current_angle = value


def _pick_image(self):
return self._views[self._current_pose][self._current_angle][0]

def _pick_anchor(self):
return self._views[self._current_pose][self._current_angle][1]


def set(self, pose=None, angle=None):
if pose is not None:
self.pose = pose

if angle is not None:
self.angle = angle
def pick_image(self, pose, angle):
return self._views[pose][angle][0]

return self._pick_image()
def pick_anchor(self, pose, angle):
return self._views[pose][angle][1]
34 changes: 29 additions & 5 deletions flora/view/debug.py
Expand Up @@ -10,34 +10,58 @@ def __init__(self, model):
super(DebugLayer, self).__init__()

self._model = model
self._batch = pyglet.graphics.Batch()
self.grid_batch = pyglet.graphics.Batch()

TEXT_OFFSET = 4
GRID_SIZE = 64

w, h = self._model.current_map.size

for x in range(0, w + 1, GRID_SIZE):
self._batch.add(2, gl.GL_LINES, None,
self.grid_batch.add(2, gl.GL_LINES, None,
('v2i', (x, 0, x, h)),
('c4f', (1.0, 1.0, 1.0, 0.5) * 2))

label = cocos.text.Label(str(x), position=(x + TEXT_OFFSET, 0 + TEXT_OFFSET), font_size=8)
self.add(label)

for y in range(0, h + 1, GRID_SIZE):
self._batch.add(2, gl.GL_LINES, None,
self.grid_batch.add(2, gl.GL_LINES, None,
('v2i', (0, y, w, y)),
('c4f', (1.0, 1.0, 1.0, 0.5) * 2))

label = cocos.text.Label(str(y), position=(0 + TEXT_OFFSET, y + TEXT_OFFSET), font_size=8)
self.add(label)

def draw(self):
super(DebugLayer, self).draw()
# Update list of entities
# TODO this should actually update instead of recomputing all day, but, whatever
entity_batch = pyglet.graphics.Batch()
for entity in self._model.current_map.entities:
x, y = entity.position
w, h = entity.size
x0, y0 = entity.position - entity.anchor
x1, y1 = x0 + w, y0 + h
entity_batch.add(4, gl.GL_LINE_LOOP, None,
('v2f', (x0, y0, x0, y1, x1, y1, x1, y0)),
('c4f', (1.0, 1.0, 0.0, 0.5) * 4))

# This is a diamond but at scale it looks enough like a dot
D = 3
entity_batch.add(4, gl.GL_QUADS, None,
('v2f', (x + D, y, x, y - D, x - D, y, x, y + D)),
('c4f', (1.0, 1.0, 0.0, 1.0) * 4))

# Collision footprint... kinda
R = entity.radius
entity_batch.add(4, gl.GL_QUADS, None,
('v2f', (x - R, y - R, x + R, y - R, x + R, y + R, x - R, y + R)),
('c4f', (1.0, 1.0, 0.0, 0.2) * 4))


# TODO wtf is GL_CURRENT_BIT
gl.glPushMatrix()
self.transform()
self._batch.draw()
self.grid_batch.draw()
entity_batch.draw()
gl.glPopMatrix()

0 comments on commit 221168b

Please sign in to comment.