Search code examples
pythonpygamenew-operator

How do I make it so my square can land on my other square and stay on it


I am a new python and pygame, and I want to learn collisionm but I only know how to detect collision.

How can I add collision that allows me to stand on my big square while my little square moves. I don't know how to make it stand on the other square instead of getting thrown. At the bottom of my script I added collision detection but can't figure out how to make it actually stand on the my big block without it getting thrown to the sides, top and bottom.

pygame.init()


win = pygame.display.set_mode((500,500))
pygame.display.set_caption("THIS GAME IS KINDA ASS")


# first squrae
x = 50
y = 50
height= 50
width = 50
speed = 5
isJump = False
JumpCount = 10

# square 2
cordx = 100
cordy = 200
cordheight = 100
cordwidth = 100
    # First Square





# main loop
runningame = True
while runningame:
    pygame.time.delay(50)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            runningame = False
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        x -= speed
    if keys[pygame.K_RIGHT]:
        x += speed
    if not(isJump):
        if keys[pygame.K_UP]:
            y -= speed
        if keys[pygame.K_DOWN]:
            y += speed
        if keys[pygame.K_SPACE]:
            isJump = True
    else:
        if JumpCount >= -10:
            y -= (JumpCount *abs(JumpCount))
            JumpCount -= 1
        else:
            isJump = False
            JumpCount = 10



    win.fill((0,0,0))
# I added a varabial to my player square  
    Player = pygame.draw.rect(win, (115, 235, 174), (x,y, height,width))

    # second square
   # this is the big box and its the enemy square that I want my script to stand on
    Enemy = pygame.draw.rect(win, (202, 185 , 241), (cordx, cordy, cordheight,cordwidth))


    if Player.colliderect(Enemy):
        pygame.draw.rect(win, (140, 101, 211), (50,50,50,50))


    pygame.display.update()

pygame.quit()


Solution

  • So you have Player.colliderect(Enemy) so you know when its colliding, you just need to figure out from where and move the player accordingly.

    A simple way to do this which is not perfect is to have a direction the player is moving and if the player is moving down, move the player above the enemy.

    In your code, you need a direction

    direction = "not moving"
    ...
    if keys[pygame.K_LEFT]:
        x -= speed
        direction = "left"
    if keys[pygame.K_RIGHT]:
        x += speed
        direction = "right"
    ...
    else:
        if JumpCount >= -10:
            y -= (JumpCount *abs(JumpCount))
            JumpCount -= 1
            if (JumpCount *abs(JumpCount)) > 0:
                direction = "down"
            else:
                direction = "up"
    ...
    if Player.colliderect(Enemy):
        if direction == "down":
            y -= player.bottom - enemy.top #get the amount that that the player is overlapping the enemy and take it off y so it no longer overlapping
        elif direction == "up":
            y += player.top - enemy.bottom
        #do the same for left and right
    

    another way is to make player and enemy Rects and use them to replace x and y

    so

    # first squrae
    x = 50
    y = 50
    height= 50
    width = 50
    player = pygame.Rect(x,y,width,height)
    speed = 5
    isJump = False
    JumpCount = 10
    
    # square 2
    cordx = 100
    cordy = 200
    cordheight = 100
    cordwidth = 100
    enemy = pygame.Rect(cordc,cordy,cordwidth,cordheight)
    

    you can then draw them with

    win.fill((0,0,0))
    # I added a varabial to my player square  
    pygame.draw.rect(win, (115, 235, 174),player)
    
    # second square
    # this is the big box and its the enemy square that I want my script to stand on
    pygame.draw.rect(win, (202, 185 , 241), enemy)
    

    and update the positions with

    if keys[pygame.K_LEFT]:
        player.x -= speed
    if keys[pygame.K_RIGHT]:
        player.x += speed
    

    and for collisions

    if player.colliderect(enemy):
        if player.centerx < enemy.left: #if the center of player is left of the enemy
            player.right = enemy.left  
    
        if player.centerx > enemy.right:
            player.left = enemy.right
    
        if player.centery < enemy.top:
            player.bottom = enemy.top
    
        if player.centery > enemy.bottom:
            player.top = enemy.bottom
    

    both ways work ad neither are perfect, there are also more ways to do it, but i think i would recommend the first method as its easier to understand and easier to add to your code. Comment if you need more explanation on how it works or more ways to do it


    import pygame
    pygame.init()
    
    
    win = pygame.display.set_mode((500,500))
    pygame.display.set_caption("THIS GAME IS KINDA ASS")
    
    class Player(pygame.sprite.Sprite):
        def __init__(self, x, y, w, h, col):
            pygame.sprite.Sprite.__init__(self)
            self.rect = pygame.Rect(x,y,w,h)
            self.image = pygame.Surface((w,h))
            self.image.fill(col)  
            self.isJump = False
            self.JumpCount = 8
    
        def update(self):
            if self.isJump:
                if self.JumpCount >= -8:
                    self.rect.y -= (self.JumpCount *abs(self.JumpCount))
                    self.JumpCount -= 1
                else:
                    self.isJump = False
                    self.JumpCount = 8          
    
    
    class Platform(pygame.sprite.Sprite):
        def __init__(self, x, y, w, h, col):
            pygame.sprite.Sprite.__init__(self)
            self.rect = pygame.Rect(x,y,w,h)
            self.image = pygame.Surface((w,h))
            self.image.fill(col)
    
    
    # first squrae
    x = 50
    y = 50
    height= 50
    width = 50
    speed = 5
    
    player = Player(x,y,width, height, (115, 235, 174))
    
    # square 2
    cordx = 100
    cordy = 200
    cordheight = 100
    cordwidth = 100
    enemy1 = Platform(cordx,cordy,cordwidth, cordheight, (202, 185 , 241))
    
    #square 3
    enemy2 = Platform(300,90,100,30,(202, 185 , 241))
    
    
    all_sprites = pygame.sprite.Group()
    all_sprites.add(player)
    all_sprites.add(enemy1)
    all_sprites.add(enemy2)
    
    enemy_sprites = pygame.sprite.Group()
    enemy_sprites.add(enemy1, enemy2)
    
    clock = pygame.time.Clock()
    # main loop
    runningame = True
    while runningame:
        clock.tick(50)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                runningame = False
    
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT]:
            player.rect.x -= speed
        if keys[pygame.K_RIGHT]:
            player.rect.x += speed
        if not(player.isJump):
            if keys[pygame.K_UP]:
                player.rect.y -= speed
            if keys[pygame.K_DOWN]:
                player.rect.y += speed
            if keys[pygame.K_SPACE]:
                player.isJump = True
    
        for enemy in enemy_sprites.sprites():
            if player.rect.colliderect(enemy.rect):
                if player.rect.centerx < enemy.rect.left: #if the center of player is left of the enemy
                    player.rect.right = enemy.rect.left  
    
                if player.rect.centerx > enemy.rect.right:
                    player.rect.left = enemy.rect.right
    
                if player.rect.centery < enemy.rect.top:
                    player.rect.bottom = enemy.rect.top
    
                if player.rect.centery > enemy.rect.bottom:
                    player.rect.top = enemy.rect.bottom
    
        all_sprites.update()
    
        win.fill((0,0,0))
    
        all_sprites.draw(win)
    
        pygame.display.update()
    
    pygame.quit()
    

    Here is your program but using sprites

    As you can see, i have moved any code relating to the squares to their classes. each sprite has a rect and a image. The rect is used the same way as the rect i showed above. The image is a surface, because that how pygame puts it on the screen. it the exact same as before by making it the same size and filling it with the colour.

    Ive got 2 sprite groups, the all_sprites group which has all of them and the enemy_sprites group, which only has enemies(platforms that you can stand on). I can draw all sprites by calling all_sprites.draw(win).

    For collisions, exact same, except i want to loop through each enemy (as i now have 2). so i can call enemy_sprites.sprites() to get each sprite.

    If you want more explanation or examples, start a chat.