Masks Works Like a triangle - python

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

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.

Related

How to make player stay on moving platform in pygame?

I want to create a program where the player has to jump on horizontally moving platforms to reach their high score. The player can move left and right and can jump.
import pygame
from pygame.locals import *
pygame.init()
run = True
width = 500
height = 500
x = 250
y = 475
vel = 10
x_plat = 100
y_plat = 400
platform_vel = 5
clock = pygame.time.Clock()
isjump = False
jumpcount = 7.5
collision_tolerance = 10
gravity = 8
surface = pygame.display.set_mode((width, height))
rect = Rect(x_plat, y_plat, 150, 20)
player = Rect(x, y, 25, 25)
while run:
clock.tick(30)
if rect.left >= 355 or rect.left < 1:
platform_vel *= -1
rect.left += platform_vel
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and x > 0:
x -= vel
if keys[pygame.K_RIGHT] and x < (500 - 25):
x += vel
if not(isjump):
if keys[pygame.K_SPACE]:
isjump = True
else:
if jumpcount >= -7.5:
y -= (jumpcount * abs(jumpcount)) * 1
jumpcount -= 1
else:
jumpcount = 7.5
isjump = False
collide = pygame.Rect.colliderect(rect, player)
if collide:
player.bottom = rect.top
player.left += platform_vel
rect.left += platform_vel
pygame.draw.rect(surface, (0, 0, 0), rect)
pygame.draw.rect(surface, (255, 255, 255), player)
pygame.display.update()
surface.fill((255, 222, 173))
for event in pygame.event.get():
if event.type == QUIT:
run = False
If you run my code, when the player jumps onto the moving platform, the platform moves but the player doesn't move with it, making the player float in mid air.
I would really appreciate it if someone could help!
The player didn't move at all with the code provided so I updated x and y references to player.x and player.y, then made some changes to implement gravity and collision checking with the platform and floor.
It's important to note that when on the platform, gravity still moves the player downward into the platform so they collide with the platform each clock tick and the "collide" logic runs. It also allows the player to fall off the platform if moving left or right.
This seems to do what you want:
import pygame
from pygame.locals import *
pygame.init()
run = True
width = 500
height = 500
vel = 10
x_plat = 100
y_plat = 400
platform_vel = 5
clock = pygame.time.Clock()
isjump = False
jumpcount = 7.5
collision_tolerance = 10
gravity = 1
player_vy = 0 # players vertical velocity
surface = pygame.display.set_mode((width, height))
rect = Rect(x_plat, y_plat, 150, 20)
player = Rect(250, 475, 25, 25)
while run:
clock.tick(30)
if rect.left >= 355 or rect.left < 1:
platform_vel *= -1
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and player.x > 0:
player.x -= vel
if keys[pygame.K_RIGHT] and player.x < (500 - 25):
player.x += vel
if not(isjump):
if keys[pygame.K_SPACE]:
isjump = True # suppress spacebar jumping until landing
player_vy = -16 # give upward velocity
player_vy += gravity # accelerate downward due to gravity
player.y += player_vy # compute new height
collide = pygame.Rect.colliderect(rect, player)
if collide and player_vy >= 0: # falling and collide with platform?
player.bottom = rect.top # land on platform.
player_vy = 0 # no longer falling
player.left += platform_vel # move with platform
isjump = False # enable jump
if player.y > 475: # don't fall off bottom of screen.
player.y = 475 # land on bottom of screen
player_vy = 0 # stop falling
isjump = False # allow jumping again
rect.left += platform_vel # move platform
pygame.draw.rect(surface, (0, 0, 0), rect)
pygame.draw.rect(surface, (255, 255, 255), player)
pygame.display.update()
surface.fill((255, 222, 173))
for event in pygame.event.get():
if event.type == QUIT:
run = False

I am wondering why it says the distance has to be less than 27? Why less than 27? Where is 27 coming from?

My teacher gave us this code to analyze and I am not sure why he put 27 there for collision.. I asked him he said if the distance between the player and the enemy is less than 27 then I will call it a collision, but I still don't understand, can someone please kindly explain it to me in simpler terms. I don't understand where the number 27 comes from.. when my dimensions are so big?
import math
import random
import pygame
# Intialize the pygame
pygame.init()
# create the screen
screen = pygame.display.set_mode((1000, 700))
# Background
background = pygame.image.load('undersea.png')
# Player
playerImg = pygame.image.load('space-invaders.png')
playerX = 500
playerY = 600
playerX_change = 0
# Enemy
enemyImg = []
enemyX = []
enemyY = []
enemyX_change = []
enemyY_change = []
num_of_enemies = 5
for i in range(num_of_enemies):
enemyImg.append(pygame.image.load('plastic.png'))
enemyX.append(random.randint(0, 636))
enemyY.append(random.randint(50, 150))
enemyX_change.append(4)
enemyY_change.append(40)
# Bullet
# Ready - You can't see the bullet on the screen
# Fire - The bullet is currently moving
bulletImg = pygame.image.load('bullet (1).png')
bulletX = 0
bulletY = 480
bulletX_change = 0
bulletY_change = 10
bullet_state = "ready"
# Score
score_value = 0
font = pygame.font.Font('freesansbold.ttf', 32)
textX = 10
testY = 10
# Game Over
over_font = pygame.font.Font('freesansbold.ttf', 64)
def show_score(x, y):
score = font.render("Score : " + str(score_value), True, (255, 255, 255))
screen.blit(score, (x, y))
def game_over_text():
over_text = over_font.render("GAME OVER", True, (255, 255, 255))
screen.blit(over_text, (200, 250))
def player(x, y):
screen.blit(playerImg, (x, y))
def enemy(x, y, i):
screen.blit(enemyImg[i], (x, y))
def fire_bullet(x, y):
global bullet_state
bullet_state = "fire"
screen.blit(bulletImg, (x + 16, y + 10))
def isCollision(enemyX, enemyY, bulletX, bulletY):
distance = ((enemyX - bulletX) ** 2 + (enemyY - bulletY) ** 2)
distance = math.sqrt(distance)
print(distance)
if distance < 27:
return True
else:
return False
# Game Loop
running = True
while running:
# RGB = Red, Green, Blue
screen.fill((0, 0, 0))
# Background Image
screen.blit(background, (0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# if keystroke is pressed check whether its right or left
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
playerX_change = -5
if event.key == pygame.K_RIGHT:
playerX_change = 5
if event.key == pygame.K_SPACE:
if bullet_state is "ready":
# Get the current x cordinate of the spaceship
bulletX = playerX
fire_bullet(bulletX, bulletY)
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
playerX_change = 0
# 5 = 5 + -0.1 -> 5 = 5 - 0.1
# 5 = 5 + 0.1
playerX += playerX_change
if playerX <= 0:
playerX = 0
elif playerX >= 636:
playerX = 636
# Enemy Movement
for i in range(num_of_enemies):
# Game Over
if enemyY[i] > 440:
for j in range(num_of_enemies):
enemyY[j] = 2000
game_over_text()
break
enemyX[i] += enemyX_change[i]
if enemyX[i] <= 0:
enemyX_change[i] = 4
enemyY[i] += enemyY_change[i]
elif enemyX[i] >= 736:
enemyX_change[i] = -4
enemyY[i] += enemyY_change[i]
# Collision
collision = isCollision(enemyX[i], enemyY[i], bulletX, bulletY)
if collision:
bulletY = 480
bullet_state = "ready"
score_value += 1
enemyX[i] = random.randint(0, 736)
enemyY[i] = random.randint(50, 150)
enemy(enemyX[i], enemyY[i], i)
# Bullet Movement
if bulletY <= 0:
bulletY = 480
bullet_state = "ready"
if bullet_state is "fire":
fire_bullet(bulletX, bulletY)
bulletY -= bulletY_change
player(playerX, playerY)
show_score(textX, testY)
pygame.display.update()
The value is 27, because you compute the Euclidean distance between (enemyX, enemyY) and (bulletX, bulletY) in the function isCollision:
def isCollision(enemyX, enemyY, bulletX, bulletY):
distance = ((enemyX - bulletX) ** 2 + (enemyY - bulletY) ** 2)
distance = math.sqrt(distance)
print(distance)
The Euclidean distance between 2 points (Ax, Ay) and (Bx, By) is
d = sqrt((Bx-Ax)**2 + (By-Ay)**2) = hypot(Bx-Ax, By-Ay)
In 2 dimensional space this is the same as the Pythagorean theorem. The length of the diagonal in a square with a side length of 19 is approximately 27.
Try changing the '27' to a small no. like '5' and you will see that the collision is happening too late on screen. OR if you replace the enemy/bullet image you are loading with some other small/large image you will notice that you will have to change you '27' to some other number to make things work as intended.
Simple Words
The reason for the hard-coding '27' could be because the width of enemy image and the width of bullet image you load in your code seem to touch (collide) eachother at the distance '27' from the point where they are drawn.
Explanation
When we load an image pygame draws it from top-left corner. For Example your background image is so large in code but when you ask your code where is this 'background' located it well tell you it is on (0,0) although it's on your full screen not a single point.
Infered from screen.blit(background, (0, 0)) line in your code
Same for the images/sprites of enemy and bullets. The position you use in collision enemyX, enemyY, bulletX, bulletY are the top-left coordinates. Now to show them actually touching/colliding you may have to tell the code that 'My enemy is more than one pixel on the position. So I want to take care of it by using the constant '27' (depends on width of enemy/bullet). As soon as the both positions have a Euclidean distance of '27' this means they are colliding on my screen hence proceed as true.
If you are still confused about the positioning issue and how center of image or top-left of image matters read these, it will help.
https://stackoverflow.com/a/51182238/11672352
Why is the pygame rect not in the right place?
It seems to me like the game triggers the collision once the bullet enters a circle of radius 27 pixels around the enemy. Although your enemy may not be a perfect circle, it is a lot simpler to treat them as if they were when calculating collisions as it is relatively easy to check whether a point has collided with a circle, in this case, the bullet with the enemy.
the lines:
distance = ((enemyX - bulletX) ** 2 + (enemyY - bulletY) ** 2)
distance = math.sqrt(distance)
are used to calculate the distance between the middle of the enemy and the bullet by using Pythagoras' theorem (a^2 + b^2 = c^2 where a and b are two different sides of a right angled triangle and c is your hypotenuse, aka the longest side)
The value 27 is likely used because the enemy is likely around 52 pixels (27 * 2) wide and tall

How do I make the character jump in Pygame?

I am trying to make a platformer game and I want the circle to jump, but whenever I try to make a jump, I get the following error, I found various methods of doing the jump, but none of them worked.
I think that the error is whenever I jump, a float number is happening and the draw method can't contain floats, only integers
here is the code for the game:
import pygame
import os
import time
import random
pygame.font.init()
Width, Height = 1280, 720
win = pygame.display.set_mode((Width, Height))
pygame.display.set_caption("Parkour Ball")
BG = pygame.image.load(os.path.join("assets", "Background.jpg"))
class Ball:
def __init__(self, x, y):
self.x = x
self.y = y
def draw(self, window):
pygame.draw.circle(window, (255,0,0), (self.x, self.y), 25)
def main():
run = True
FPS = 60
clock = pygame.time.Clock()
level = 0
lives = 10
main_font = pygame.font.SysFont("comicsans", 50)
lost_font = pygame.font.SysFont("comicsans", 60)
spikes = []
holes = []
hammers = []
platforms = []
enemies = []
player_vel = 5
jump = False
jumpCount = 10
player = Ball(300, 450)
lost = False
lost_popup = 0
def redraw():
win.blit(BG, (0,0))
player.draw(win)
pygame.display.update()
while run:
clock.tick(FPS)
redraw()
if lives == 0:
lost = True
lost_popup += 1
if lost:
if lost_popup > FPS * 3:
run = False
else:
continue
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and player.x - player_vel - 25 > 0:
player.x -= player_vel
if keys[pygame.K_RIGHT] and player.x + player_vel + 25 < Width:
player.x += player_vel
if keys[pygame.K_w]and player.y - player_vel > 0: #up
player.y -= player_vel
if keys[pygame.K_s] and player.y + player_vel < Height: #down
player.y += player_vel
if not(jump):
if keys[pygame.K_SPACE]:
jump = True
else:
if jumpCount >= -10:
neg = 1
if jumpCount < 0:
neg = -1
player.y -= (jumpCount ** 2) / 2 * neg
jumpCount -= 1
main()
The coordinates to pygame.draw.circle() have to be integral values. round() the floating point coordinates to integral coordinates:
pygame.draw.circle(window, (255,0,0), (self.x, self.y), 25)
pygame.draw.circle(window, (255,0,0), (round(self.x), round(self.y)), 25)

How do I fix my Collision so I could stand on a box without Falling from it

What I am trying to do here is make my collision detect allow me to jump on a square but it doesn't seem to work. Its a the very bottom of the main loop.
# --- COLLISION is at the bottom of main loop
# ------
# this is a pygame module that I imported
import pygame
pygame.init()
# this is just my screen I created win defines it
win = pygame.display.set_mode((500,500))
# this is my caption for my game
pygame.display.set_caption("Just Tryna learn Something")
# these are my coordinates for my enemy where it will spawn
cordx = 300
cordy = 300
heights = 70
widths = 70
# my Player Coordinate and its speed and and its Jump
x = 200
y = 200
height = 40
width = 40
speed = 5
isJump = False
jumpCount = 10
# main loop
# main loop for my game
running = True
while running:
pygame.time.delay(100)
win.fill((0,0,0))
#-----------------------------------------------------------------------------------------
# this here draws my player in my window
Player = pygame.draw.rect(win, (140, 0,150), (x, y, height, width))
#-----------------------------------------------------------------------------------------
# this here draws my enemy
Enemy = pygame.draw.rect(win, (90,90,90), (cordx, cordy, heights, widths))
#=-------------------------------------------------------------------------------------
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
#-------------------------------------------------------------------
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
x -= speed
if keys[pygame.K_RIGHT]:
x += speed
# this here is my functions for movement and Jumping
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)) * 0.5
jumpCount -= 1
else:
jumpCount = 10
isJump = False
# COLLISION here is my collision detect and collision
its suppose to make me stand on the square with my little box when I jump on it
but it doesnt seem to work (Enemy) is the big box and (Player) is the little box
if Player.colliderect(Enemy):
pygame.draw.rect(win, (150,0,140), (50, 50, 20, 70))
if Player.top >= 375 and Player.top <= 370:
x = 375
# ---------------------------------------------------------
pygame.display.update()
pygame.quit()
Continuously let the player fall down. Add a variable fall = 0 and the variable to y and increment fall in every frame, if the player is not jumping. A jump ends, if the player reaches the maximum jump height (jumpCount == 0):
if not isJump:
y += fall
fall += 1
# [...]
else:
if jumpCount > 0:
y -= (jumpCount * abs(jumpCount)) * 0.5
jumpCount -= 1
else:
jumpCount = 10
isJump = False
Limit the player to the bottom of the window (500), and the top of the block by setting the y coordinate of the player:
Player.topleft = (x, y)
collide = False
if Player.colliderect(Enemy):
y = Enemy.top - Player.height
collide = True
if Player.bottom >= 500:
y = 500 - Player.height
collide = True
It is only allowed to jump, if the player stands on the ground or on the block:
if collide:
if keys[pygame.K_SPACE]:
isJump = True
fall = 0
Furthermore use pygame.time.Clock() and tick(), instead of pygame.time.delay() for a smooth movement. Control the speed by the flops per second (FPS):
FPS = 60
clock = pygame.time.Clock()
running = True
while running:
clock.tick(FPS)
#pygame.time.delay(100)
See the example:
# --- COLLISION is at the bottom of main loop
# ------
# this is a pygame module that I imported
import pygame
pygame.init()
# this is just my screen I created win defines it
win = pygame.display.set_mode((500,500))
# this is my caption for my game
pygame.display.set_caption("Just Tryna learn Something")
# these are my coordinates for my enemy where it will spawn
cordx = 300
cordy = 350
heights = 70
widths = 70
# my Player Coordinate and its speed and and its Jump
x = 200
y = 200
height = 40
width = 40
speed = 5
isJump = False
jumpCount = 10
fall = 0
FPS = 60
clock = pygame.time.Clock()
# main loop
# main loop for my game
running = True
while running:
clock.tick(FPS)
#pygame.time.delay(100)
win.fill((0,0,0))
#-----------------------------------------------------------------------------------------
# this here draws my player in my window
Player = pygame.draw.rect(win, (140, 0,150), (x, y, height, width))
#-----------------------------------------------------------------------------------------
# this here draws my enemy
Enemy = pygame.draw.rect(win, (90,90,90), (cordx, cordy, heights, widths))
#=-------------------------------------------------------------------------------------
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
#-------------------------------------------------------------------
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
x -= speed
if keys[pygame.K_RIGHT]:
x += speed
# this here is my functions for movement and Jumping
if not isJump:
y += fall
fall += 1
Player.topleft = (x, y)
collide = False
if Player.colliderect(Enemy):
collide = True
y = Enemy.top - Player.height
if Player.right > Enemy.left and Player.left < Enemy.left:
x = Enemy.left - Player.width
if Player.left < Enemy.right and Player.right > Enemy.right:
x = Enemy.right
if Player.bottom >= 500:
collide = True
y = 500 - Player.height
if collide:
if keys[pygame.K_SPACE]:
isJump = True
fall = 0
else:
if jumpCount > 0:
y -= (jumpCount * abs(jumpCount)) * 0.5
jumpCount -= 1
else:
jumpCount = 10
isJump = False
pygame.display.update()
pygame.quit()

Python (pygame) collision detection - sprite and group

I am new to python and am trying to write a game that launches a character and when he interacts with a sprite on the ground, something will change, for example speed. My apologies for the disorganization in my code. I have taken samples from a few tutorials and I can't make them work together.
How do I make the player's collision with the bomb detectable?
import pygame
import random
import math
drag = 1
gravity = (math.pi, .4)
elasticity = .75
# Colors
BLACK = ( 0, 0, 0)
WHITE = ( 255, 255, 255)
BLUE = ( 0, 0, 255)
RED = ( 255, 0, 0)
GREEN = ( 0, 255, 0)
# Screen dimensions
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
def addVectors((angle1, length1), (angle2, length2)):
x = math.sin(angle1) * length1 + math.sin(angle2) * length2
y = math.cos(angle1) * length1 + math.cos(angle2) * length2
angle = 0.5 * math.pi - math.atan2(y, x)
length = math.hypot(x, y)
return (angle, length)
class Player(pygame.sprite.Sprite):
change_x = 0
change_y = 0
level = None
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('player.png')
image_rect = self.image.get_rect()
self.rect = pygame.rect.Rect(x, y, image_rect.width, image_rect.height)
def update(self):
pass
def move(self):
(self.angle, self.speed) = addVectors((self.angle, self.speed), gravity)
self.rect.x += math.sin(self.angle) * self.speed
self.rect.y -= math.cos(self.angle) * self.speed
self.speed *= drag
def bounce(self):
if self.rect.x > 800 - self.rect.width:
self.rect.x = 2*(800 - self.rect.width) - self.rect.x
self.angle = - self.angle
self.speed *= elasticity
elif self.rect.x < 0:
self.rect.x = 2*self.rect.width - self.rect.x
self.angle = - self.angle
self.speed *= elasticity
if self.rect.y > SCREEN_HEIGHT - self.rect.height:
self.rect.y = 2*(SCREEN_HEIGHT - self.rect.height) - self.rect.y
self.angle = math.pi - self.angle
self.speed *= elasticity
class Bomb(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('cherry.png')
image_rect = self.image.get_rect()
self.rect = pygame.rect.Rect(x, y, image_rect.width, image_rect.height)
class Level():
bomb_list = None
world_shift = 0
def __init__(self, player):
self.bomb_list = pygame.sprite.Group()
self.player = player
def update(self):
self.bomb_list.update()
def draw(self, screen):
screen.fill(BLACK)
self.bomb_list.draw(screen)
def shift_world(self, shift_x):
self.world_shift += shift_x
for bomb in self.bomb_list:
bomb.rect.x += shift_x
if bomb.rect.x < 0:
self.bomb_list.remove(bomb)
self.bomb_list.add(Bomb(random.randint(SCREEN_WIDTH, 2*SCREEN_WIDTH), 580))
I am not sure if this Level_01 class is even necessary:
class Level_01(Level):
def __init__(self, player):
Level.__init__(self, player)
bombs = 0
for n in range(10):
self.bomb_list.add(Bomb(random.randint(0, SCREEN_WIDTH), 580))
This was my attempt at a collision detection method. I'm not sure if it should be in a class, in main, or seperate. I can't seem to get the list of bombs, and the player at the same time.
def detectCollisions(sprite1, sprite_group):
if pygame.sprite.spritecollideany(sprite1, sprite_group):
sprite_group.remove(pygame.sprite.spritecollideany(sprite1, sprite_group))
print True
else:
print False
def main():
pygame.init()
size = [SCREEN_WIDTH, SCREEN_HEIGHT]
screen = pygame.display.set_mode(size)
active_sprite_list = pygame.sprite.Group()
enemy_list = pygame.sprite.Group()
player = Player(0, 0)
player.speed = 30
player.angle = math.radians(45)
player.rect.x = 500
player.rect.y = SCREEN_HEIGHT - player.rect.height
active_sprite_list.add(player)
done = False
clock = pygame.time.Clock()
current_level = Level_01(player)
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player.go_left()
if event.key == pygame.K_RIGHT:
player.go_right()
active_sprite_list.update()
enemy_list.update()
player.level = current_level
player.move()
player.bounce()
if player.rect.x >= 500:
diff = player.rect.x - 500
player.rect.x = 500
current_level.shift_world(-diff)
if player.rect.x <= 120:
diff = 120 - player.rect.x
player.rect.x = 120
current_level.shift_world(diff)
current_level.draw(screen)
active_sprite_list.draw(screen)
enemy_list.draw(screen)
clock.tick(60)
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()
Thanks for helping me out!
Probably the easiest thing to do is to draw out pixels (or virtual pixels) and if in drawing bomb/person pixels you find an overlap then you know a collision occurred.
You can however get way more complicated (and efficient) in your collision detection if you need a higher performance solution. See Wikipedia Collision Detection for a reference.
I suggest creating sprite groups using pygame.sprite.Group() for each class; Bomb and Player. Then, use pygame.sprite.spritecollide().
For example:
def Main()
...
player_list = pygame.sprite.Group()
bomb_list = pygame.sprite.Group()
...
Then in your logic handling loop, after creating a Player and Bomb instance, you could do something like this:
for bomb in bomb_list:
# See if the bombs has collided with the player.
bomb_hit_list = pygame.sprite.spritecollide(bomb, player_list, True)
# For each bomb hit, remove bomb
for bomb in bomb_hit_list:
bomb_list.remove(bomb)
# --- Put code here that will trigger with each collision ---

Categories