my error:
pygame 2.1.0 (SDL 2.0.16, Python 3.10.0)
Hello from the pygame community. >https://www.pygame.org/contribute.html
Traceback (most recent call last):
File "c:\Users\Tameem\PycharmProjects\pythonProject\Games\Pygame_first_game.py", line 200, in <module>
main()
File "c:\Users\Tameem\PycharmProjects\pythonProject\Games\Pygame_first_game.py", line 194, in main
main()
File "c:\Users\Tameem\PycharmProjects\pythonProject\Games\Pygame_first_game.py", line 180, in main
draw_winner(winner_text)
File "c:\Users\Tameem\PycharmProjects\pythonProject\Games\Pygame_first_game.py", line 124, in draw_winner
WIN.blit(draw_text, (WIDTH/2 - draw_text.get_width()/2, HEIGHT/2 - draw_text.get_height()/2))
pygame.error: display Surface quit
my code:
import pygame
import os
from pygame import display
pygame.font.init()
from pygame import event #opereting system
WIDTH, HEIGHT = 900, 500 #this is to make the WINDOW
WIN = pygame.display.set_mode((WIDTH, HEIGHT)) #your telling python that you want a window with this height and width
pygame.display.set_caption("Maisa Ahmed") #it sets a name for the game
DARK_GREEN = (00, 64, 00)
BLACK = (0, 0, 0)
BLOOD_RED = (136, 8, 8)
YELLOW = (255, 255, 0)
WHITE = (255, 255, 255)
health_font = pygame.font.SysFont("comicsans", 40)
winner_font = pygame.font.SysFont("comicsans", 100)
boarder = pygame.Rect(WIDTH//2-5, 0, 10, HEIGHT) # so i-5 because you want to draw it from 0 , 0 (left)
FPS = 60 #to contol how many times your game refersh per second
SPACESHIP_WIDTH , SPACESHIP_HEIGHT = 55,40
velocity = 5
Bullet_velocity = 7
MAX_BULLETS = 5
YELLOW_HITS = pygame.USEREVENT + 1
RED_HITS = pygame.USEREVENT + 2
#importing images
YELLOW_SPACESHIP_IMAGE = pygame.image.load(
os.path.join('Assets', 'spaceship_yellow.png')) # assets to go in the that folder and which file
YELLOW_SPACESHIP = pygame.transform.rotate(pygame.transform.scale(
YELLOW_SPACESHIP_IMAGE, (SPACESHIP_WIDTH, SPACESHIP_HEIGHT)), 90) #so now the space ship is smaller and has rotated
RED_SPACESHIP_IMAGE = pygame.image.load(
os.path.join('Assets', "spaceship_red.png"))
RED_SPACESHIP = pygame.transform.rotate(pygame.transform.scale(RED_SPACESHIP_IMAGE, (SPACESHIP_WIDTH, SPACESHIP_HEIGHT)),270)
background = pygame.transform.scale(pygame.image.load(
os.path.join('Assets', 'space.png')), (WIDTH, HEIGHT)) #Mom.jpg
def draw_window(red ,yellow, red_bullets, yellow_bullets, red_health, yellow_health): #the order in which you draw matters
WIN.blit(background, (0,0))
#WIN.fill(DARK_GREEN) #to fill color to the WINDOW
pygame.draw.rect(WIN, BLACK, boarder)
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))
WIN.blit(YELLOW_SPACESHIP, (yellow.x, yellow.y))# you use blit when you want draw a surface on the screen (basically used to put the images/texts on the screen)
WIN.blit(RED_SPACESHIP, (red.x, red.y)) # are cordinates
for bullet in red_bullets:
pygame.draw.rect(WIN, BLOOD_RED, bullet)
for bullet in yellow_bullets:
pygame.draw.rect(WIN, YELLOW, bullet)
pygame.display.update() #you have to update the WINDOW to show anychanges
def yellow_movement(keys_pressed, yellow):
if keys_pressed[pygame.K_a] and yellow.x - velocity > 0: # left not allowing to cross the boarder
yellow.x -= velocity
if keys_pressed[pygame.K_d] and yellow.x + velocity + yellow.width < boarder.x: # right not allowing to cross the boarder
yellow.x += velocity
if keys_pressed[pygame.K_w] and yellow.y - velocity > 0: # up not allowing to cross the boarder
yellow.y -= velocity
if keys_pressed[pygame.K_s] and yellow.y + velocity + yellow.height < HEIGHT - 10: # down not allowing to cross the boarder
yellow.y += velocity
def red_movement(keys_pressed, red):
if keys_pressed[pygame.K_LEFT] and red.x - velocity > boarder.x + boarder.width: # left
red.x -= velocity
if keys_pressed[pygame.K_RIGHT] and red.x + velocity + red.width < WIDTH: # right
red.x += velocity
if keys_pressed[pygame.K_UP] and red.y - velocity > 0:# up
red.y -= velocity
if keys_pressed[pygame.K_DOWN] and red.y + velocity + red.height < HEIGHT - 10: # down
red.y += velocity
def handle_bullets(yellow_bullets, red_bullets, yellow, red):
for bullet in yellow_bullets:
bullet.x += Bullet_velocity
if red.colliderect(bullet):
pygame.event.post(event.Event(RED_HITS))
yellow_bullets.remove(bullet)
elif bullet.x > WIDTH:
yellow_bullets.remove(bullet)
for bullet in red_bullets:
bullet.x -= Bullet_velocity
if yellow.colliderect(bullet):
pygame.event.post(event.Event(YELLOW_HITS))
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(5000)
def main():
red = pygame.Rect(700, 300, SPACESHIP_WIDTH, SPACESHIP_HEIGHT)
yellow = pygame.Rect(100, 300, SPACESHIP_WIDTH, SPACESHIP_HEIGHT) # x,y , width , height
red_bullets = []
yellow_bullets = []
red_health = 10
yellow_health = 10
clock = pygame.time.Clock() # create a clock object which can be used to keep track of time
run = True
while run: #event loop
clock.tick(FPS) #ensures that you will never go above the FPS you set the game on
for event in pygame.event.get():
if event.type == pygame.QUIT: #.QUIT to end pygame in the loop
run = False
pygame.quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LCTRL and len(yellow_bullets) < MAX_BULLETS:
bullet = pygame.Rect(
yellow.x + yellow.width, yellow.y + yellow.height//2 - 2,10,5) # so it goes from the middle
yellow_bullets.append(bullet)
if event.key == pygame.K_RCTRL and len(red_bullets) < MAX_BULLETS:
bullet = pygame.Rect(
red.x, red.y + red.height//2 - 2,10,5) # so it goes from the middle
red_bullets.append(bullet)
if event.type == RED_HITS:
red_health -= 1
if event.type == YELLOW_HITS:
yellow_health -= 1
winner_text = ''
if red_health <= 0:
winner_text = "Yellow WINS!"
if yellow_health <= 0:
winner_text = "Red WINS!"
if winner_text == '':
draw_winner(winner_text)
break
keys_pressed = pygame.key.get_pressed() # this tells us what keys are being clicked every 1
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) #you took the yellow and red to the Drawing funcations
main()
#pygame.quit .quit to end pygame in general
if __name__ == "__main__": #doesnt matter
main()
the code was working fine until i added the winning messages , if you anyone has a fix please tell me.
The problem is that you call pygame.quit() in the application loop. You have to call pygame.quit() after the application loop:
def main():
# [...]
run = True
while run: #event loop
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# pygame.quit() <--- DELETE
# [...]
pygame.quit() # <--- INSERT
Note, when pygame.quit() is executed, all PyGame modules will be uninstalled. Every further call to a PyGame function throws an exception.
I'd like to draw your attention to the block that, per comments, you just added.
winner_text = ''
if red_health <= 0:
winner_text = "Yellow WINS!"
if yellow_health <= 0:
winner_text = "Red WINS!"
if winner_text == '':
draw_winner(winner_text)
break
This will break out of the game loop the first time a non-winning condition is encountered, so probably directly at the start. You probably intended quite the opposite.
You might also want to make this compatible with the overall looping mechanism via the run variable, so either universally leave the loop via setting run=False or universally use the break keyword. This then leads to the point of the other answer, the clean-up should happen for all ways that the main loop can be exited, so best centralized after the loop.
Related
This question already has an answer here:
Faster version of 'pygame.event.get()'. Why are events being missed and why are the events delayed?
(1 answer)
Closed 5 months ago.
I'm trying to create a game in pygame where you are a rectangle trying to shoot a bullet towards another rectangle, when you click a mousebutton. I'm using this with the pygame.MOUSEBUTTONDOWN, as you can see here:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN and ammunition > 0:
bullets.append(Bullet(*bullet_pos))
ammunition -= 1
**I'll include more code for reference later.
However, when I click the mouse button, it only shoots a bullet every few times I click. I checked multiple times, and there doesn't seem to be a pattern of when it does/doesn't shoot. It shoots about every 4-5 times, but that's just an estimate.
Full code, for reference:
import pygame
import math
import random
import time
pygame.init()
# Setting all variables to use later \/
width, height = 798, 552
white = pygame.Color('white')
black = pygame.Color('black')
green = pygame.Color('green')
blue = pygame.Color('blue')
red = pygame.Color('red')
grey = pygame.Color('gray')
yellow = pygame.Color('yellow')
orange = pygame.Color('orange')
azure = pygame.Color('azure')
size_x = 100
size_y = 50
pos_x = 100
pos_y = 275
size_x_2 = 100
size_y_2 = 50
pos_x_2 = random.randint(0, 798)
pos_y_2 = random.randint(0, 552)
pos_x_3 = random.randint(0, 798)
pos_y_3 = random.randint(0, 552)
window = pygame.display.set_mode((width, height))
bullet_pos_y = random.randint(0, 552)
bullet_pos_x = 798
ammunition = 5
enemy_health = 1000
player_health = 50
font = pygame.font.SysFont('Times New Roman', 32)
shooting_x = 0
shooting_y = 0
# Setting All Variables to use later /\
class Bullet:
def __init__(self, x, y):
self.pos = (x, y)
self.dir = (shooting_x - x, shooting_y - y)
length = math.hypot(*self.dir)
if length == 0.0:
self.dir = (0, -1)
else:
self.dir = (self.dir[0]/length, self.dir[1]/length)
angle = math.degrees(math.atan2(-self.dir[1], self.dir[0]))
self.bullet = pygame.Surface((27.5, 17.5)).convert_alpha()
self.bullet.fill((red))
self.bullet = pygame.transform.rotate(self.bullet, angle)
self.speed = 1
def update(self):
self.pos = (self.pos[0] + self.dir[0] * self.speed, self.pos[1] + self.dir[1] * self.speed)
def draw(self, surf):
bullet_rect = self.bullet.get_rect(center = self.pos)
surf.blit(self.bullet, bullet_rect)
bullets = []
keys = pygame.key.get_pressed()
# EVERYTHING BELOW THIS IS IN THE GAME LOOP, EVERYTHING ABOVE ISN'T
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill(black)
# Drawing Everything
character = pygame.draw.rect(window, white, (pos_x, pos_y, size_x, size_y))
enemy = pygame.draw.rect(window, blue, (shooting_x, shooting_y, size_x, size_y))
coin = pygame.draw.circle(window, yellow, [pos_x_2, pos_y_2], 15)
# Setting Text
enemy_health_text = font.render(str(enemy_health), True, white, blue)
enemy_health_textRect = enemy_health_text.get_rect()
enemy_health_textRect.center = (enemy.center)
player_health_text = font.render(str(player_health), True, black, white)
player_health_textRect = enemy_health_text.get_rect()
player_health_textRect.center = (character.center[0] + 9, character.center[1])
ammunition_text = font.render("ammunition remaining: " + str(ammunition), True, azure, black)
ammunition_textRect = ammunition_text.get_rect()
ammunition_textRect.center = (205, 25)
bullet_pos = character.center
enemy_pos = enemy.center
# Shooting a bullet
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN and ammunition > 0:
bullets.append(Bullet(*bullet_pos))
ammunition -= 1
# Enemy Dealing Damage to Player
if enemy.colliderect(character):
player_health -= 1
white = pygame.Color('red')
if not enemy.colliderect(character):
player_health -= 0
white = pygame.Color('white')
mouse_pos_x, mouse_pos_y = pygame.mouse.get_pos()
pos_x, pos_y = pygame.mouse.get_pos()
# If Character collides with coin
if character.colliderect(coin):
pos_x_2 = random.randint(0, 798)
pos_y_2 = random.randint(100, 552)
num = random.randint(0, 20)
if num == 17:
yellow = pygame.Color('purple')
ammunition += 5
if num != 17:
yellow = pygame.Color('yellow')
ammunition += 2
elif enemy.colliderect(coin):
pos_x_2 = random.randint(0, 798)
pos_y_2 = random.randint(100, 552)
enemy_health += 3
# Setting the Enemy Movement
if shooting_x < pos_x_2:
shooting_x += 0.1
if shooting_x > pos_x_2:
shooting_x -= 0.1
if shooting_y < pos_y_2:
shooting_y += 0.1
if shooting_y > pos_y_2:
shooting_y -= 0.1
# Updating/Drawing Bullets
for bullet in bullets:
bullet.update()
''' WORK ON THIS '''
if not window.get_rect().collidepoint(bullet.pos):
bullets.remove(bullet)
for bullet in bullets:
bullet.draw(window)
# Making sure the player doesn't leave boundaries
if pos_y >= 552:
pos_y = 552
if pos_y <= 0:
pos_y = 0
if pos_x <= 0:
pos_x = 0
if pos_x >= 700:
pos_x = 700
# Drawing all text on screen
window.blit(ammunition_text, ammunition_textRect)
window.blit(enemy_health_text, enemy_health_textRect)
window.blit(player_health_text, player_health_textRect)
pygame.display.update()
The function pygame.event.get() returns an object with all events that happens. It clears all events afterwards so nothing will be executed twice when an event occurs. So you shouldn't call it twice without checking all needed events both times. Some events might get ignored when you don't call pygame.event.get() often because it has a limit of 128 events.
As a solution you can save the events in a variable (Some events might be delayed):
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
run = False
...
for event in events:
if event.type == pygame.MOUSEBUTTONDOWN and ammunition > 0:
bullets.append(Bullet(*bullet_pos))
ammunition -= 1
Or you only have one event loop:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.MOUSEBUTTONDOWN and ammunition > 0:
bullets.append(Bullet(*bullet_pos))
ammunition -= 1
...
# 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()
I am working on a pong game. When either of the scores hit 10, it is supposed to put up some text on the screen and say the right player has won or left player has won. However, in my program, it isn't working. When it has to show the text that the right or left player has won, it doesn't show it. But it works for everything else. Here is the code:
# Importing libraries
import pygame
import random
import time
# Initializing PyGame
pygame.init()
# Setting a window name
pygame.display.set_caption("Ping Pong")
# Creating a font
pygame.font.init()
font = pygame.font.SysFont(None, 30)
pong_font = pygame.font.SysFont("comicsansms", 75)
# Set the height and width of the screen
window_width = 700
window_height = 500
size = [window_width, window_height]
game_win = pygame.display.set_mode(size)
game_win2 = pygame.display.set_mode(size)
# Creating a messaging system
def message(sentence, color, x, y, font_type, display):
sentence = font_type.render(sentence, True, color)
display.blit(sentence, [x, y])
# Creating colors
white = (225, 225, 225)
black = (0, 0, 0)
gray = (100, 100, 100)
# Setting up ball
ball_size = 25
class Ball:
"""
Class to keep track of a ball's location and vector.
"""
def __init__(self):
self.x = 0
self.y = 0
self.change_x = 0
self.change_y = 0
def make_ball():
ball = Ball()
# Starting position of the ball.
ball.x = 350
ball.y = 250
# Speed and direction of rectangle
ball.change_x = 5
ball.change_y = 5
return ball
def main():
# Scores
left_score = 0
right_score = 0
pygame.init()
# Loop until the user clicks the close button.
done = False
ball_list = []
ball = make_ball()
ball_list.append(ball)
# Right paddle coordinates
y = 200
y_change = 0
x = 50
# Left paddle coordinates
y1 = 200
y1_change = 0
x1 = 650
while not done:
# --- Event Processing
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
y_change = -7
elif event.key == pygame.K_s:
y_change = 7
elif event.key == pygame.K_UP:
y1_change = -7
elif event.key == pygame.K_DOWN:
y1_change = 7
elif event.type == pygame.KEYUP:
y_change = 0
y1_change = 0
y += y_change
y1 += y1_change
# Preventing from letting the paddle go off screen
if y > window_height - 100:
y -= 10
if y < 50:
y += 10
if y1 > window_height - 100:
y1 -= 10
if y1 < 50:
y1 += 10
# Logic
for ball in ball_list:
# Move the ball's center
ball.x += ball.change_x
ball.y += ball.change_y
# Bounce the ball if needed
if ball.y > 500 - ball_size or ball.y < ball_size:
ball.change_y *= -1
if ball.x > window_width - ball_size:
ball.change_x *= -1
left_score += 1
if ball.x < ball_size:
ball.change_x *= -1
right_score += 1
ball_rect = pygame.Rect(ball.x - ball_size, ball.y - ball_size, ball_size * 2, ball_size * 2)
left_paddle_rect = pygame.Rect(x, y, 25, 75)
if ball.change_x < 0 and ball_rect.colliderect(left_paddle_rect):
ball.change_x = abs(ball.change_x)
right_paddle_rect = pygame.Rect(x1, y1, 25, 75)
if ball.change_x > 0 and ball_rect.colliderect(right_paddle_rect):
ball.change_x = -abs(ball.change_x)
# Here is the where the messaging system doesn't work, I don't know why! It works fine for everything else
if right_score == 10:
message("RIGHT PLAYER HAS WON!!", white, 300, 200, font, game_win)
time.sleep(5)
pygame.quit()
quit()
elif left_score == 10:
message("LEFT PLAYER HAS WON!!", white, 300, 200, font, game_win)
time.sleep(5)
pygame.quit()
quit()
# Drawing
# Set the screen background
game_win.fill(black)
# Draw the balls
for ball in ball_list:
pygame.draw.circle(game_win, white, [ball.x, ball.y], ball_size)
# Creating Scoreboard
message("Left player score: " + str(left_score), white, 10, 10, font, game_win)
message("Right player score: " + str(right_score), white, 490, 10, font, game_win)
# Drawing a left paddle
pygame.draw.rect(game_win, white, [x, y, 25, 100])
# Drawing a right paddle
pygame.draw.rect(game_win, white, [x1, y1, 25, 100])
# Setting FPS
FPS = pygame.time.Clock()
FPS.tick(60)
# Updating so actions take place
pygame.display.flip()
while True:
game_win2.fill(black)
pygame.event.get()
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
message("Pong", white, 280, 100, pong_font, game_win2)
if 150 + 100 > mouse[0] > 150 and 350 + 50 > mouse[1] > 350:
pygame.draw.rect(game_win, gray, [150, 350, 100, 50])
if click[0] == 1:
break
else:
pygame.draw.rect(game_win, white, [150, 350, 100, 50])
if 450 + 100 > mouse[0] > 450 and 350 + 50 > mouse[1] > 350:
pygame.draw.rect(game_win, gray, [450, 350, 100, 50])
if click[0] == 1:
pygame.quit()
quit()
else:
pygame.draw.rect(game_win, white, [450, 350, 100, 50])
message("Start", black, 175, 367, font, game_win2)
message("Quit", black, 475, 367, font, game_win2)
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Wrap-up
# Limit to 60 frames per second
clock = pygame.time.Clock()
clock.tick(60)
if __name__ == "__main__":
main()
I have added a little comment, it is: "# Here is the where the messaging system doesn't work, I don't know why! It works fine for everything else". Now when someone scores 10 points, Nothing happens. Its= wait for a couple of seconds. That is so you can read the "Left player has won" or "Right player has won" before the program closes. But it simply doesn't show up! I don't know why! Can someone help with this?
The display is updated only if either pygame.display.update() or pygame.display.flip()
is called. See pygame.display.flip():
This will update the contents of the entire display.
Further you've to handles the events with pygame.event.pump(), before the update of the display becomes visible in the window.
See pygame.event.pump():
For each frame of your game, you will need to make some sort of call to the event queue. This ensures your program can internally interact with the rest of the operating system.
If you want to display a text and delay the game, then you've to update the display and handle the events.
Write a function which delays the game and updates the display. I recommend to use the pygame.time module to implement the delay (e.g. pygame.time.delay())
def update_and_wait(delay):
pygame.display.flip()
pygame.event.pump()
pygame.time.delay(delay * 1000) # 1 second == 1000 milliseconds
Or even implement a function which its own event loop to keep the application responding. Measure the time by pygame.time.get_ticks():
def update_and_wait(delay):
start_time = pygame.time.get_ticks()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
print("auit")
pygame.quit()
return False
if pygame.time.get_ticks() >= start_time + delay * 1000:
break
return True
Use the function in the application:
def main():
# [...]
while not done:
# [...]
for ball in ball_list:
# [...]
if right_score == 0:
message_wait("RIGHT PLAYER HAS WON!!", white, 300, 200, font, game_win)
update_and_wait(5)
quit()
elif left_score == 0:
message_wait("LEFT PLAYER HAS WON!!", white, 300, 200, font, game_win)
update_and_wait(5)
quit()
I am making a game with pygame where you have to dodge falling objects. I am having trouble with the collision detection, as when the obstacle touches the player, it just passes through to the bottom. Here is my code.
pygame.K_LEFT:
p_x_change = 0
screen.fill(WHITE)
pygame.draw.rect(screen,BLUE,(p_x,p_y, 60, 60))
pygame.draw.rect(screen,RED,(e_x,e_y,100,100))
p_x += p_x_change
e_y += e_ychange
if e_y > display_height:
e_y = 0
e_x = random.randint(1,display_width)
#Collision detection below
elif e_y == p_y - 90 and e_x == p_x :
done = True
clock.tick(60)
pygame.display.update()
Could you tell me what is wrong with my code?
I suggest to create two pygame.Rects, one for the player and one for the enemy. Then move the enemy by incrementing the y attribute of the rect and use the colliderect method to see if the two rects collide.
import random
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
display_width, display_height = screen.get_size()
clock = pygame.time.Clock()
BG_COLOR = pygame.Color('gray12')
BLUE = pygame.Color('dodgerblue1')
RED = pygame.Color('firebrick1')
# Create two rects (x, y, width, height).
player = pygame.Rect(200, 400, 60, 60)
enemy = pygame.Rect(200, 10, 100, 100)
e_ychange = 2
done = False
while not done:
# Event handling.
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
player.x -= 5
elif keys[pygame.K_d]:
player.x += 5
# Game logic.
enemy.y += e_ychange # Move the enemy.
if enemy.y > display_height:
enemy.y = 0
enemy.x = random.randint(1, display_width)
# Use the colliderect method for the collision detection.
elif player.colliderect(enemy):
print('collision')
# Drawing.
screen.fill(BG_COLOR)
pygame.draw.rect(screen, BLUE, player)
pygame.draw.rect(screen, RED, enemy)
pygame.display.flip()
clock.tick(30)
pygame.quit()