0
\$\begingroup\$

I've been trying to create a collision detection between the player (Blue), and the walls (Green), but the player can just move through the walls. I made it so that every collision is printed in the console, just to see if the code was even detecting collisions. When I ran this version of the program, it was constantly printing "collide," indicating that there was always a collision detected, but even so, none of my code for when a collision was detected ran. Here is my code:

import pygame, random, sys
from pygame.locals import *

# Set FPS
FPS = 30

# Window Dimensions
WINDOWWIDTH = 640
WINDOWHEIGHT = 480

# Colors
BLUE = (0, 0, 255)
BLACK = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)

# Key Constants
UP = 'up'
DOWN = 'down'
RIGHT = 'right'
LEFT = 'left'

class Player(pygame.sprite.Sprite):

    def __init__(self, x, y):
        # Call the parent's constructor
        pygame.sprite.Sprite.__init__(self)

        # Set Properties
        self.image = pygame.Surface((30, 30))
        self.image.fill(BLUE)
        self.rect = self.image.get_rect()
        self.width = self.image.get_width()
        self.height = self.image.get_height()
        self.x = x
        self.y = y
        self.vx = 0
        self.vy = 0
        self.vel = 5

    def onKeyDown(self, event): # Move 3 pixels when a directional key is pressed
        if event.key == K_LEFT:
            self.vx = -self.vel
        if event.key == K_RIGHT:
            self.vx = self.vel
        if event.key == K_UP:
            self.vy = -self.vel
        if event.key == K_DOWN:
            self.vy = self.vel

    def onKeyUp(self, event): # Stop moving when key is released
        if event.key == K_LEFT or event.key == K_RIGHT:
            self.vx = 0
        if event.key == K_UP or event.key == K_DOWN:
            self.vy = 0

    def update(self):
        # Move Player
        self.x += self.vx
        self.y += self.vy

        # Check for Collisions
        for wall in wallList:
            if pygame.sprite.collide_rect(self, wall):
                print("collide")
                if self.vx > 0:
                    self.rect.right = wall.rect.left
                if self.vx < 0:
                    self.rect.left = wall.rect.right
                if self.vy > 0:
                    self.rect.bottom = wall.rect.top
                if self.vy < 0:
                    self.rect.top = wall.rect.bottom


class Enemy(pygame.sprite.Sprite):

    def __init__(self, x, y):
        # Call the parent's constructor
        pygame.sprite.Sprite.__init__(self)

        self.image = pygame.Surface((30, 30))
        self.image.fill(RED)
        self.rect = self.image.get_rect()
        self.width = self.image.get_width()
        self.height = self.image.get_height()
        self.x = x
        self.y = y
        self.vel = 2

    def move(self):
        self.y += self.vel

class Wall(pygame.sprite.Sprite):

    def __init__(self, x, y):
        # Call the parent's constructor
        pygame.sprite.Sprite.__init__(self)

        self.image = pygame.Surface((30, 30))
        self.image.fill(GREEN)
        self.rect = self.image.get_rect()
        self.width = self.image.get_width()
        self.height = self.image.get_height()
        self.x = x
        self.y = y

def main():
    global FPSCLOCK, DISPLAYSURF, wallList, enemyList, walls
    pygame.init()
    random.seed()

    FPSCLOCK = pygame.time.Clock()
    DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
    pygame.display.set_caption('Lembalo')
    player = Player(WINDOWWIDTH / 2, WINDOWHEIGHT - 100)

    enemyList = [
        Enemy(random.randint(0, WINDOWWIDTH - 30), random.randint(0, WINDOWHEIGHT - 430)),
        Enemy(random.randint(0, WINDOWWIDTH - 30), random.randint(0, WINDOWHEIGHT - 430)),
        Enemy(random.randint(0, WINDOWWIDTH - 30), random.randint(0, WINDOWHEIGHT - 430)),
        Enemy(random.randint(0, WINDOWWIDTH - 30), random.randint(0, WINDOWHEIGHT - 430)),
        Enemy(random.randint(0, WINDOWWIDTH - 30), random.randint(0, WINDOWHEIGHT - 430))
        ]

    wallList = [
        Wall(70, 100),
        Wall(100, 100),
        Wall(130, 100),
        Wall(160, 100),
        Wall(190, 100),
        Wall(220, 100)
        ]

    allsprites = pygame.sprite.Group()

    allsprites.add(player)
    allsprites.add(enemyList)
    allsprites.add(wallList)

    while True:

        DISPLAYSURF.fill(BLACK) # Drawing the window

        for event in pygame.event.get(): # Event handling
            if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
                pygame.quit()
                sys.exit()
            if event.type == KEYDOWN:
                player.onKeyDown(event)
            if event.type == KEYUP:
                player.onKeyUp(event)

        # Update player position
        player.update()

        # Render enemies and update position
        for enemy in enemyList:
##            enemy.move()
            DISPLAYSURF.blit(enemy.image, (enemy.x, enemy.y))

        # Render walls
        for wall in wallList:
            DISPLAYSURF.blit(wall.image, (wall.x, wall.y))

        # Render player
        DISPLAYSURF.blit(player.image, (player.x, player.y))
        allsprites.update()
        pygame.display.update()
        FPSCLOCK.tick(FPS)

if __name__ == '__main__':
    main()
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

You updated velocity and then checked for collision. I see how you clipped the character to whichever wall he was at afterwards with the series of conditional statements. But this isn't how collision is typically done and could spell problems later.

You should think about changing the velocity due to the collision rather than seeing there is a collision and then just positioning your character at that wall.

Make another rectangle (We will call it the speculative rectangle) with the addition of vx and vy as if you were updating the characters position. (But don't actually update the characters position just yet.) Then check for collision between this speculative rectangle and the walls. If there is a collision you change your velocities to the distance between the characters actual rectangle (not the speculative) and the wall. Then, during the next go around, if you're still trying to walk into the wall the distance between the character and the wall would be 0 so the velocity would be 0. Making your character unmoving.

This is called speculative collision. Here are some more articles on it.

http://www.wildbunny.co.uk/blog/2011/03/25/speculative-contacts-an-continuous-collision-engine-approach-part-1/

http://jitter-physics.com/wordpress/?tag=speculative-contacts

\$\endgroup\$

You must log in to answer this question.

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