I cant seem to understand the clock function in pygame as much i search and code it if it is possible can you help me with this code that is trying to simply make the square move with the up down left and right arrows and if possible and if you have time simply help me understand the clock system.
import pygame
import sys
pygame.init()
fps = 30
fpsclock=pygame.time.Clock()
window = pygame.display.set_mode((600, 600))
# main application loop
run = True
while run:
# limit frames per second
# event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# clear the display
window.fill(0)
# draw the scene
color = (255,0,0)
x = 275
y = 275
key_input = pygame.key.get_pressed() #key imputs
pygame.draw.rect(window, color, pygame.Rect(x,y,60,60))
pygame.display.flip()
if key_input[pygame.K_LEFT]:
x - 1
if key_input[pygame.K_RIGHT]:
x + 1
if key_input[pygame.K_DOWN]:
y + 1
if key_input[pygame.K_UP]:
y - 1
pygame.display.update()
fpsclock.tick(fps)
# update the display
pygame.display.flip()
pygame.quit()
exit()
x -= 1 instead of x - 1 and x += 1 instead of x + 1. Do the same for y. x and y needs to be initialized before the application instead of in the loop. So you have to do x = 275 and y = 275 before the application loop:
import pygame
import sys
pygame.init()
fps = 30
fpsclock=pygame.time.Clock()
window = pygame.display.set_mode((600, 600))
x = 275
y = 275
color = (255,0,0)
# main application loop
run = True
while run:
# limit frames per second
fpsclock.tick(fps)
# event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
key_input = pygame.key.get_pressed() #key imputs
if key_input[pygame.K_LEFT]:
x -= 1
if key_input[pygame.K_RIGHT]:
x += 1
if key_input[pygame.K_DOWN]:
y += 1
if key_input[pygame.K_UP]:
y -= 1
# clear the display
window.fill(0)
# draw the scene
pygame.draw.rect(window, color, pygame.Rect(x,y,60,60))
# update the display
pygame.display.flip()
pygame.quit()
exit()
if you have time simply help me understand the clock system.
Use pygame.time.Clock to control the frames per second and thus the game speed. The method tick() of a pygame.time.Clock object, delays the game in that way, that every iteration of the loop consumes the same period of time. See pygame.time.Clock.tick():
This method should be called once per frame.
That means that the loop:
fps = 30
fpsclock=pygame.time.Clock()
while run:
fpsclock.tick(fps)
runs 30 times per second.
There are some problems about it:
Change x - 1 to x -= 1
And put x = 275 and y = 275 after defining window
Finally:
import pygame
import sys
pygame.init()
fps = 30
fpsclock=pygame.time.Clock()
window = pygame.display.set_mode((600, 600))
x = 275
y = 275
# main application loop
run = True
while run:
# limit frames per second
# event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# clear the display
window.fill(0)
# draw the scene
color = (255,0,0)
key_input = pygame.key.get_pressed() #key imputs
pygame.draw.rect(window, color, pygame.Rect(x,y,60,60))
pygame.display.flip()
if key_input[pygame.K_LEFT]:
x -= 1
if key_input[pygame.K_RIGHT]:
x += 1
if key_input[pygame.K_DOWN]:
y += 1
if key_input[pygame.K_UP]:
y -= 1
pygame.display.update()
fpsclock.tick(fps)
# update the display
pygame.display.flip()
pygame.quit()
exit()
Related
I am trying to make an aim game where a target pops up and once the player clicks on it, the target vanishes and a new one appears in a random location, I want it so that there is a 10 second timer but it keeps going back to 10 each time a target is "hit"
import pygame, random as r, time
FPS = 60
WIDTH = 900
HEIGHT = 500
WHITE = 255,255,255
BG = 26,26,26
RANGEXMIN = 20
RANGEXMAX = 840
RANGEYMIN = 20
RANGEYMAX = 440
window = pygame.display.set_mode((WIDTH, HEIGHT))
tick = pygame.USEREVENT
pygame.time.set_timer(tick,1000)
pygame.font.init()
FONT = pygame.font.Font('slkscr.ttf', 50)
def aim_train():
def new_target(countdown,text):
clock = pygame.time.Clock()
x = r.randint(RANGEXMIN, RANGEXMAX)
y = r.randint(RANGEYMIN, RANGEYMAX)
hit = False
while not hit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.USEREVENT:
if event.type == tick:
countdown= countdown - 1
text = str(countdown)
clock.tick(FPS)
window.fill(BG)
timer = FONT.render(text, False, WHITE)
window.blit(timer, (435, 20))
pygame.mouse.set_visible(False)
pos = pygame.mouse.get_pos()
pos_x = pos[0]
pos_y = pos[1]
target = pygame.draw.rect(window, WHITE, (x,y,50,50))
cursor_outline = pygame.draw.circle(window, BG, (pos_x,pos_y), 11)
cursor = pygame.draw.circle(window, WHITE,(pos_x,pos_y) ,10)
hit = (pygame.mouse.get_pressed()[0] and target.colliderect(cursor_outline))
pygame.display.update()
run = True
countdown = 10
text = str(countdown)
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill(BG)
pygame.mouse.set_visible(False)
new_target(countdown,text)
pygame.quit()
aim_train()
The variable "counter" somehow gets reset back to 10 after ever successful hit on a target
The problem is that you use a seperate function, new_target, for each target.
The countdown variable is defined inside the scope of the function aim_train. Because the new_target function is defined inside the aim_train function, it is a nested function and it can use all the variables that are defined inside aim_train. However, the new_target function still has its own scope. It can make changes to any variables defined inside aim_train, but those changes remain in its own scope. When a call of new_target is ended, its scope is discarded and all changes to the variables of aim_target are undone. This causes the countdown variable to be reset every in new call of new_target and thus every time a new target is created.
You might also have noticed that you can't close your window. The window doesn't react to clicking the red cross. This is because the same applies to the run variable. When you click the red cross, the run variable is set to True inside new_target, but not in the scope of aim_train. As such, the main loop in aim_train is not quitted and the program continues.
As a solution to this problem, I would recommend to include all the code of new_target into the aim_train function. Then you only have one function, which makes that all changes to variables are in the same scope and no changes are discarded:
import pygame, random as r, time
pygame.init()
FPS = 60
WIDTH = 900
HEIGHT = 500
WHITE = 255,255,255
BG = 26,26,26
RANGEXMIN = 20
RANGEXMAX = 840
RANGEYMIN = 20
RANGEYMAX = 440
window = pygame.display.set_mode((WIDTH, HEIGHT))
tick = pygame.USEREVENT
pygame.time.set_timer(tick,1000)
pygame.font.init()
FONT = pygame.font.SysFont('arial', 50)
def aim_train():
run = True
hit = False
countdown = 10
text = str(countdown)
x = r.randint(RANGEXMIN, RANGEXMAX)
y = r.randint(RANGEYMIN, RANGEYMAX)
clock = pygame.time.Clock()
pygame.mouse.set_visible(False)#switched
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == tick: #changed
countdown= countdown - 1
text = str(countdown)
window.fill(BG)
timer = FONT.render(text, False, WHITE)
window.blit(timer, (435, 20))
pos = pygame.mouse.get_pos()
pos_x = pos[0]
pos_y = pos[1]
target = pygame.draw.rect(window, WHITE, (x,y,50,50))
cursor_outline = pygame.draw.circle(window, BG, (pos_x,pos_y), 11)
cursor = pygame.draw.circle(window, WHITE,(pos_x,pos_y) ,10)
hit = (pygame.mouse.get_pressed()[0] and target.colliderect(cursor_outline))
if hit:
hit = False
x = r.randint(RANGEXMIN, RANGEXMAX)
y = r.randint(RANGEYMIN, RANGEYMAX)
pygame.display.update()
pygame.quit()
aim_train()
Apart from fixing the problem, I have also restructured your code a bit and did the following changes:
I have changed:
elif event.type == pygame.USEREVENT:
if event.type == tick:
into:
elif event.type == tick:
Outside of the aim_train function, you have stated that tick and pygame.USEREVENT are equal. As such, it is useless to compare to them two times, because if the first check is true, then the second one will certainly be.
I've placed pygame.mouse.set_visible(False) outside of the main loop.
Calling the function sets the mouse invisible untill another call changes sets the mouse back to visible. As such, it is useless to call it multiple times in the loop.
There are actually 2 countdown variables, one in the new_target function and one in the aim_train function. If you change the variable countdown in the new_target function, this will not change the variable countdown in the aim_train function. You must return the new value of countdown from the new_target function:
def aim_train():
def new_target(countdown):
# [...]
while not hit:
for event in pygame.event.get():
# [...]
elif event.type == pygame.USEREVENT:
if event.type == tick:
countdown= countdown - 1
# [...]
return countdown
run = True
countdown = 10
while run:
# [...]
countdown = new_target(countdown)
However, I suggest that you restructure your code. Do not use nested application loops. Also see Pygame mouse clicking detection:
import pygame, random as r
FPS = 60
WIDTH = 900
HEIGHT = 500
WHITE = 255,255,255
BG = 26,26,26
RANGEXMIN = 20
RANGEXMAX = 840
RANGEYMIN = 20
RANGEYMAX = 440
window = pygame.display.set_mode((WIDTH, HEIGHT))
tick = pygame.USEREVENT
pygame.time.set_timer(tick,1000)
pygame.font.init()
FONT = pygame.font.Font('slkscr.ttf', 50)
def new_target():
x = r.randint(RANGEXMIN, RANGEXMAX)
y = r.randint(RANGEYMIN, RANGEYMAX)
return x, y
def aim_train():
clock = pygame.time.Clock()
pygame.mouse.set_visible(False)
run = True
countdown = 10
hits = 0
countdownSurf = FONT.render(f'time {countdown}', False, WHITE)
hitsSurf = FONT.render(f'hits {hits}', False, WHITE)
target = pygame.Rect(0, 0, 50, 50)
target.center = new_target()
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == tick:
countdown -= 1
countdownSurf = FONT.render(f'time {countdown}', False, WHITE)
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
if target.collidepoint(event.pos):
target.center = new_target()
hits += 1
hitsSurf = FONT.render(f'hits {hits}', False, WHITE)
pos = pygame.mouse.get_pos()
window.fill(BG)
window.blit(countdownSurf, countdownSurf.get_rect(center = (300, 45)))
window.blit(hitsSurf, hitsSurf.get_rect(center = (600, 45)))
pygame.draw.rect(window, WHITE, target)
pygame.draw.circle(window, BG, pos, 11)
pygame.draw.circle(window, WHITE, pos, 10)
pygame.display.update()
pygame.quit()
aim_train()
heres the video of the animation https://www.youtube.com/watch?v=uhPdN3v8vg0
other pygame codes dont act like this, only this specific one, so i'm pretty sure its not hardware problem
import sys
import time
import pygame
import os
from pygame import mixer
pygame.init()
pygame.mixer.init()
width,height=(900,500)
border= pygame.Rect(0,0,6,height)
WIN=pygame.display.set_mode((width,height))
bg= pygame.image.load(os.path.join('','pixel_bg.jpg'))
bg=pygame.transform.scale(bg,(width,height))
person1=pygame.image.load(os.path.join('','mario.png'))
p1_width, p1_height = (50,60)
person1=pygame.transform.scale(person1,(p1_width,p1_height))
black=(0,0,0)
p1_rect = pygame.Rect(50,340,p1_width,p1_height)
pygame.display.set_caption("rpg game")
Velocity= 4
jump_height = 50
FPS = 60
mixer.music.load("adventurebeat.mp3")
mixer.music.play(-1)
def draw():
WIN.blit(bg, (0, 0))
WIN.blit(person1, (p1_rect.x,p1_rect.y))
pygame.display.update()
def p1_movement(keys_pressed, p1_rect):
if keys_pressed[pygame.K_a] and p1_rect.x - Velocity > border.x + border.width: # left
p1_rect.x -= Velocity
if keys_pressed[pygame.K_d] and p1_rect.x + Velocity + p1_rect.width < width: # right
p1_rect.x += Velocity
if keys_pressed[pygame.K_w] and p1_rect.y - Velocity > 0: # up
p1_rect.y -= Velocity
if keys_pressed[pygame.K_SPACE] and p1_rect.y - Velocity > 0: # up
p1_rect.y -= jump_height
if keys_pressed[pygame.K_s] and p1_rect.y + Velocity + p1_rect.height < height: # down
p1_rect.y += Velocity
def main():
clock = pygame.time.Clock()
run = True
while run:
clock.tick_busy_loop(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.quit()
sys.exit()
draw()
keys_pressed = pygame.key.get_pressed()
p1_movement(keys_pressed,p1_rect)
main()
I've tried changing the clock.tick_busy_loop() to clock.tick() but it still doesnt work :/
You need to draw the object in the application loop instead of the event loop:
def main():
clock = pygame.time.Clock()
run = True
while run:
clock.tick_busy_loop(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.quit()
sys.exit()
# draw() <--- DELETE
keys_pressed = pygame.key.get_pressed()
p1_movement(keys_pressed,p1_rect)
draw() # <--- INSERT
The application loop is executed in every frame, but the event loop only is executed when an event occurs.
import pygame
Red = 255, 0, 0
Black= 0,0,0
rectXpos = 2
rectypos = 2
speed = 2
screenedgex = 500
pygame.init()
window = pygame.display.set_mode(size=(500, 500))
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.update()
window.fill(Black)
square = pygame.draw.rect(window, Red, [rectXpos, rectypos, 50, 50],2)
rectXpos += 2
if rectXpos < 500:
rectXpos -= 2
clock.tick(60)
print(rectXpos)`enter code here`
so what am i doing wrong? i tried making a if statment to stop the ball and reverse it but it keeps the ball at the edge of the window
This is complete code, I separated the x and y bounces, so you can use either one, also updated the code a bit more, plus some extra formatting.
# Imports
import pygame
# Vars
Red = 255, 0, 0
Black= 0,0,0
rectXpos = 2
rectYpos = 2
rect_width = 50
rect_height = 50
screen_width = 500
screen_height = 500
block_x_direction = 1
block_y_direction = 1
# Setup Code
pygame.init()
window = pygame.display.set_mode(size=(screen_width, screen_height))
clock = pygame.time.Clock()
running = True
# Game Loop
########################################################
while running:
# Event Loop
########################################################
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Game Code - Update
########################################################
# Game Code - Update - Rect X Bounce
if rectXpos + (rect_width)>= screen_width:
block_x_direction = block_x_direction * -1
rectXpos += 2 * block_x_direction
# Game Code - Update - Rect Y Bounce
if rectYpos + (rect_height)>= screen_height:
block_y_direction = block_y_direction * -1
rectYpos += 2 * block_y_direction
# - Tick Game
clock.tick(60)
# Game Code - Render
########################################################
window.fill(Black)
square = pygame.draw.rect(window, Red, [rectXpos, rectYpos, rect_width, rect_height],2)
pygame.display.update()
# Game Code - Debug Code
########################################################
print(clock.tick)
I assume you want to move rectangle to and fro when mouse is moving.
There are 2 things you are doing wrong here:
1. correct this:
if rectXpos > 500: as you have to decrease X when it will reach 500
2. when reach rectXpos 501 it should change its direction till it reach rectXpos 0
but you have decreased position till it is greater than 500 so it will then stuck in between 499 to 501
correct code:
import pygame
Red = 255, 0, 0
Black= 0,0,0
rectXpos = 2
rectypos = 2
speed = 2
screenedgex = 500
pygame.init()
window = pygame.display.set_mode(size=(500, 500))
clock = pygame.time.Clock()
running = True
k=1 #here is k used to indicate direction
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.update()
window.fill(Black)
square = pygame.draw.rect(window, Red, [rectXpos, rectypos, 50, 50],2)
rectXpos += 2*k #here is addition of 2 in given direction
if (rectXpos > 500) or (rectXpos < 0): #here is condition to change direction
k=-k
clock.tick(60)
print(rectXpos)
You should add speed to position and when you touch border then you should change speed to -speed.
You could also use pygame.Rect() to keep position and size - it has properties .left and .right (and other) which can be very useful. And you can use Rect to draw pygame.draw.rect() (or to check collision with other Rect)
import pygame
# --- constants --- (UPPER_CASE_NAMES)
RED = (255, 0, 0)
BLACK = (0, 0, 0)
WIDTH = 500
HEIGHT = 500
# --- main ---
speed = 10
pygame.init()
window = pygame.display.set_mode((WIDTH, HEIGHT))
item = pygame.Rect(0, 0, 50, 50)
clock = pygame.time.Clock()
running = True
while running:
# - events -
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# - updates - (without draws)
item.x += speed
if item.right >= WIDTH:
speed = -speed
if item.left <= 0:
speed = -speed
# - draws - (without updates)
window.fill(BLACK)
pygame.draw.rect(window, RED, item, 2)
pygame.display.update()
clock.tick(60)
# - end -
pygame.quit()
I am trying to make a simple moving game with Pygame since I am currently learning it. Whenever i try to run the code I keep on getting a problem saying: "pygame.error: display Surface quit"
I've tried adding "break" at the end but the window closes immediately! I've tried searching for the solution but I can't find one that helps my code.
import pygame
import random
pygame.init()
# Window setup
size = [400, 400]
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
# player position
x = size[0] // 2
y = size[1] // 2
# ball position
ballX = random.randrange(0, size[0])
ballY = random.randrange(0, size[1])
# colours
red = pygame.color.Color('#FF8080')
blue = pygame.color.Color('#8080FF')
white = pygame.color.Color('#FFFFFF')
black = pygame.color.Color('#000000')
def CheckOffScreenX(x):
if x > size[0]:
x = 0
elif x < 0:
x = size[0]
return x
def CheckOffScreenY(y):
if y > size[1]:
y = 0
elif y < 0:
y = size[1]
return y
# Game loop
done = False
while not done:
screen.fill(black)
keys = pygame.key.get_pressed()
#player movement
if keys[pygame.K_w]:
y -=1
if keys[pygame.K_s]:
y +=1
if keys[pygame.K_a]:
x -=1
if keys[pygame.K_d]:
x +=1
# Check offscreen
x = CheckOffScreenX(x)
y = CheckOffScreenY(y)
# draw player
pygame.draw.circle(screen, red, [x, y], 6)
pygame.display.flip()
# draw ball
pygame.draw.circle(screen, blue, [ballX, ballY], 6)
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
clock.tick(32)
pygame.quit()
Any help would be appreciated!
The issue is the pygame.quit() insider the main loop. pygame.quit() uninitialize all pygame modules. After the modules are uninitialized all further calls to pygyme instructions (in the next frame) will cause a crash.
Do pygame.quit() after the main loop, when the application has end.
done = False
while not done:
screen.fill(black)
# [...]
# pygame.quit() <----- delete
pygame.quit() # <---- add
Note, probably you've added an Indentation when you copied the code.
I'm experimenting, and I'm trying to get a circle to move along a line but stop once it reaches the edge of the screen. once this happens I can no longer go back the other way. There's probably a simple fix I'm not seeing, and it would be helpful to have someone point me in the right direction. Please keep in mind I am still a beginner.
from pygame import *
import random
import math
import os #Displays the pygame window at the top left of the screen
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d, %d" %(0,25)
init() #Starts pygame
font.init()
LENGTH = 1000 #Creates Screen that is 1000 X 700
WIDTH = 700
SIZE = (LENGTH, WIDTH)
Screen = display.set_mode(SIZE)
#Defines colours
BLACK = (0,0,0)
WHITE = (255,255,255)
RED = (255,0,0)
running = True
CaptainY = 350
Key = 0
while running:
for evnt in event.get(): # checks all events that happen
if evnt.type == QUIT: # if event type is quit the program stops running
running = False
if evnt.type == KEYDOWN:
Key = evnt.key
if evnt.type == KEYUP:
Key = 0
if 20 < CaptainY < 680:
if Key == K_UP:
CaptainY -= 5
if Key == K_DOWN:
CaptainY += 5
draw.rect(Screen, BLACK, (0,0, LENGTH, WIDTH))
draw.circle(Screen, WHITE, (950, CaptainY), 15)
if Key == K_ESCAPE:
print(CaptainY)
display.flip()
quit()
The program is doing what you told it: Move only if the y-position is between 20 and 680. If it's less than 20, this condition won't be True and the circle won't be able move anymore.
# You only move if this is True.
if 20 < CaptainY < 680:
Instead of stopping the movement, you should just move the position back, so that the circle ends up on the screen. Here's a complete example with a few more changes:
import pygame # Avoid * imports, since they make code harder to read and cause bugs.
pygame.init()
screen = pygame.display.set_mode((1000, 700))
HEIGHT = screen.get_height()
BLACK = (0,0,0)
WHITE = (255,255,255)
clock = pygame.time.Clock() # Use a clock to limit the frame rate.
running = True
captain_y = 350
captain_radius = 15
while running:
# Handle events.
for evnt in pygame.event.get():
if evnt.type == pygame.QUIT:
running = False
elif evnt.type == pygame.KEYDOWN:
if evnt.key == pygame.K_ESCAPE:
running = False
# To see if a key is being held down, use `pygame.key.get_pressed()`.
pressed_keys = pygame.key.get_pressed()
# Move if up or down keys are pressed.
if pressed_keys[pygame.K_UP]:
captain_y -= 5
elif pressed_keys[pygame.K_DOWN]:
captain_y += 5
# Update the game.
# Reset the position if the circle is off screen.
if captain_y - captain_radius <= 0:
captain_y = 0 + captain_radius
elif captain_y + captain_radius >= HEIGHT:
captain_y = HEIGHT - captain_radius
# Draw everything.
screen.fill(BLACK)
pygame.draw.circle(screen, WHITE, (950, captain_y), captain_radius)
pygame.display.flip()
clock.tick(60) # Cap the frame rate at 60 fps.
pygame.quit()