Mercurial > skaapsteker
view skaapsteker/sprites/player.py @ 200:4e3f9cb49489
Fix bug in jump to tile top logic
author | Neil Muller <drnlmuller@gmail.com> |
---|---|
date | Wed, 06 Apr 2011 22:26:17 +0200 |
parents | 52344e9cab82 |
children | a11325bc5ff0 |
line wrap: on
line source
"""Class for dealing with the player""" import pygame.transform import os import time from skaapsteker.sprites.base import TILE_SIZE, PC_LAYER, MONSTER_LAYER from skaapsteker.physics import Sprite from skaapsteker.constants import Layers from skaapsteker.data import get_files, load_image class Player(Sprite): collision_layer = PC_LAYER collides_with = set([MONSTER_LAYER]) wants_updates = True def __init__(self): Sprite.__init__(self) self.image = None self.rect = None self._image_dict = {} self._animation_frame = 0.0 self._last_time = time.time() # State flags and such self.running = False self.jumping = False self.flying = False self._load_images() # We muck with these in load for convience, so ensure they're right self.tails = 0 self.set_facing('left') self.set_image() self.set_pos((0, 0)) self._collisions_seen = 0 self._last_collide = [] self._layer = Layers.PLAYER def set_image(self): key = self._make_key() images = self._image_dict[key] if self._animation_frame >= len(images): self._animation_frame = 0.0 if self.rect: cur_pos = self.collide_rect.midbottom else: cur_pos = (0, 0) # TODO: can save a lot of calculation here by caching collision rects cand_image = images[int(self._animation_frame)] cand_collide_rect = cand_image.get_bounding_rect(1).inflate(-2,-2) cand_rect = cand_image.get_rect() cand_rect_offset = cand_rect.centerx - cand_collide_rect.centerx, cand_rect.bottom - cand_collide_rect.bottom cand_rect.midbottom = cur_pos[0] + cand_rect_offset[0], cur_pos[1] + cand_rect_offset[1] cand_collide_rect.midbottom = cur_pos if not self.check_collide_rect(cand_collide_rect, cand_rect, cand_image): return self.image = cand_image self.collide_rect = cand_collide_rect self.rect = cand_rect self.rect_offset = cand_rect_offset self.init_pos() def update(self): v_x, v_y = self.velocity # Never animate slower than !7 fps, never faster than ~15 fps old_frame = self._animation_frame self._animation_frame += abs(v_x) / 300 time_diff = time.time() - self._last_time if int(self._animation_frame) - int(old_frame) > 0: # Check time diff if time_diff < 0.10: # Delay animation frame jump self._animation_frame -= abs(v_x) / 300 else: self._last_time = time.time() elif time_diff > 0.20: # Force animation frame jump self._animation_frame = old_frame + 1 self._last_time = time.time() if abs(v_x) < 80: # Clamp when we're not moving at least 5 pixel / s self.velocity = (0, v_y) self.running = not self.on_solid # if you're not on something you can't stand else: self.velocity = (0, v_y) # Standard platformer physics self.running = True self.set_image() if self._collisions_seen > 2 * len(self._last_collide): # Can we find a position "nearby" that reduces the collision # surface best_move = (0, 0) clip_area = 0 for obj in self._last_collide[:]: if not obj.collide_rect.colliderect(self.collide_rect): # Prune stale objects from the list self._last_collide.remove(obj) continue clip = obj.collide_rect.clip(self.collide_rect) clip_area += clip.width * clip.height if (obj.floor or obj.block) and \ clip.width > TILE_SIZE[0] / 2 and \ self.collide_rect.bottom < obj.collide_rect.top + TILE_SIZE[1] / 3: delta = self.rect.bottom - self.collide_rect.bottom self.collide_rect.bottom = obj.collide_rect.top - 1 self.rect.bottom = self.collide_rect.bottom + delta self.init_pos() return # Jump out of this case min_area = clip_area for attempt in [(0, 2), (2, 0), (-2, 0), (2, 2), (-2, 2)]: clip_area = 0 for obj in self._last_collide: cand_rect = self.collide_rect.move(attempt) clip = obj.collide_rect.clip(cand_rect) clip_area += clip.width * clip.height if clip_area < min_area: min_area = clip_area best_move = attempt self.collide_rect.move_ip(best_move) self.rect.move_ip(best_move) self.init_pos() self._last_collide = [] self._collisions_seen = 0 def set_facing(self, new_facing): self.facing = new_facing def collided(self, other): if other not in self._last_collide: self._last_collide.append(other) self._collide_pos = self.collide_rect.midbottom self._collisions_seen = 0 else: self._collisions_seen += 1 def set_pos(self, pos): self.starting_tile_pos = pos self.rect.midbottom = pos[0] * TILE_SIZE[0] + self.rect_offset[0], pos[1] * TILE_SIZE[1] + self.rect_offset[1] self.collide_rect.midbottom = pos[0] * TILE_SIZE[0], pos[1] * TILE_SIZE[1] def action_left(self): if self.facing != 'left': self.facing = 'left' self.set_image() self.deltav((-450.0, 0.0)) def action_right(self): if self.facing != 'right': self.facing = 'right' self.set_image() self.deltav((450.0, 0.0)) def action_up(self): if self.on_solid: self.deltav((0.0, -self.terminal_velocity[1])) self.on_solid = False def action_down(self): self.deltav((0.0, 100.0)) def action_fire1(self): print "F1" def action_fire2(self): print "F2" def _get_action(self): if self.running: return 'running' if self.jumping: return 'jumpin' return 'standing' def _make_key(self, action=None): if action is None: action = self._get_action() tails = self.tails if self.tails >= 4: tails = 4 elif self.tails >= 2: tails = 2 return '%s %s %d' % (action, self.facing, tails) def _load_images(self): for action in ['standing', 'running', 'jumping', 'attacking']: for tails in [0, 1, 2, 4]: self.tails = tails directory = os.path.join('sprites', 'kitsune_%s' % action, 'kitsune_%s_%dtail' % (action, tails)) for facing in ['left', 'right']: self.facing = facing key = self._make_key(action) self._image_dict[key] = [] for image_file in get_files(directory): if image_file.startswith('.'): # Skip extra junk for now continue image = load_image(os.path.join(directory, image_file)) if facing == 'right': image = pygame.transform.flip(image, True, False) self._image_dict[key].append(image)