Search code examples
pythonpygamecollision-detectioncollision

Collision with wall doesn't stop object's velocity


I'm working on a game, and in that game whenever the player's x is >= the screen's width, I want the player's x velocity to = 0. But when I tried, it didn't work. The collision code looks fine to me, I think there is some issue with when I call player.collision().

PS. The object's velocity is equal to 0 when the objects x is <= 0, but not when the x is >= displayW the velocity doesn't equal to 0.

The problem is under #collision to walls

PYTHON

# IMPORTS
import pygame, random;

# GLOBALS
global screen, displayW, displayH;
global clock, FPS;
global end, food, player;

# SETGLOBALVALUES
def setGlobalValues():
    global screen, displayW, displayH;
    global clock, FPS;
    global end, food, player;

    displayW = 800;
    displayH = 600;
    screen = pygame.display.set_mode((displayW, displayH));

    clock = pygame.time.Clock();
    FPS = 60;

    end = False;
    food = Food();
    player = Player();

# MAIN
def main():
    pygame.init();

    setGlobalValues();
    setup();
    gameLoop();
    quitGame();

# GAMELOOP
def gameLoop():
    global end, player;

    while(not end):
        for event in pygame.event.get():
            # ONCLICK QUIT
            if(event.type == pygame.QUIT):
                end = True;

            # KEYDOWN
            if(event.type == pygame.KEYDOWN):
                if(event.key == pygame.K_LEFT):
                    player.velX -= 1;
                if(event.key == pygame.K_RIGHT):
                    player.velX += 1;

            # KEYUP
            if(event.type == pygame.KEYUP):
                if(event.key == pygame.K_LEFT):
                    player.velX = 0;
                if(event.key == pygame.K_RIGHT):
                    player.velX = 0;

        draw();
        animate();
        collision();


# DRAW
def draw():
    global screen, food, player;

    # fill background
    screen.fill((255, 255, 255));

    player.draw();

    # update
    pygame.display.update();

# ANIMATE
def animate():
    global food, player;

    food.animate();
    player.animate();

# COLLISION
def collision():
    player.collision();

# CLASSES
class Food():
    def __init__(self, x=0, y=0, w=0, h=0, velY=0, color=()):
        global displayW;

        self.x = random.randrange(0, displayW);
        self.y = -100;
        self.w = 20;
        self.h = 20;
        self.velY = 0.7;
        self.color = (255, 0, 0);

    def draw(self):
        global screen;

        pygame.draw.rect(screen, self.color, (self.x, self.y, self.w, self.h));

    def animate(self):
        self.y += self.velY;

    def collision(self):
        global displayW, displayH;

        pass;

class Player():
    def __init__(self, x=0, y=0, velX=0, velY=0, w=0, h=0, color=()):
        global displayW, displayH;

        self.w = 20;
        self.h = 20;
        self.x = displayW / 2 - self.w / 2;
        self.y = displayH - 100;
        self.velX = 0;
        self.velY = 0;
        self.color = (0, 0, 0);

    def draw(self):
        global screen;

        pygame.draw.ellipse(screen, self.color, (self.x, self.y, self.w, self.h));

    def animate(self):
        self.x += self.velX;
        self.y += self.velY;

    def collision(self):
        global displayW;

        # collision to walls
        if(self.x <= 0):
            self.velX = 0;
        elif(self.x >= displayW):
            self.velX = 0;


# SETUP
def setup():
    pygame.display.set_caption("Food Catcher");

# QUIT GAME
def quitGame():
    pygame.quit();
    quit();

# CALL MAIN
if(__name__ == "__main__"):
    main();

Solution

  • The logic is correct - but it allows the player to slowly "sleep by" on both sides. The reason you perceive it as working on the left side, and the player vanishing on the rigth side is that you do compare the width of the screen with the left corner coordinate of your player. When it is zero, the player is on screen . But when player.x == screen width, the player is already drawn off the screen.

    Change your condition to:

    elif(self.x  + self.h) >= displayW:
    

    The logic is right - but you have no delays in there, meaning you gte updates as fast as your computer can repaint the screen - and, the computation of the new poision (player.animate) takes place after you had updated the speed (velX) due to keypresses, and before you check if there was a colision, and reduce the speed to 0.

    So, there is effectively no change to the speed of the player, even though the collision detection reduces it to zero - on the next frame it will be increased again by the continuous keypress. I just am not sure why oes not it goes away on the left-hand side as well.

    Anyway, just change the call to "collision" to take place before the call to animate.

    More pygame tips: include a delay of some tens of microseconds in your while loop. This way, the game won't eat up 100% pf your CPU. Just a call to pygame.time.delay(20) should do it. Also, you will notice that moving by 1 or 2px at a time may be slow - so, adjust your speed in multiples of 5 or 10px.

    And, on the "collision" method. change not just the speed, but also the position, so that the player is on screen.

    More Python tips: unlike a gazzilion of other languages, Python is not a direct modificationd of C sytax: no need to ; at each line, and no need for ( ) around if expressions (they are delimited by the : anyway). Also, if you are only using a global variable for reading, or calling a method in the object stored in the global variable, there is no need to declare it - in fact you need none of those global declarations but on the setGlobalValues function.