user1758231
user1758231

Reputation: 1001

Pygame Collison Detection

MY PROBLEM:

At the point I'm at in my game I want to add a couple features like double jumps, wall jumps, etc.
However I've run into a problem with my collision detection and I can't seem to straighten it out.
The problem is that when my character collides with the ground it registers a collision.
However the next tick it no longer is registering a collision even if I'm on the ground.
This creates problems with detecting if I've collided with a wall or if I'm currently colliding with the ground etc.
This creates even more of a problem because when its not detecting a collision my y-velocity will constantly decrease.
So, my player has a constant y-velocity that is increasing instead of it just being 0 when on the ground.

EXAMPLE:

As you can see in the left hand corner my y-velocity is bouncing through numbers like this:

0  
0  
0.3  
0.6  
0.9

then it starts over.
What I really need is for this to constantly be 0 when I'm on the ground and for the code to recognize that I'm on the ground.
But It's not and I can't figure out why, Can anyone help?

CODE:

import pygame
from pygame import *

WIN_WIDTH = 800
WIN_HEIGHT = 640
HALF_WIDTH = int(WIN_WIDTH / 2)
HALF_HEIGHT = int(WIN_HEIGHT / 2)

DISPLAY = (WIN_WIDTH, WIN_HEIGHT)
DEPTH = 32
FLAGS = 0
CAMERA_SLACK = 30

def main():
    pygame.init()
    screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH)
    #pygame.display.set_caption("Use arrows to move!")
    timer = pygame.time.Clock()

    up = down = left = right = space = False
    bg = Surface((32,32))
    bg.convert()
    bg.fill(Color("#0094FF"))
    entities = pygame.sprite.Group()
    player = Player(32, 32)
    platforms = []
    plants = []

    space_num = 0

    x = 0
    y = 0
    level = [
        "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                           PPPP     PPPPPPP",
        "P                           PPPP     PPPPPPP",
        "P                           PPPP     PPPPPPP",
        "P                           PPPP     PPPPPPP",
        "P                           PPPP     PPPPPPP",
        "P                           PPPP     PPPPPPP",
        "P                           PPPP     PPPPPPP",
        "P                           PPPP     PPPPPPP",
        "P                           PPPP     PPPPPPP",
        "P                           PPPP     PPPPPPP",
        "P                           PPPP     PPPPPPP",
        "P                           PPPP     PPPPPPP",
        "P                           PPPP     PPPPPPP",
        "P                           PPPP     PPPPPPP",
        "P                           PPPP     PPPPPPP",
        "P                           PPPP     PPPPPPP",
        "P                           PPPP     PPPPPPP",
        "P                           PPPP     PPPPPPP",
        "P                           PPPP     PPPPPPP",
        "P                                       PPPP",
        "P                                       PPPP",
        "P                                       PPPP",
        "PPPPPPPPPPPPPPP           PPPPPPPPPPPPPPPPPP",
        "PPPPPPPPPPPPPPP           PPPPPPPPPPPPPPPPPP",
        "PPPPPPPPPPPPPPP           PPPPPPPPPPPPPPPPPP",
        "PPPPPPPPPPPPPPP           PPPPPPPPPPPPPPPPPP",
        "PPPPPPPPPPPPPPP           PPPPPPPPPPPPPPPPPP",
        "PPPPPPPPPPPPPPP           PPPPPPPPPPPPPPPPPP",
        "PPPPPPPPPPPPPPP           PPPPPPPPPPPPPPPPPP",
        "PPPPPPPPPPPPPPP           PPPPPPPPPPPPPPPPPP",
        "PPPPPPPPPPPPPPP           PPPPPPPPPPPPPPPPPP",
        "PPPPPPPPPPPPPPP           PPPPPPPPPPPPPPPPPP",
        "PPPPPPPPPPPPPPP           PPPPPPPPPPPPPPPPPP",
        "PPPPPPPPPPPPPPP           PPPPPPPPPPPPPPPPPP",]
    # 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)
            if col == "p":
                p = Plant(x, y)
                plants.append(p)
                entities.add(p)
            x += 32
        y += 32
        x = 0

    total_level_width = len(level[0]) * 32
    total_level_height = len(level) * 32
    camera = Camera(complex_camera, total_level_width, total_level_height)
    entities.add(player)

    while 1:
        timer.tick(60)

        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_UP:
                up = True
            if e.type == KEYDOWN and e.key == K_DOWN:
                down = True
            if e.type == KEYDOWN and e.key == K_LEFT:
                left = True
            if e.type == KEYDOWN and e.key == K_RIGHT:
                right = True
            if e.type == KEYDOWN and e.key == K_SPACE:
                if space_num < 6:
                    space = True
                    space_num += 1

            if e.type == KEYUP and e.key == K_UP:
                up = False
            if e.type == KEYUP and e.key == K_DOWN:
                down = False
            if e.type == KEYUP and e.key == K_LEFT:
                left = False
            if e.type == KEYUP and e.key == K_RIGHT:
                right = False
            if e.type == KEYUP and e.key == K_SPACE:
                space = False

        # draw background
        for y in range(20):
            for x in range(25):
                screen.blit(bg, (x * 32, y * 32))

        camera.update(player)

        # update player, draw everything else
        player.update(up, down, left, right, platforms, space)
        for e in entities:
            screen.blit(e.image, camera.apply(e))
        pygame.display.update()

class Camera(object):
    def __init__(self, camera_func, width, height):
        self.camera_func = camera_func
        self.state = Rect(0, 0, width, height)

    def apply(self, target):
        return target.rect.move(self.state.topleft)

    def update(self, target):
        self.state = self.camera_func(self.state, target.rect)

#def simple_camera(camera, target_rect):
#    l, t, _, _ = target_rect
#    _, _, w, h = camera
#    return Rect(-l+HALF_WIDTH, -t+HALF_HEIGHT, w, h)

def complex_camera(camera, target_rect):
    l, t, _, _ = target_rect
    _, _, w, h = camera
    l, t, _, _ = -l + HALF_WIDTH, -t+HALF_HEIGHT, w, h

    l = min(0, l)                           # stop scrolling left
    l = max(-(camera.width-WIN_WIDTH), l)   # stop scrolling right
    t = max(-(camera.height-WIN_HEIGHT), t) # stop scrolling bottom

    return Rect(l, t, w, h)

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.xvel = 0
        self.yvel = 0
        self.onGround = False
        self.image = Surface((32,32))
        self.image.fill(Color("#0000FF"))
        self.image.convert()
        self.rect = Rect(400, 400, 32, 32)

    def update(self, up, down, left, right, platforms, space):
        if space:
            if not self.onGround:
                self.yvel = -12
            if self.onGround:
                self.yvel = 0
        else:
            if up:
                if self.onGround: self.yvel -= 10 # only jump if on the ground
            if down:
                pass
            if left:
                self.xvel = -8
            if right:
                self.xvel = 8
            if not self.onGround:
                self.yvel += 0.3 # only accelerate with gravity if in the air
                if self.yvel > 40: self.yvel = 40 # max falling speed
            if not(left or right):
                self.xvel = 0

            self.rect.left += self.xvel # increment in x direction
            self.collide(self.xvel, 0, platforms) # do x-axis collisions
            self.rect.top += self.yvel # increment in y direction
            self.onGround = False; # assuming we're in the air
            self.collide(0, self.yvel, platforms) # do y-axis collisions

    def collide(self, xvel, yvel, platforms):
        for p in platforms:
            if pygame.sprite.collide_rect(self, p):
                if isinstance(p, ExitBlock):
                    pygame.event.post(pygame.event.Event(QUIT))
                if xvel > 0: self.rect.right = p.rect.left
                if xvel < 0: self.rect.left = p.rect.right
                if yvel > 0:
                    self.rect.bottom = p.rect.top
                    self.onGround = True
                    self.yvel = 0
                if yvel < 0: 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 = pygame.image.load("bush.png")

class Decoration(Entity):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)

class Plant(Decoration):
    def __init__(self, x, y):
        Entity.__init__(self)
        self.image = Surface((64, 64))
        self.image.convert()
        #self.image = pygame.image.load("bush.png")
        self.rect = Rect(x, y, 64, 64)




if __name__ == "__main__":
    main()

Upvotes: 1

Views: 1175

Answers (1)

Patashu
Patashu

Reputation: 21773

It looks like whenever space is not held down, the code for update will set you as being in the air:

def update(self, up, down, left, right, platforms, space):
    if space:
    else:
        self.onGround = False; # assuming we're in the air
        self.collide(0, self.yvel, platforms) # do y-axis collisions

Perhaps pygame's collision code does not detect a collision if the character doesn't move a single pixel to overlap the platform, and is instead exactly on it as far as it's concerned? In such a case, you'd go through a loop of landed, set yvel to 0. Ok, we're falling, 0.3, falling, 0.6, falling, 0.9, falling... OK we moved 1 pixel and we overlapped, we landed, set yvel to 0. And repeat at the beginning.

Upvotes: 1

Related Questions