0
\$\begingroup\$

I'm new to python and pygame and am trying to build a very simple "game" (it's more of a test for me to figure things out) in which you move a character around the screen and he can collide with walls and things. Everything is broken up into 32x32 tiles and the player is 32x64. This means that when his head hits a wall his feet are 2 tiles away and I've been trying to get it so that he's only 1 away, ie. I'm after a 32x32 hitbox starting at x=0, y=32 on the player image.

Do I need to create a separate rect to deal with this and if so how would I go about implementing it? Any help would be greatly appreciated.

Here's the code for my player class so far, hopefully it's not too messy:

class Player(pygame.sprite.Sprite):
'''The Main Player Class'''
def __init__(self, x, y):
    pygame.sprite.Sprite.__init__(self) #Call sprite initialiser
    self.image, self.rect = load_image("char.png")
    screen = pygame.display.get_surface()
    self.area = screen.get_rect()
    self.rect.x, self.rect.y = x, y
    self.x, self.y = x, y
    self.vx, self.vy = 0, 0
    self.movespeed = 2

def update(self):
    self._walk()

def _walk(self):
    if self.vx != 0:
        self._walk_single_axis(self.vx, 0)
    if self.vy != 0:
        self._walk_single_axis(0, self.vy)

def _walk_single_axis(self, vx, vy):
    "normalise for diagonal movement if necessary"
    if self.vx != 0 and self.vy !=0:
        norm = 1/(2**0.5)
    else:
        norm = 1
    "set new x and y positions"
    self.x += vx * self.movespeed * norm
    self.y += vy * self.movespeed * norm
    self.rect.x, self.rect.y = self.x, self.y

    "check for collisions with any walls"
    for wall in walls:
        if self.rect.colliderect(wall.rect):
            if vx > 0: #collide with the left side of the wall
                self.rect.right = wall.rect.left
                self.x = self.rect.x
            if vx < 0:
                self.rect.left = wall.rect.right
                self.x = self.rect.x
            if vy > 0:
                self.rect.bottom = wall.rect.top
                self.y = self.rect.y
            if vy < 0:
                self.rect.top = wall.rect.bottom
                self.y = self.rect.y

    "bound the player within the screen edges"
    if self.rect.x < self.area.left:
        self.rect.x = 0
    if self.rect.x > self.area.right-32:
        self.rect.x = self.area.right-32
    if self.rect.y < self.area.top:
        self.rect.y = 0
    if self.rect.y > self.area.bottom-64:
        self.rect.y = self.area.bottom-64

self.x and self.y are there because applying the movement directly to self.rect.x and self.rect.y caused the character to move at different speeds depending on which diagonal direction he was travelling in. self.vx and self.vy just return either +1 or -1 depending on the direction of travel.

\$\endgroup\$
2
  • \$\begingroup\$ I'm not sure what the problem is here. Is your player not responding to collisions properly? The code seems OK as long as only your player moves. \$\endgroup\$
    – Mokosha
    Mar 16, 2013 at 15:03
  • \$\begingroup\$ The collisions are working fine, but the collision box is the same size as the player image (32x64) where I would like it to be 32x32 and starting from halfway down the player image, so that the bounding box is at his feet. \$\endgroup\$ Mar 16, 2013 at 15:26

1 Answer 1

0
\$\begingroup\$

You want to split the rendering and collision detection into separate variables:

self.image, self.rect = load_image("char.png")
collisionRectPos = (self.rect.left, self.rect.top + 32)
collisionRectSz = (self.rect.width, self.rect.height - 32)
self.collisionrect = pygame.Rect(collisionRectPos, collisionRectSz)

Then when you go to do your collision detection, simply collide against the collision rect. Be sure to update your self.rect variable after you do your collision detection:

"set new x and y positions"
self.x += vx * self.movespeed * norm
self.y += vy * self.movespeed * norm
self.collisionrect.x = self.x
self.collisionrect.y = self.y + 32

for wall in walls:
    if self.collisionrect.colliderect(wall.rect):
        if vx > 0: #collide with the left side of the wall
            self.collisionrect.right = wall.rect.left
            self.x = self.collisionrect.x
        if vx < 0:
            self.collisionrect.left = wall.rect.right
            self.x = self.collisionrect.x
        if vy > 0:
            self.collisionrect.bottom = wall.rect.top
            self.y = self.collisionrect.y - 32
        if vy < 0:
            self.collisionrect.top = wall.rect.bottom
            self.y = self.collisionrect.y - 32

self.rect.x, self.rect.y = self.x, self.y
\$\endgroup\$
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .