I'm fairly new to python and I'm trying to build a game with Pygame. I kept having issues with collisions not being recognized. Here's the code I tried
import pygame
pygame.init()
WIDTH, HEIGHT = (900, 500)
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Bong Pong')
FPS = 60
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BORDER = pygame.Rect(WIDTH// 2 -5, 0, 10, HEIGHT)
VEL = 3
PLAYER_HEIGHT = 50
PLAYER_WIDTH = 15
BALL_HEIGHT = 15
BALL_WIDTH = 15
def draw_window(player_1, player_2, game_ball):
WIN.fill(WHITE)
pygame.draw.rect(WIN, BLACK, BORDER)
pygame.draw.rect(WIN, BLACK, player_1)
pygame.draw.rect(WIN, BLACK, player_2)
pygame.draw.rect(WIN, RED, game_ball)
pygame.display.update()
def player_1_movement(keys_pressed, player_1):
if keys_pressed[pygame.K_w] and player_1.y - VEL > 0:
player_1.y -= VEL
if keys_pressed [pygame.K_s] and player_1.y + PLAYER_HEIGHT + VEL < 500:
player_1.y += VEL
def player_2_movement(keys_pressed, player_2):
if keys_pressed[pygame.K_UP] and player_2.y - VEL > 0:
player_2.y -= VEL
if keys_pressed [pygame.K_DOWN] and player_2.y + PLAYER_HEIGHT + VEL < 500:
player_2.y += VEL
def player_collision(player_1, player_2, game_ball, ball_vel_x):
if game_ball.colliderect(player_1) or game_ball.colliderect(player_2):
ball_vel_x *= -1
def main():
clock = pygame.time.Clock()
run = True
player_1 = pygame.Rect(50, HEIGHT//2 - PLAYER_HEIGHT// 2, PLAYER_WIDTH, PLAYER_HEIGHT)
player_2 = pygame.Rect(850, HEIGHT//2 - PLAYER_HEIGHT// 2, PLAYER_WIDTH, PLAYER_HEIGHT)
game_ball = pygame.Rect(50 + PLAYER_WIDTH, HEIGHT//2 - BALL_HEIGHT// 2, BALL_WIDTH, BALL_HEIGHT)
ball_vel_y = 2
ball_vel_x = 2
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.QUIT()
keys_pressed = pygame.key.get_pressed()
player_1_movement(keys_pressed, player_1)
player_2_movement(keys_pressed, player_2)
draw_window(player_1, player_2, game_ball)
if game_ball.y - BALL_HEIGHT - VEL <= 0 or game_ball.y + BALL_HEIGHT + VEL >= 500:
ball_vel_y *= -1
game_ball.y -= ball_vel_y
player_collision(player_1, player_2, game_ball, ball_vel_x)
game_ball.x += ball_vel_x
main()
if __name__ == '__main__':
main()
I've also tried putting the
game_ball.x += ball_vel_x
in the player_collsion function but it doesn't reverse it properly.
I've already solved the issue by putting the entire player_collision function code inside the main function like so
def main():
clock = pygame.time.Clock()
run = True
player_1 = pygame.Rect(50, HEIGHT//2 - PLAYER_HEIGHT// 2, PLAYER_WIDTH, PLAYER_HEIGHT)
player_2 = pygame.Rect(850, HEIGHT//2 - PLAYER_HEIGHT// 2, PLAYER_WIDTH, PLAYER_HEIGHT)
game_ball = pygame.Rect(50 + PLAYER_WIDTH, HEIGHT//2 - BALL_HEIGHT// 2, BALL_WIDTH, BALL_HEIGHT)
ball_vel_y = 2
ball_vel_x = 2
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.QUIT()
keys_pressed = pygame.key.get_pressed()
player_1_movement(keys_pressed, player_1)
player_2_movement(keys_pressed, player_2)
draw_window(player_1, player_2, game_ball)
if game_ball.y - BALL_HEIGHT - VEL <= 0 or game_ball.y + BALL_HEIGHT + VEL >= 500:
ball_vel_y *= -1
game_ball.y -= ball_vel_y
if game_ball.colliderect(player_1) or game_ball.colliderect(player_2):
ball_vel_x *= -1
game_ball.x += ball_vel_x
main()
and this works exactly how I want it I just want to clear up my understanding of why this function wouldn't work properly if just called in the main function instead of putting it directly in.
Python has no concept of in-out parameters. The argument is passed by value. If you change ball_vel_x in the player_collision function, only the parameter changes, but the argument remains unchanged. You must return the new value of ball_vel_x from the function:
def player_collision(player_1, player_2, game_ball, ball_vel_x):
if game_ball.colliderect(player_1) or game_ball.colliderect(player_2):
ball_vel_x *= -1
return ball_vel_x
def main():
# [...]
while run:
# [...]
ball_vel_x = player_collision(player_1, player_2, game_ball, ball_vel_x)
# [...]
Another possibility is to store the velocity in an object (e.g. gyame.math.Vector2). A variable stores a reference to an object, so you can change the object's attributes in the function if the variable is an argument of the function call:
def player_collision(player_1, player_2, game_ball, ball_vel):
if game_ball.colliderect(player_1) or game_ball.colliderect(player_2):
ball_vel.x *= -1
def main():
# [...]
ball_vel = pygame.math.Vector2(2, 2)
while run:
# [...]
if game_ball.y - BALL_HEIGHT - VEL <= 0 or game_ball.y + BALL_HEIGHT + VEL >= 500:
ball_vel.y *= -1
game_ball.y -= ball_vel.y
player_collision(player_1, player_2, game_ball, ball_vel)
game_ball.x += ball_vel.x
Related
I coded a Pong game but the ball goes out of the window.
import pygame
pygame.init()
WIDTH, HEIGHT = 900, 500
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
#Colors
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLACK = (0, 0, 0)
#Game Variables
VEL = 5
FPS = 60
CENTER = (WIDTH/2, HEIGHT/2)
DOTRAD = 10
DOT_speed_x = 2
DOT_speed_y = 0
DOT = pygame.Rect(CENTER[0], CENTER[1], DOTRAD, DOTRAD)
#Player
PLAYER1 = pygame.Rect(50, HEIGHT/2-75, 10, 150)
PLAYER2 = pygame.Rect(850, HEIGHT/2-75, 10, 150)
def draw_window():
WIN.fill(WHITE)
pygame.draw.rect(WIN, BLACK, PLAYER1)
pygame.draw.rect(WIN, BLACK, PLAYER2)
pygame.draw.circle(WIN, BLACK, DOT[0:2], DOTRAD)
pygame.display.update()
def player1_handle_movement(keys_pressed):
if keys_pressed[pygame.K_w] and PLAYER1.y > 0: #Up
PLAYER1.y -= VEL
if keys_pressed[pygame.K_s] and PLAYER1.y + PLAYER1.height < HEIGHT: #Down
PLAYER1.y += VEL
def player2_handle_movement(keys_pressed):
if keys_pressed[pygame.K_UP] and PLAYER2.y > 0: #Up
PLAYER2.y -= VEL
if keys_pressed[pygame.K_DOWN] and PLAYER2.y + PLAYER2.height < HEIGHT: #Down
PLAYER2.y += VEL
def DOT_handle_movement (DOT_speed_x, DOT_speed_y):
DOT.x += DOT_speed_x
DOT.y += DOT_speed_y
if DOT.top <= 0 or DOT.bottom >= HEIGHT:
DOT_speed_y *= -1
if DOT.left <= 0 or DOT.right >= WIDTH:
DOT_speed_x *= -1
print(DOT_speed_x, DOT_speed_y)
def main():
clock = pygame.time.Clock()
run = True
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys_pressed = pygame.key.get_pressed()
player1_handle_movement(keys_pressed)
player2_handle_movement(keys_pressed)
DOT_handle_movement(DOT_speed_x, DOT_speed_y)
draw_window()
pygame.quit()
if __name__ == "__main__":
main()
I print the DOT_speed_x and DOT_speed_y to see if it works that the ball detects that its out of the window.
Yes it does.
But its not changing the direction.
I saw so many tutorials that say that this method should work fine but for me it doesn't.
In Python, there is no concept of in-out parameters. If you change DOT_speed_x or DOT_speed_y, only the parameter variable that is local to the function changes, but not the arguments that are passed to the function. You have to return the new values of DOT_speed_x and DOT_speed_y from the function.
def DOT_handle_movement(DOT_speed_x, DOT_speed_y):
DOT.x += DOT_speed_x
DOT.y += DOT_speed_y
if DOT.top <= 0 or DOT.bottom >= HEIGHT:
DOT_speed_y *= -1
if DOT.left <= 0 or DOT.right >= WIDTH:
DOT_speed_x *= -1
return DOT_speed_x, DOT_speed_y
def main():
DOT_speed_x = 2
DOT_speed_y = 0
clock = pygame.time.Clock()
run = True
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys_pressed = pygame.key.get_pressed()
player1_handle_movement(keys_pressed)
player2_handle_movement(keys_pressed)
DOT_speed_x, DOT_speed_y = DOT_handle_movement(DOT_speed_x, DOT_speed_y)
draw_window()
pygame.quit()
Note that it is absolutely not necessary that the function arguments and the functions parameter variables have the same name.
# DONT CHANGE THIS
import pygame, os
pygame.init()
pygame.font.init()
WIDTH, HEIGHT = 450, 250
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Space Spar")
# VARIABLES
HEALTH_FONT = pygame.font.SysFont('comicsans', 20)
WINNER_FONT = pygame.font.SysFont('comicsans', 50)
WINCOUNT_FONT = pygame.font.SysFont('comicsans', 20)
SPACE = pygame.transform.scale(pygame.image.load(os.path.join("Assets", "space.png")), (WIDTH, HEIGHT))
BLUE = (0, 0, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
WHITE = (255, 255, 255)
BORDER = pygame.Rect(WIDTH//2 - 5, 0, 5, HEIGHT)
FPS = 60
VEL = 3.5
SHIP_WIDTH, SHIP_HEIGHT, = 25, 20
BULLETS_VEL = 10
MAX_BULLETS = 1
YELLOW_HIT = pygame.USEREVENT + 1
RED_HIT = pygame.USEREVENT + 2
YELLOW_SPACESHIP_IMAGE = pygame.image.load(os.path.join('Assets', "spaceship_yellow.png"))
YELLOW_SPACESHIP = pygame.transform.rotate(pygame.transform.scale(YELLOW_SPACESHIP_IMAGE, (SHIP_HEIGHT, SHIP_WIDTH)), 90)
RED_SPACESHIP_IMAGE = pygame.image.load(os.path.join('Assets', "spaceship_red.png"))
RED_SPACESHIP = pygame.transform.rotate(pygame.transform.scale(RED_SPACESHIP_IMAGE, (SHIP_WIDTH, SHIP_HEIGHT)), 270)
# PLACE ANYTHING ON THE WINDOW HERE
# +y = down from top left
# +x = right from top left
def draw_window(red, yellow, red_bullets, yellow_bullets, red_health, yellow_health, red_win_count, yellow_win_count):
WIN.blit(SPACE, (0, 0))
pygame.draw.rect(WIN, BLACK, BORDER)
red_health_text = HEALTH_FONT.render("Health: " + str(red_health), 1, WHITE)
yellow_health_text = HEALTH_FONT.render("Health: " + str(yellow_health), 1, WHITE)
WIN.blit(red_health_text, (WIDTH - red_health_text.get_width() - 10, 10))
WIN.blit(yellow_health_text, (10, 10))
yellow_win_text = WINCOUNT_FONT.render('Wins: ' + str(yellow_win_count), 1, WHITE)
red_win_text = WINCOUNT_FONT.render('Wins: ' + str(red_win_count), 1, WHITE)
WIN.blit(red_win_text, (WIDTH - red_health_text.get_width() - 70, 10))
pygame.display.update()
WIN.blit(yellow_win_text, (70, 10))
pygame.display.update()
WIN.blit(YELLOW_SPACESHIP, (yellow.x, yellow.y))
WIN.blit(RED_SPACESHIP, (red.x, red.y))
for bullet in red_bullets:
pygame.draw.rect(WIN, RED, bullet)
for bullet in yellow_bullets:
pygame.draw.rect(WIN, RED, bullet)
pygame.display.update()
# MOVEMENT
def yellow_movement(keys_pressed, yellow):
if keys_pressed[pygame.K_a] and yellow.x - VEL > 0: #left
yellow.x -= VEL
if keys_pressed[pygame.K_d] and yellow.x + VEL + yellow.width < BORDER.x: #right
yellow.x += VEL
if keys_pressed[pygame.K_w] and yellow.y - VEL > 0: #up
yellow.y -= VEL
if keys_pressed[pygame.K_s] and yellow.y + VEL + yellow.height < HEIGHT: #dowm
yellow.y += VEL
def red_movement(keys_pressed, red):
if keys_pressed[pygame.K_LEFT] and red.x - VEL > BORDER.x + BORDER.width: #left
red.x -= VEL
if keys_pressed[pygame.K_RIGHT] and red.x + VEL + red.width < WIDTH: #right
red.x += VEL
if keys_pressed[pygame.K_UP] and red.y - VEL > 0: #up
red.y -= VEL
if keys_pressed[pygame.K_DOWN] and red.y + VEL + red.height < HEIGHT: #dowm
red.y += VEL
def handle_bullets(yellow_bullets, red_bullets, yellow, red):
for bullet in yellow_bullets:
bullet.x += BULLETS_VEL
if red.colliderect(bullet):
pygame.event.post(pygame.event.Event(RED_HIT))
yellow_bullets.remove(bullet)
elif bullet.x > WIDTH:
yellow_bullets.remove(bullet)
for bullet in red_bullets:
bullet.x -= BULLETS_VEL
if yellow.colliderect(bullet):
pygame.event.post(pygame.event.Event(YELLOW_HIT))
red_bullets.remove(bullet)
elif bullet.x < 0:
red_bullets.remove(bullet)
def draw_winner(text):
draw_text = WINNER_FONT.render(text, 1, WHITE)
WIN.blit(draw_text, (WIDTH//2 - draw_text.get_width()/2, HEIGHT/2 - draw_text.get_height()/2))
pygame.display.update()
pygame.time.delay(1000)
# MAIN LOOP
def main():
red = pygame.Rect(350, 150,SHIP_WIDTH, SHIP_HEIGHT)
yellow = pygame.Rect(50, 150, SHIP_WIDTH, SHIP_HEIGHT)
red_bullets = []
yellow_bullets = []
red_health = 1
yellow_health = 1
red_win_count = 0
yellow_win_count = 0
clock = pygame.time.Clock()
run = True
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE and len(yellow_bullets) < MAX_BULLETS:
bullet = pygame.Rect(yellow.x + yellow.width, yellow.y + yellow.height//2 - 1, 15, 4)
yellow_bullets.append(bullet)
if event.key == pygame.K_LCTRL and len(red_bullets) < MAX_BULLETS:
bullet = pygame.Rect(red.x, red.y + red.height//2 - 1, 15, 4)
red_bullets.append(bullet)
if event.type == RED_HIT:
red_health -= 1
if event.type == YELLOW_HIT:
yellow_health -= 1
winner_text = ""
if red_health <= 0:
yellow_win_count += 1
pygame.display.update()
winner_text = "Yellow is cooler"
if yellow_health <= 0:
winner_text = "Red is cooler"
red_win_count += 1
pygame.display.update()
if winner_text != "":
draw_winner(winner_text)
main()
keys_pressed = pygame.key.get_pressed()
yellow_movement(keys_pressed, yellow)
red_movement(keys_pressed, red)
handle_bullets(yellow_bullets, red_bullets, yellow, red)
draw_window(red, yellow, red_bullets, yellow_bullets, red_health, yellow_health, red_win_count, yellow_win_count)
pygame.quit()
# this is to make sure its opening the right thing blah blaho
if __name__ == "__main__":
main()
I'm trying to display a win count through a variable but the variable isn't updating on the screen, why?
I took a look at the Github version of this game and determined that what you are wanting to do is add an enhancement that shows wins on the screen. The issue at the moment is that once a spaceship wins and the program again starts "main()", the win variables get reset to "0" before the screen is redisplayed.
red_health = 1
yellow_health = 1
red_win_count = 0
yellow_win_count = 0
As a quick tweak, instead of constantly restarting the game by calling "main" at the end of the "while" block, I revised the last "if" block to just do the resetting of the player health and then continue on with the while loop function.
if winner_text != "":
draw_winner(winner_text)
red_health = 4
yellow_health = 4
continue
#main()
That then allowed for viewing the win count on the screen.
You might want to check to see if any other variables need to be reset, but you can give that a try.
First time posting, I can't figure out how to make the character in my game drag the camera when it hits the edge of the screen. Using clamp just stops my character from moving when hitting a certain point (not at the place I want it to). Any help would be appreciated.
import pygame, os
pygame.init()
SPEED = 10
SCREEN_W, SCREEN_H = 800, 600
MAP_W, MAP_H = SCREEN_W*3, SCREEN_H*3
WIN = pygame.display.set_mode((SCREEN_W, SCREEN_H))
FPS = 60
clock = pygame.time.Clock()
back = pygame.image.load("my_image")
BACKGROUND = pygame.transform.scale(back, (MAP_W, MAP_H))
CAMERA_X1, CAMERA_Y1, CAMERA_X2, CAMERA_Y2 = 0, 0, SCREEN_W, SCREEN_H
def draw_screen(player):
WIN.fill((255,255,255))
WIN.blit(BACKGROUND, (CAMERA_X1, CAMERA_Y1))
pygame.draw.rect(WIN, (0,0,0), (player.x, player.y, 50, 50))
pygame.display.update()
def handle_movement(pressed, player):
global CAMERA_X1, CAMERA_Y1, CAMERA_X2, CAMERA_Y2
if pressed[pygame.K_a]: #left
if player.x+50 < CAMERA_X1:
CAMERA_X1 -= SPEED*2
else:
player.x -= SPEED
if pressed[pygame.K_d]: #right
if player.x-50 > CAMERA_X2:
CAMERA_X2 += SPEED*2
else:
player.x += SPEED
if pressed[pygame.K_w]: #up
if player.y+50 < CAMERA_Y1:
CAMERA_Y1 -= SPEED*2
else:
player.y -= SPEED
if pressed[pygame.K_s]: #down
if player.y-50 > CAMERA_Y2:
CAMERA_Y2 += SPEED*2
else:
player.y += SPEED
def main(): #Main game loop
player = pygame.Rect(0, 0, 50, 50)
running = True
while running:
clock.tick(FPS)
draw_screen(player)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
keys_pressed = pygame.key.get_pressed()
handle_movement(keys_pressed, player)
pygame.quit()
if __name__ == "__main__":
main()
I'm relatively new to pygame so please try to explain as thoroughly as possible :)
So a camera is basically just an offset added to everything in the scene. Here is a
scene with just a controllable rectangle and a background and no camera.
import pygame, os
pygame.init()
SPEED = 10
SCREEN_W, SCREEN_H = 800, 600
MAP_W, MAP_H = SCREEN_W*3, SCREEN_H*3
WIN = pygame.display.set_mode((SCREEN_W, SCREEN_H))
FPS = 60
clock = pygame.time.Clock()
BACKGROUND = pygame.Surface((MAP_W, MAP_H)).convert()
sq = pygame.Surface((50, 50)).convert()
sq.fill((255, 255, 0))
for x in range(27):
for y in range(18):
BACKGROUND.blit(sq, (x * 100, y * 100))
cameraOffset = [0 ,0]
def draw_screen(player):
WIN.fill((255,255,255))
WIN.blit(BACKGROUND, (0, 0))
playerPos = (player.x, player.y)
pygame.draw.rect(WIN, (100, 10, 255), (*playerPos, player.width, player.height))
pygame.display.update()
def handle_movement(pressed, player):
global cameraOffset, playerScreenPos
player.x += (pressed[pygame.K_d] - pressed[pygame.K_a]) * SPEED
player.y += (pressed[pygame.K_s] - pressed[pygame.K_w]) * SPEED
def main(): #Main game loop
player = pygame.Rect(0, 0, 50, 50)
running = True
while running:
clock.tick(FPS)
draw_screen(player)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
keys_pressed = pygame.key.get_pressed()
handle_movement(keys_pressed, player)
pygame.quit()
if __name__ == "__main__":
main()
Now, how do you make a camera follow that rectangle? Simple, just add an offset value to everything in the scene. We only have a background and a rectangle in the scene so the above code changes accordingly:
#WIN.blit(BACKGROUND, (0, 0))
WIN.blit(BACKGROUND, (0 - cameraOffset[0], 0 - cameraOffset[1]))
#playerPos = (player.x, player.y)
playerPos = (player.x - cameraOffset[0], player.y - cameraOffset[1])
All this does is moves the objects by the offset amount. Now the only thing left to figure out is what the offset value needs to be. The offset value should be the player position - half the screen size. This causes the rectangle to always be in the centre.
cameraOffset[0] += player.x - cameraOffset[0] - (SCREEN_W * 0.5)
cameraOffset[1] += player.y - cameraOffset[1] - (SCREEN_H * 0.5)
Notice the player position is written out in form of vector. This allows us to add
a delayed following effect by reducing the size of the vector by dividing it by something.
delay = 10
cameraOffset[0] += (player.x - cameraOffset[0] - (SCREEN_W * 0.5)) / delay
cameraOffset[1] += (player.y - cameraOffset[1] - (SCREEN_H * 0.5)) / delay
Then, based on your needs you can clamp the camera offsets so it won't pan beyond a
certain limit.
cameraOffset[0] = max(0, min(cameraOffset[0], MAP_W - SCREEN_W))
cameraOffset[1] = max(0, min(cameraOffset[1], MAP_H - SCREEN_H))
Here is the code after camera is added. As you can see, not much has changed.
import pygame, os
pygame.init()
SPEED = 10
SCREEN_W, SCREEN_H = 800, 600
MAP_W, MAP_H = SCREEN_W*3, SCREEN_H*3
WIN = pygame.display.set_mode((SCREEN_W, SCREEN_H))
FPS = 60
clock = pygame.time.Clock()
BACKGROUND = pygame.Surface((MAP_W, MAP_H)).convert()
sq = pygame.Surface((50, 50)).convert()
sq.fill((255, 255, 0))
for x in range(27):
for y in range(18):
BACKGROUND.blit(sq, (x * 100, y * 100))
delay = 10
cameraOffset = [0 ,0]
def draw_screen(player):
WIN.fill((255,255,255))
WIN.blit(BACKGROUND, (0 - cameraOffset[0], 0 - cameraOffset[1]))
playerPos = (player.x - cameraOffset[0], player.y - cameraOffset[1])
pygame.draw.rect(WIN, (100, 10, 255), (*playerPos, player.width, player.height))
pygame.display.update()
def handle_movement(pressed, player):
global cameraOffset, playerScreenPos
player.x += (pressed[pygame.K_d] - pressed[pygame.K_a]) * SPEED
player.y += (pressed[pygame.K_s] - pressed[pygame.K_w]) * SPEED
cameraOffset[0] += (player.x - cameraOffset[0] - (SCREEN_W * 0.5)) / delay
cameraOffset[1] += (player.y - cameraOffset[1] - (SCREEN_H * 0.5)) / delay
cameraOffset[0] = max(0, min(cameraOffset[0], MAP_W - SCREEN_W))
cameraOffset[1] = max(0, min(cameraOffset[1], MAP_H - SCREEN_H))
def main(): #Main game loop
player = pygame.Rect(0, 0, 50, 50)
running = True
while running:
clock.tick(FPS)
draw_screen(player)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
keys_pressed = pygame.key.get_pressed()
handle_movement(keys_pressed, player)
pygame.quit()
if __name__ == "__main__":
main()
This question already has an answer here:
Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)?
(1 answer)
Closed 2 years ago.
I was trying to make a game where you chop down some trees and sell it, but in doing so I managed to find a mind-breaking bug - the first tree would invert all the other trees! I know it doesn't make sense, but what i mean is that I have sorted the trees into a class and I have created a variable instance called self.tree_property which determines if the tree has been cut down or not. When you chop down the first tree all the other tree_property get set to True again. When you chop down any other tree, the first tree's tree_property gets set to True again Can anyone tell me how to fix it, and why it is happening?
The code is below:
import pygame
import time
pygame.init()
print('Loading game...')
icon = pygame.image.load('C:\Program Files\Foraging Simulator\icon.png')
market = pygame.image.load('C:\Program Files\Foraging Simulator\market.png')
house = pygame.image.load('C:\Program Files\Foraging Simulator\house.png')
pygame.display.set_icon(icon)
market = pygame.transform.scale(market, (100, 100))
house = pygame.transform.scale(house, (100, 100))
root = pygame.display.set_mode((603, 573))
pygame.display.set_caption("Foraging Simulator")
window_is_open = True
white = (255, 255, 255)
black = (0, 0, 0)
width = 10
leaves_width = 30
height = 20
leaves_height = 10
x = 0
tree_trunk_x = 10
y = 0
tree_trunk_y = 10
vel = 5
brown = (150, 75, 0)
green = (58, 95, 11)
grass = (124, 252, 0)
score = 0
chop_wood = 'C:\Program Files\Foraging Simulator\chopping.mp3'
clock = pygame.time.Clock()
time = 0
happiness = 10
def test(string):
print(string)
def reset_trees():
for tree in trees:
tree.tree_property = True
def open_house():
reset_trees()
score = 0
def open_market():
happiness = score / 2
class Tree: # The class for the trees
def __init__(self, tree_x, tree_y):
self.tree_x = tree_x
self.tree_y = tree_y
self.tree_property = True # Creating instance tree_property
self.trunk = None
self.leaves = None
def destroy(self):
self.tree_property = False
def create_tree(self):
if self.tree_property:
trunk_x = self.tree_x + 10
trunk_y = self.tree_y + 10
self.trunk = pygame.draw.rect(root, brown, (trunk_x, trunk_y, width, height))
self.leaves = pygame.draw.rect(root, green, (self.tree_x, self.tree_y, leaves_width, leaves_height))
def redraw(self):
self.create_tree()
trees = []
for x in range(5):
for y in range (5):
trees.append(Tree(x*50, y*50))
root.fill(grass)
destroy_tree = None
countdown = 3
clock.tick(60)
say = True
print('Loading and attributes finsihed! Using mainloop...')
while window_is_open:
if say:
print('Mainloop loaded! Ready to go.')
say = False
time = pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
window_is_open = False
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = event.pos
if market.get_rect().collidepoint(mouse_x, mouse_y):
dx = mouse_x - x
dy = mouse_y - y
if abs(dx) <= 50 and abs(dy) <= 50:
open_market()
mouse_x, mouse_y = event.pos
if house.get_rect().collidepoint(mouse_x, mouse_y):
dx = mouse_x - x
dy = mouse_y - y
if abs(dx) <= 50 and abs(dy) <= 50:
open_house()
for tree in trees: # Clicking detection
mouse_x, mouse_y = pygame.mouse.get_pos()
if tree.trunk.collidepoint(mouse_x, mouse_y):
dx = mouse_x - x
dy = mouse_y - y
if abs(dx) <= 50 and abs(dy) <= 50:
countdown = 3
destroy_tree = tree
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
x += vel
if keys[pygame.K_LEFT]:
x -= vel
if keys[pygame.K_UP]:
y -= vel
if keys[pygame.K_DOWN]:
y += vel
if destroy_tree != None:
if countdown == 3:
pygame.time.delay(950)
countdown = countdown - 1
pygame.mixer.music.load(chop_wood)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
pygame.time.Clock().tick(1)
elif countdown > 0:
pygame.mixer.music.load(chop_wood)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
pygame.time.Clock().tick(1)
pygame.time.delay(950)
countdown = countdown - 1
else:
destroy_tree.destroy()
destroy_tree = None
countdown = 3
score = score + 1
font = pygame.font.SysFont('Tahoma', 18, True, False)
count = font.render(str(countdown), True, (0, 0, 0))
screen_score = font.render("Score: " + str(score), True, (0, 0, 0))
rendered_happiness = font.render("Happines: " + str(happiness), True, (0, 0, 0))
root.blit(rendered_happiness, (410, 40))
root.blit(count, (410, 0))
root.blit(screen_score, (410, 20))
rectangle = pygame.draw.rect(root, (0, 0, 0), (x, y, width, 10))
for tree in trees:
tree.redraw()
root.blit(market, (400, 300))
seconds = clock.tick()
pre = time + seconds / 1000
time = int(pre)
root.blit(house, (400, 100))
pygame.display.update()
root.fill(grass)
pygame.quit()
Thanks!
A pygame.Surface object has no location. The position of the rectangle which is returned by get_rect() is always (0, 0).
Note, you specify a location when the surface is blit:
root.blit(house, (400, 100))
You have to set the same location, when you retrieve the rectangle for the collision. You can pass keyword argument values to this function, which are set to the pygame.Rect object:
house.get_rect(topleft = (400, 100))
For instance:
while window_is_open:
# [...]
for event in pygame.event.get():
if event.type == pygame.QUIT:
window_is_open = False
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = event.pos
dx = mouse_x - x
dy = mouse_y - y
if market.get_rect(topleft = (400, 300)).collidepoint(mouse_x, mouse_y):
if abs(dx) <= 50 and abs(dy) <= 50:
open_market()
if house.get_rect(topleft = (400, 100)).collidepoint(mouse_x, mouse_y):
if abs(dx) <= 50 and abs(dy) <= 50:
open_house()
for tree in trees:
if tree.trunk.collidepoint(mouse_x, mouse_y):
if abs(dx) <= 50 and abs(dy) <= 50:
countdown = 3
destroy_tree = tree
# [...]
Furthermore in you have to use the global statement to treat happiness as a variable in global namespace in the function open_market:
def open_market():
global happiness
happiness = score / 2
I was programming my game, with no lag and no problems. Suddenly, when I added my intro the game started lagging.
I have tried making a lot of functions, and replacing a lot of numbers with variables.
I removed the intro, and the game still lagged.
I just need help finding out why my game lags so much, even if it is as simple as this.
import pygame
import sys
import random
pygame.init()
pygame.display.set_caption("Game Draft")
clock = pygame.time.Clock()
# Variables
width = 500
height = 500
LightBlue = (0, 150, 215)
Pink = (215, 0, 100)
Green = (51, 204, 51)
Black = (0, 0, 0)
Blue = (0,0,255)
Yellow = (255, 255, 0)
White = (255, 255, 255)
background_color = (102, 204, 255)
scoreboard_color = (255,255,255)
plataform_color = (153, 102, 51)
intro_background = (128, 128, 128)
player_name = "Neme"
player_color = (0, 0, 0)
player_size = 20
player_vel_x = 20
player_vel_y = 10
player_pos = [width/2, height - (2 * player_size)]
enemy_color = (102, 51, 0)
enemy_size = 20
enemy_vel_x = 20
enemy_vel_y = 5
enemy_pos = [random.randint(0, width-enemy_size), (24 / 25 * height)]
enemy_list = [enemy_pos]
enemy_dif = 4
enemy_dif_increase = 2
enemy_amount = 30
prop_color = (0, 51, 204)
prop_size = 1
prop_vel_y = 5
prop_vel_x = 5
prop_pos = [random.randint(0, width - prop_size), (21 / 25 * height)]
prop_list = [prop_pos]
prop_dif = 4
prop_dif_increase = 2
prop_amount = 50
intro_size1 = 50
intro_size2 = 40
intro_sentence1 = "Welcome to Game Draft"
intro_sentence2 = "Press key to start!"
scoreboard_font = pygame.font.SysFont("monoface", 50)
ign_font = pygame.font.SysFont("monoface", player_size)
intro_font1 = pygame.font.SysFont("monoface", intro_size1)
intro_font2 = pygame.font.SysFont("monoface", intro_size2)
score = 0
#Velocity Functions
def player_level_y(score, player_vel_y):
pvy = player_vel_y
sc = score
if sc < 100:
pvy = player_size*.25
elif sc < 200:
pvy = player_size*.5
elif sc < 300:
pvy = player_size*.75
elif sc < 400:
pvy = player_size
elif sc < 500:
pvy = player_size*1.25
else:
pvy = player_size * 1.25
return pvy
def player_level_x(score, player_vel_x):
sc = score
pvx = player_vel_x
if sc < 100:
pvx = player_size/2
elif sc < 200:
pvx = player_size*.75
elif sc < 300:
pvx = player_size*1
elif sc < 400:
pvx = player_size*1.15
elif sc < 500:
pvx = player_size*1.25
else:
pvx = player_size * 1.25
return pvx
def enemy_level_y(score, enemy_vel_y):
sc = score
evy = enemy_vel_y
if sc < 100:
evy = enemy_dif + enemy_dif_increase*1
elif sc < 300:
evy = enemy_dif + enemy_dif_increase*2
elif sc < 500:
evy = enemy_dif + enemy_dif_increase*3
elif sc < 700:
evy = enemy_dif + enemy_dif_increase*4
elif sc < 1500:
evy = enemy_dif + enemy_dif_increase*5
else:
evy = enemy_dif + enemy_dif_increase*6
return enemy_vel_y
#Enemey Functions
def drop_enemies(enemy_list):
delay = random.random()
if len(enemy_list) < enemy_amount and delay < 0.1:
x_pos = random.randint(0, width - enemy_size)
y_pos = enemy_size
enemy_list.append([x_pos, y_pos])
def draw_enemies(enemy_list):
for enemy_pos in enemy_list:
pygame.draw.rect(screen, enemy_color, (enemy_pos[0], enemy_pos[1], enemy_size, enemy_size))
def update_enemy_pos(enemy_list, score):
for idx, enemy_pos in enumerate(enemy_list):
if enemy_pos[1] >= 0 and enemy_pos[1] <= height:
enemy_pos[1] += enemy_vel_y
else:
enemy_list.pop(idx)
score += 5
return score
# Prop Functions
def drop_props(prop_list):
delay = random.random()
if len(prop_list) < prop_amount and delay < 0.1:
x_pos = random.randint(0, width - prop_size)
y_pos = prop_size
prop_list.append([x_pos, y_pos])
def draw_props(prop_list):
for prop_pos in prop_list:
pygame.draw.rect(screen, prop_color, (prop_pos[0], prop_pos[1], width/20, prop_size))
def update_prop_pos(prop_list):
for idx, prop_pos in enumerate(prop_list):
if prop_pos[1] >= 0 and prop_pos[1] <= height:
prop_pos[1] += prop_vel_y
else:
prop_list.pop(idx)
# Boarder Functions
def boarder_left(player_pos):
if player_pos[0] <= 0 - player_size:
return True
return False
def boarder_right(player_pos):
if player_pos[0] >= width:
return True
return False
def boarder_down(player_pos):
if player_pos[1] >= height - player_size:
return True
return False
# Game_Over Functions
def collision_check(enemy_list, player_pos):
for enemy_pos in enemy_list:
if detect_collision(enemy_pos, player_pos):
return True
return False
def detect_collision(player_pos, enemy_pos):
p_x = player_pos[0]
p_y = player_pos[1]
e_x = enemy_pos[0]
e_y = enemy_pos[1]
if (e_x >= p_x and e_x < (p_x + enemy_size)) or (p_x >= e_x and p_x < (e_x + player_size)):
if (e_y >= p_y and e_y < (p_y + enemy_size)) or (p_y >= e_y and p_y < (e_y + player_size)):
return True
return False
#Winning Function
def winning(player_pos):
if player_pos[1] <= 0 - player_size:
return True
return False
# Intro Screen
screen = pygame.display.set_mode((width, height))
intro = True
while intro:
pygame.time.delay(100)
for event in pygame.event.get():
keys = pygame.key.get_pressed()
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
intro = False
screen.fill(intro_background)
welcome = str(intro_sentence1)
tfs1 = intro_font1.render(welcome, 1, White)
screen.blit(tfs1, (width/4 - intro_size1, height/3))
welcome = str(intro_sentence2)
tfs1 = intro_font2.render(welcome, 1, White)
screen.blit(tfs1, (width/4 - intro_size2, height/2))
clock.tick(60)
pygame.display.update()
# Game Screen
screen = pygame.display.set_mode((width, height))
game_over = False
run = True
while run:
pygame.time.delay(100)
for event in pygame.event.get():
keys = pygame.key.get_pressed()
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
x = player_pos[0]
y = player_pos[1]
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
x -= player_vel_x
elif keys[pygame.K_d] or keys[pygame.K_RIGHT]:
x += player_vel_x
elif keys[pygame.K_s] or keys[pygame.K_DOWN]:
y += player_vel_y
elif keys[pygame.K_w] or keys[pygame.K_UP]:
y -= player_vel_y
elif keys[pygame.K_PERIOD]:
enemy_dif += 1
elif keys[pygame.K_COMMA]:
enemy_dif -= 1
player_pos = [x,y]
screen.fill(background_color)
drop_enemies(enemy_list)
drop_props(prop_list)
score = update_enemy_pos(enemy_list, score)
player_vel_y = player_level_y(score, player_vel_y)
player_vel_x = player_level_x(score, player_vel_x)
enemy_vel_y = enemy_level_y(score, enemy_vel_y)
update_prop_pos(prop_list)
if boarder_left(player_pos):
player_pos[0] = width - player_size/2
if boarder_right(player_pos):
player_pos[0] = 0 - player_size/2
if boarder_down(player_pos):
player_pos[1] = height - player_size
if winning(player_pos):
enemy_amount = 0
enemy_vel_y = 0
player_pos[1] = 0
if collision_check(enemy_list, player_pos):
game_over = True
break
pygame.draw.rect(screen, plataform_color, (0, 0, width, height - (23 / 25 * height) ) )
pygame.draw.rect(screen, player_color, (player_pos[0], player_pos[1], player_size, player_size))
draw_enemies(enemy_list)
draw_props(prop_list)
scoreboard = str(score)
tfs2 = scoreboard_font.render(scoreboard, 1, scoreboard_color)
screen.blit(tfs2, (width - 125, height - ( .98 * height)))
ign = str(player_name)
tfs3 = ign_font.render(ign, 1, White)
screen.blit(tfs3, (player_pos[0] - player_size/4, player_pos[1] + player_size))
clock.tick(60)
pygame.display.update()
No error, just random lag.
[...] need help finding out why my game lags so much [...]
Of course, what do you expect? You've a delay of a 0.1 seconds in the game loop:
pygame.time.delay(100)
Delete the delay. Use pygame.time.Clock to control the frames per second and thus the game speed.
Further move the code which moves the player out of the event loop in the main application loop for a smooth movement:
while run:
#pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# <--
keys = pygame.key.get_pressed()
x = player_pos[0]
y = player_pos[1]
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
x -= player_vel_x
elif keys[pygame.K_d] or keys[pygame.K_RIGHT]:
x += player_vel_x
elif keys[pygame.K_s] or keys[pygame.K_DOWN]:
y += player_vel_y
elif keys[pygame.K_w] or keys[pygame.K_UP]:
y -= player_vel_y
elif keys[pygame.K_PERIOD]:
enemy_dif += 1
elif keys[pygame.K_COMMA]:
enemy_dif -= 1
player_pos = [x,y]
# [...]
clock.tick(30)