Can't move up and down while holding left or right
Question: I am starting an RPG and when the character is running against a wall to
either side, I can't get the player to move up and down smoothly. Also, the
character can't move left or right smoothly while holding down the up and down
key and running against a wall to the north or south.

I've tried different configurations of the 'move' function with no success. I
know why the algorithm doesn't work, I just can't figure out how to construct
the branch so that when running to the right, it will set 'self.rect.right =
p.rect.left' without setting 'self.rect.bottom = p.rect.top' on the next
invocation of 'move()' when I start to press down while running against a
wall.

    
    
      def move(self, x, y, platforms):
            self.rect.left += x
            self.rect.top += y
    
            for p in platforms:
                if pygame.sprite.collide_rect(self, p):
                    if isinstance(p, ExitBlock):
                        pygame.event.post(pygame.event.Event(QUIT))
    
                    if self.x > 0: # Moving right
                        self.rect.right = p.rect.left
                    if self.x < 0: # Moving left
                        self.rect.left = p.rect.right
    
                    if self.y > 0: # Moving down
                        self.rect.bottom = p.rect.top
                    if self.y < 0: # Moving up
                        self.rect.top = p.rect.bottom
    

Here's the complete code you can run to see the unwanted behavior:

    
    
    #! /usr/bin/python
    
    import pygame, platform, sys
    platform.architecture()
    
    from pygame import *
    
    import spritesheet
    from sprite_strip_anim import SpriteStripAnim
    
    WIN_W = 1400
    WIN_H = 800
    HALF_W = int(WIN_W / 2)
    HALF_H = int(WIN_H / 2)
    
    DEPTH = 32
    FLAGS = 0
    CAMERA_SLACK = 30
    
    class Entity(pygame.sprite.Sprite):
        def __init__(self):
            pygame.sprite.Sprite.__init__(self)
    
    class Player(Entity):
        def __init__(self, x, y):
            Entity.__init__(self)
            self.x = 0
            self.y = 0
            self.onGround = False
            self.image = Surface((32,32))
            self.image.fill(Color("#0000FF"))
            self.image.convert()
            self.rect = Rect(x, y, 32, 32)
    
        def update(self, up, down, left, right, running, platforms):
            if up:
                self.y = -5
            if down:
                self.y = 5
            if left:
                self.x = -5
            if right:
                self.x = 5
            if not(left or right):
                self.x = 0
            if not(up or down):
                self.y = 0
    
            self.move(self.x, 0, platforms)
            self.move(0, self.y, platforms)
    
        def move(self, x, y, platforms):
            self.rect.left += x
            self.rect.top += y
    
            for p in platforms:
                if pygame.sprite.collide_rect(self, p):
                    if isinstance(p, ExitBlock):
                        pygame.event.post(pygame.event.Event(QUIT))
    
                    if self.x > 0: # Moving right
                        self.rect.right = p.rect.left
                    if self.x < 0: # Moving left
                        self.rect.left = p.rect.right
    
                    if self.y > 0: # Moving down
                        self.rect.bottom = p.rect.top
                    if self.y < 0: # Moving up
                        self.rect.top = p.rect.bottom
    
    
    class Platform(Entity):
        def __init__(self, x, y):
            Entity.__init__(self)
            self.image = Surface((32, 32))
            self.image.convert()
            self.image.fill(Color("#DDDDDD"))
            self.rect = Rect(x, y, 32, 32)
    
        def update(self):
            pass
    
    class ExitBlock(Platform):
        def __init__(self, x, y):
            Platform.__init__(self, x, y)
            self.image.fill(Color("#0033FF"))
    
    
    def main():
        pygame.init
    
        screen = pygame.display.set_mode((WIN_W, WIN_H), FLAGS, DEPTH)
        pygame.display.set_caption("Use arrows to move!")
        timer = pygame.time.Clock()
    
        up = down = left = right = running = False
        bg = Surface((32,32))
        bg.convert()
        bg.fill(Color("#000000"))
        entities = pygame.sprite.Group()
        player = Player(32, 32)
        platforms = []
    
        x = y = 0
        level = [
            "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",
            "P                                          P",
            "P                                          P",
            "P                                          P",
            "P                    PPPPPPPPPPP           P",
            "P                                          P",
            "P                                          P",
            "P                                          P",
            "P    PPPPPPPP                              P",
            "P                                          P",
            "P                          PPPPPPP         P",
            "P                 PPPPPP                   P",
            "P                                          P",
            "P         PPPPPPP                          P",
            "P                                          P",
            "P                     PPPPPP               P",
            "P                                          P",
            "P   PPPPPPPPPPP                            P",
            "P                                          P",
            "P                 PPPPPPPPPPP              P",
            "P                                          P",
            "P                                          P",
            "P                                          P",
            "P                                          P",
            "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",]
    
        # build the level
        for row in level:
            for col in row:
                if col == "P":
                    p = Platform(x, y)
                    platforms.append(p)
                    entities.add(p)
                if col == "E":
                    e = ExitBlock(x, y)
                    platforms.append(e)
                    entities.add(e)
                x += 32
            y += 32
            x = 0
    
        total_level_width  = len(level[0])*32
        total_level_height = len(level)*32
    
        entities.add(player)
    
        while 1:
            timer.tick(60)
    
            # draw background
            for y in range(32):
                for x in range(32):
                    screen.blit(bg, (x * 32, y * 32))
    
            for e in pygame.event.get():
                if e.type == QUIT: raise SystemExit, "QUIT"
                if e.type == KEYDOWN and e.key == K_ESCAPE:
                    raise SystemExit, "ESCAPE"
                if e.type == KEYDOWN and e.key == K_w:
                    up = True
                    down = False
                if e.type == KEYDOWN and e.key == K_s:
                    down = True
                    up = False
                if e.type == KEYDOWN and e.key == K_a:
                    left = True
                    right = False
                if e.type == KEYDOWN and e.key == K_d:
                    right = True
                    left = False
    
                if e.type == KEYUP and e.key == K_w:
                    up = False
                if e.type == KEYUP and e.key == K_s:
                    down = False
                if e.type == KEYUP and e.key == K_d:
                    right = False
                if e.type == KEYUP and e.key == K_a:
                    left = False
    
            # update player, draw everything else
            player.update(up, down, left, right, running, platforms)
    
            for e in entities:
                screen.blit(e.image, e.rect)
    
            pygame.display.update()
    
    
    if __name__ == "__main__":
        main()
    


Answer: Found an algorithm that works @ <http://pygame.org/project-
Rect+Collision+Response-1061-.html>:

    
    
    def move(self, dx, dy):
    
        # Move each axis separately. Note that this checks for collisions both times.
        if dx != 0:
            self.move_single_axis(dx, 0)
        if dy != 0:
            self.move_single_axis(0, dy)
    
    def move_single_axis(self, dx, dy):
    
        # Move the rect
        self.rect.x += dx
        self.rect.y += dy
    
        # If you collide with a wall, move out based on velocity
        for wall in walls:
            if self.rect.colliderect(wall.rect):
                if dx > 0: # Moving right; Hit the left side of the wall
                    self.rect.right = wall.rect.left
                if dx < 0: # Moving left; Hit the right side of the wall
                    self.rect.left = wall.rect.right
                if dy > 0: # Moving down; Hit the top side of the wall
                    self.rect.bottom = wall.rect.top
                if dy < 0: # Moving up; Hit the bottom side of the wall
                    self.rect.top = wall.rect.bottom
    

