0

I'm trying to create a game where you are supposed to land on a platforms in the air, and if you fall under the camera's point of view, you die. You can jump in the air but that consumes fat you build up by eating. I'm not done with the game so you won't see that part, but the problem is with the collisions. I've made many objects and one I'm testing is a wooden box. It works, but the collision works like a triangle instead of a square. Half of the collision doesn't work in the top right, but the bottom left works. I have no clue why that can be the case, so help would come a long way. I will link the file and code.

import pygame, math
from pygame.locals import *

pygame.init()
Screen = pygame.display.set_mode((450, 800))
Running = True

ScreenWidth = Screen.get_width()
ScreenHeight = Screen.get_height()

CharacterSize = 25
Character = pygame.image.load('assets/game/Character.png').convert_alpha()
CharacterJumpPower = 15
JumpCooldown = 0

PlayerX = 0
PlayerY = 0
CameraX = PlayerX
CameraY = PlayerY
CameraSmoothness = 28

Gravity = -0.025
XVelocity = 0
YVelocity = 0

MouseX, MouseY = pygame.mouse.get_pos()

Keyboard = pygame.key.get_pressed()

Clock = pygame.time.Clock()

WoodenBox = pygame.transform.smoothscale(pygame.image.load('assets/game/WoodenBox.png').convert_alpha(), (25, 25))

Obstacles = [[WoodenBox, (400, -400)]]


def UpdateVariables():
    global Screen, ScreenWidth, ScreenHeight, CharacterSize, Character, MouseX, MouseY, Keyboard, Clock
    ScreenWidth = Screen.get_width()
    ScreenHeight = Screen.get_height()
    Character = pygame.transform.smoothscale(pygame.image.load('assets/game/Character.png').convert_alpha(), (CharacterSize, CharacterSize))
    MouseX, MouseY = pygame.mouse.get_pos()
    Keyboard = pygame.key.get_pressed()
    Clock = pygame.time.Clock()

def CharacterPhysics():
    global Gravity, CharacterSize, XVelocity, YVelocity, MouseX, MouseY, PlayerX, PlayerY, Keyboard, CharacterJumpPower, JumpCooldown, ScreenWidth, ScreenHeight
    XVelocity = (MouseX - PlayerX) / 5
    YVelocity += CharacterSize * Gravity
    if Keyboard[K_SPACE] and CharacterSize > 5 and JumpCooldown == 0:
        YVelocity = CharacterJumpPower
        CharacterSize -= 1
        JumpCooldown = 1
    elif not Keyboard[K_SPACE]:
        JumpCooldown = 0
    if (-PlayerY + CameraY + ScreenHeight / 2) > ScreenHeight:
        Dead()
    if CheckCollisions():
        print("True")
    PlayerX += XVelocity
#    if CheckCollisions():
#        if XVelocity > 0:
#            PlayerX -= 1
#        else:
#            PlayerX += 1
#        PlayerX -= XVelocity
#        XVelocity = 0
    PlayerY += YVelocity
    if CheckCollisions():
        if YVelocity > 0:
            PlayerY -= 1
        else:
            PlayerY += 1
        PlayerY -= YVelocity
        YVelocity = 0

def CameraMovement():
    global PlayerX, PlayerY, CameraX, CameraY, CameraSmoothness
    CameraY += (PlayerY - CameraY) / CameraSmoothness

def Dead():
    global Running
    print("You Died!")
    Running = False

def BlitObstacles():
    global Screen, Obstacles, CameraX, CameraY, ScreenWidth, ScreenHeight
    for Obstacle in Obstacles:
        Screen.blit(Obstacle[0], Obstacle[0].get_rect(center = (Obstacle[1][0] - CameraX, -Obstacle[1][1] + CameraY + ScreenHeight / 2)))

def CheckCollisions():
    global Obstacles, PlayerX, PlayerY, Character, CharacterSize
    for Obstacle in Obstacles:
        if abs((PlayerX + PlayerY) - (Obstacle[1][0] + Obstacle[1][1])) < Obstacle[0].get_size()[0] / 2 + Obstacle[0].get_size()[1] / 2:
            PlayerMask = pygame.mask.from_surface(Character)
            ObstacleMask = pygame.mask.from_surface(Obstacle[0])
            Offset = (PlayerX - Obstacle[1][0], PlayerY - Obstacle[1][1])
            if ObstacleMask.overlap(PlayerMask, Offset):
                return True
    return False

while Running:
    Screen.fill((255, 255, 255, 255))
    UpdateVariables()
    CharacterPhysics()
    CameraMovement()
    Screen.blit(Character, Character.get_rect(center = (PlayerX - CameraX, -PlayerY + CameraY + ScreenHeight / 2))) # Player
    BlitObstacles()
    pygame.display.flip()
    Clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            Running = False

pygame.quit()

Here is the folder: https://www.dropbox.com/sh/26mmmtxmcwvazdd/AACAlbEpA3bUH5YwJILZ5t8fa?dl=0

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Emanuel
  • 1
  • 1
  • Please read [What topics can I ask about here?](https://stackoverflow.com/help/on-topic): *"Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the **shortest code necessary to reproduce it in the question itself**."*. The code in the question is not the *"shortest code necessary to reproduce"* and the problem cannot be reproduced with out downloading external resources. – Rabbid76 Sep 04 '22 at 15:50
  • 1
    Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. – Community Sep 05 '22 at 06:07
  • The problem has nothing to do with the mask. It will behave the same if you use `pygame.Rect.colliderect` instead of `pygame.maks.Maks.overlaop`. – Rabbid76 Sep 05 '22 at 14:30
  • `if abs((PlayerX + PlayerY) - (Obstacle[1][0] + Obstacle[1][1])) < Obstacle[0].get_size()[0] / 2 + Obstacle[0].get_size()[1] / 2` - I don't exactly understand what you're trying to do here, but the `PlayerX + PlayerY` might explain the triangular behavior. – The_spider Sep 05 '22 at 18:11
  • Hey, thanks for noting that out The_spider, I have not tested it yet, but I'm pretty certain that's the problem so thank you. Sorry Rabbid76, but this is my first post and I am not sure how to make the best post, but thanks for noting some key things out. – Emanuel Sep 06 '22 at 11:37

1 Answers1

0

The abs( clause in CheckCollisions() looks incorrect. I can't work out why a collision is summing the x and y coordinates of the object. There's no information suggesting specialised game physics, so I assume we're just using plain two-dimensional cartesian coordinate collision.

I think a re-working of your objects would really help. The best way forward would be to convert them to PyGame sprites, allowing use of the built-in collision functionality. But in the short-term, just using a PyGame Rect to hold the size and position would help:

box1_image = pygame.transform.smoothscale(pygame.image.load('assets/game/WoodenBox.png').convert_alpha(), (25, 25))
box1_rect = box1_image.get_rect()
box1_rect.center = ( 400, -400 )   # -400 is off-screen??

Obstacles = [[box1_image, box1_rect]]


def CheckCollisions():
    global Obstacles, PlayerX, PlayerY, Character, CharacterSize
    player_rect = pygame.Rect( PlayerX, PlayerY, CharacterSize, CharacterSize )
    PlayerMask = pygame.mask.from_surface(Character)

    for Obstacle in Obstacles:
        obs_image, obs_rect = Obstacle
        if ( player_rect.colliderect( obs_rect ) ):
            print( "Rect-Rect Collide" )
            ObstacleMask = pygame.mask.from_surface( obs_image )
            Offset = ( player_rect.x - obs_rect.x, player_rect.y - obs_rect.y )
            if ObstacleMask.overlap(PlayerMask, Offset):
                print( "Mask-Mask Collide" )
                return True
    return False

Keeping the size+position in a Rect allows the code to use a quick collision-check via colliderect( some_other_rect ). It's worth spending the time to understand the Rect class, it's very very useful.

Kingsley
  • 14,398
  • 5
  • 31
  • 53