def collision_resolution(entity, x, y): # basic platformer collision resolution routine # TODO fix spacing. for some reason certain collisions allow for a gap in # space (between 1 and 2 pixels wide) vel_x = entity.velocity_x vel_y = entity.velocity_y collision_results = collides_solid(entity.container, entity.at_point(x, y)) for result in collision_results: colliding = result.function other = result.shape # Check if collision is resolved, on y axis, or on x axis test_a = (other, entity.at_point(x, y)) test_b = (other, entity.at_point(x, entity.y)) test_c = (other, entity.at_point(entity.x, y)) if not result.is_first: test_a = test_a[1], test_a[0] test_b = test_b[1], test_b[0] test_c = test_c[1], test_c[0] # collision already resolved if not colliding(*test_a): continue # collision resolved by reverting y axis elif not colliding(*test_b): if y - entity.height - vel_y < other.y and vel_y > 0: y = other.y - entity.height elif y + vel_y > other.y - other.height and vel_y < 0: y = other.y + other.height vel_y = 0 # collision resolved by reverting x axis elif not colliding(*test_c): if x - entity.width - vel_x < other.x and vel_x > 0: x = other.x - entity.width elif x + vel_x > other.x - other.width and vel_x < 0: x = other.x + other.width vel_x = 0 # collision resoultion not found, revert to original space else: x = entity.x y = entity.y vel_x = 0 vel_y = 0 break collision_occurred = len(collision_results) > 0 return collision_occurred, x, y, vel_x, vel_y
def update(self): temp_x = self.x temp_y = self.y has_solid_below = solid_below(self, self.x, self.y) # Key polling keydown_up = Key.down('up') keydown_down = Key.down('down') keydown_left = Key.down('left') keydown_right = Key.down('right') keydown_run = False # Key.down('lshift') keypressed_up = Key.pressed('up') keypressed_down = Key.pressed('down') keypressed_left = Key.pressed('left') keypressed_right = Key.pressed('right') keypressed_jump = Key.pressed('space') keypressed_gadget = Key.pressed('x') if self.state == Player.STATE_STANDARD: # Run/Walk if keydown_left == keydown_right: self.velocity_x = 0 self.walking_sound.stop() else: if keydown_left: if keydown_run: self.velocity_x = -Player.SPEED_FAST else: self.velocity_x = -Player.SPEED self.facing_x = -1 if keydown_right: if keydown_run: self.velocity_x = Player.SPEED_FAST else: self.velocity_x = Player.SPEED self.facing_x = 1 # Play walking sound effect if has_solid_below: self.walking_sound.play(-1) else: self.walking_sound.stop() # Push block block = peachy.collision.collides_group( self.container, 'block', self.at_point(temp_x + self.velocity_x, self.y)) if len(block) == 1 and has_solid_below: block = block[0] block_temp_x = block.x + self.velocity_x if not peachy.collision.collides_solid( self.container, block.at_point(block_temp_x, block.y)): self.change_state(Player.STATE_PUSHING, block=block) if self.x < block.x: self.x = block.x - self.width else: self.x = block.x + block.width return # Jump if keypressed_jump and has_solid_below: self.velocity_y = -Player.JUMP_FORCE # Wall Grab if self.velocity_y < 4 and self.velocity_y > 0: climb_wall = [] if keydown_left: climb_wall = solid_left(self, self.x, self.y) if keydown_right: climb_wall = solid_right(self, self.x, self.y) ledge = None for wall in climb_wall: if wall.y - self.y < 3 and not wall.member_of('block'): if self.y <= wall.y: ledge = wall else: ledge = None if ledge is not None: test = Rect(self.x, ledge.y - Player.HEIGHT_CROUCH, self.width, Player.HEIGHT_CROUCH) test.container = self.container if test.x + test.width <= ledge.x: test.x = ledge.x elif test.x >= ledge.x + ledge.width: test.x = ledge.x + ledge.width - test.width if not collides_solid(self.container, test): self.change_state(Player.STATE_LEDGEGRAB, ledge=ledge) return # Interact if keydown_up: interactables = collides_group(self.container, 'interact', self) for interact in interactables: if interact.member_of('rope'): vertical = False if interact.width == 0: vertical = True elif interact.height == 0: vertical = False self.change_state(Player.STATE_CLIMBING, handle=interact, vertical=vertical) interact.attach(self) return elif interact.member_of('ladder'): self.x = interact.x self.change_state(Player.STATE_CLIMBING, handle=interact, vertical=True) return elif keypressed_up: if interact.member_of('button'): interact.press() elif interact.member_of('door'): if self.gadget.state == gadgets.Gadget.STATE_ACTIVE: self.gadget.cancel() interact.enter() elif interact.member_of('lever'): interact.pull() elif interact.member_of('hiding-spot'): self.change_state(Player.STATE_HIDDEN, hiding_spot=interact) return elif interact.member_of('message-box'): interact.activate() return # Climb down ladder if keypressed_down: ladder = collides_group(self.container, 'ladder', self.at_point(self.x, self.y + 1)) if ladder: ladder = ladder[0] if self.y + self.height < ladder.y + ladder.height: self.x = ladder.x if self.y < ladder.y: self.y = ladder.y self.change_state(Player.STATE_CLIMBING, handle=ladder, vertical=True) return # Pickup if keypressed_up: pickups = collides_group(self.container, 'pickup', self) for pickup in pickups: if pickup.member_of('gadget'): self.change_gadget(pickup.gadget) elif pickup.member_of('key'): self.obtained_keys.append(pickup.tag) self.key_count += 1 pickup.destroy() # Crouching if keypressed_down and has_solid_below: self.change_state(Player.STATE_CROUCHING) return # Gravity if self.velocity_y < config.MAX_GRAVITY: self.velocity_y += config.GRAVITY elif self.state == Player.STATE_CLIMBING: if keypressed_jump: self.velocity_y = -Player.JUMP_FORCE self.change_state(Player.STATE_STANDARD) return handle = self.state_args['handle'] vertical = self.state_args['vertical'] if vertical: if keydown_up == keydown_down: self.velocity_y = 0 else: if keydown_up: if self.y > handle.y: self.velocity_y = -Player.SPEED_SLOW else: self.velocity_y = 0 if handle.member_of('ladder'): self.y = handle.y - self.height self.change_state(Player.STATE_STANDARD) return if keydown_down: if self.y + self.height < handle.y + handle.height: self.velocity_y = Player.SPEED_SLOW else: self.velocity_y = 0 self.change_state(Player.STATE_STANDARD) return else: if keypressed_down: self.change_state(Player.STATE_STANDARD) return if keydown_left == keydown_right: self.velocity_x = 0 else: if keydown_left: if self.x > handle.x: self.velocity_x = -Player.SPEED_SLOW else: self.velocity_x = 0 if keydown_right: if self.x + self.width < handle.x + handle.width: self.velocity_x = Player.SPEED_SLOW else: self.velocity_x = 0 elif self.state == Player.STATE_CROUCHING: if keydown_left == keydown_right: self.velocity_x = 0 else: if keydown_left: self.velocity_x = -Player.SPEED_SLOW self.facing_x = -1 if keydown_right: self.velocity_x = Player.SPEED_SLOW self.facing_x = 1 # attempt to grab ledge when crawling off one tx = self.velocity_x + self.x if not solid_below(self, tx, self.y): ledges = solid_below(self, self.x, self.y) try: ledge = ledges[0] if self.velocity_x > 0: test = Rect(ledge.x + ledge.width, ledge.y, self.width, Player.HEIGHT_STANDARD) else: test = Rect(ledge.x - self.width, ledge.y, self.width, Player.HEIGHT_STANDARD) test.container = self.container if not collides_solid(self.container, test): self.x = test.x self.facing_x *= -1 self.change_state(Player.STATE_LEDGEGRAB, ledge=ledge) return except IndexError: pass attempt_stand = False if keypressed_jump and has_solid_below: self.velocity_y = -Player.JUMP_FORCE attempt_stand = True if not keydown_down: attempt_stand = True if attempt_stand: self.height = Player.HEIGHT_STANDARD temp_y = self.y - \ abs(Player.HEIGHT_STANDARD - Player.HEIGHT_CROUCH) if collides_solid(self.container, self.at_point(self.x, temp_y)): self.height = Player.HEIGHT_CROUCH temp_y = self.y self.velocity_y = 0 else: self.state = Player.STATE_STANDARD self.y = temp_y self.height = Player.HEIGHT_STANDARD if collides_solid(self.container, self.at_point(temp_x, temp_y)): self.height = Player.HEIGHT_CROUCH else: self.state = Player.STATE_STANDARD elif self.state == Player.STATE_HIDDEN: if keypressed_up or keypressed_left or keypressed_right: self.change_state(Player.STATE_STANDARD) elif self.state == Player.STATE_LEDGEGRAB: # Climb up ledge if keypressed_up or keypressed_left or keypressed_right: ledges = None if keydown_up: try: ledges = solid_left(self, self.x, self.y) assert ledges[0].y == self.y except (AssertionError, IndexError): ledges = solid_right(self, self.x, self.y) elif keydown_left: ledges = solid_left(self, self.x, self.y) elif keydown_right: ledges = solid_right(self, self.x, self.y) try: ledge = ledges[0] assert ledge.y == self.y test = peachy.geo.Rect(self.x, ledge.y - Player.HEIGHT_CROUCH, self.width, Player.HEIGHT_CROUCH) test.container = self.container if test.x + test.width <= ledge.x: test.x = ledge.x elif test.x >= ledge.x + ledge.width: test.x = ledge.x + ledge.width - test.width if not collides_solid(self.container, test): self.change_state(Player.STATE_CROUCHING) self.x = test.x self.y = test.y return except (AssertionError, IndexError): pass # Let go of ledge if keypressed_left and not solid_left(self, self.x, self.y) or \ keypressed_right and not solid_right(self, self.x, self.y) or \ keypressed_down: self.state = Player.STATE_STANDARD if keypressed_jump: self.state = Player.STATE_STANDARD self.velocity_y = -Player.JUMP_FORCE elif self.state == Player.STATE_PUSHING: block = self.state_args['block'] if self.x > block.x and keydown_left: self.velocity_x = -Player.SPEED_SLOW elif self.x < block.x and keydown_right: self.velocity_x = Player.SPEED_SLOW else: self.change_state(Player.STATE_STANDARD) return if keypressed_jump: self.velocity_y = -Player.JUMP_FORCE self.change_state(Player.STATE_STANDARD) return test_x = temp_x + self.velocity_x if not rect_rect(self.at_point(test_x, self.y), block): self.change_state(Player.STATE_STANDARD) return block_temp_x = block.x + self.velocity_x if not collides_solid(self.container, block.at_point(block_temp_x, block.y)): block.x = block_temp_x # GADGET if self.state != Player.STATE_DEAD: if keypressed_gadget and self.gadget.name: self.gadget.use() # ADVANCE temp_x += self.velocity_x temp_y += self.velocity_y # LOCKED DOOR lock = collides_group(self.container, 'locked-door', self.at_point(temp_x, temp_y)) if lock: lock = lock[0] if self.key_count > 0: self.key_count -= 1 self.container.unlocked_doors.append(lock.tag) lock.destroy() # FINALIZE c, self.x, self.y, self.velocity_x, self.velocity_y = \ collision_resolution(self, temp_x, temp_y) self.gadget.update()
def solid_left(entity, x, y): return collides_solid(entity.container, entity.at_point(x - 1, y))
def solid_right(entity, x, y): return collides_solid(entity.container, entity.at_point(x + 1, y))
def solid_below(entity, x, y): return collides_solid(entity.container, entity.at_point(x, y + 1))