0
votes

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()
1
This is a lot of code to read through and reason about; you should probably trim some of the fat to make it easier for us to help you out (e.g. I'm pretty sure that everything involving images and colors in this code is irrelevant to this problem). - JesseTG
I just ran this code and I was able to move up and down while holding left or right. It moves diagonally. Hitting the walls some times makes you jump though. - cmd
That's the issue that I want to fix. I want to be able to run against a wall to the left and then, at the same time, press up and have the character run up while pushing against the wall. Take a look a the move method and you will see that the branch sets the y value of the rect after I start pressing down while pressing left while at the same time colliding against a wall. - daniel

1 Answers

0
votes

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