Why is this simple pygame program lagging? - python-3.x

So, I've been studying programming for a short time and decided to make the snake game in pygame. However, while making the base of the program I realized that the rectangle (snake) controlled by the player is teleporting (maybe by the lag) every second while moving. Here is the code:
import pygame
pygame.init()
# Window
window = (1280, 720)
center = (window[0]//2, window[1]//2)
screen = pygame.display.set_mode(window)
pygame.display.set_caption("Snake")
# Colors
COLOR_LIGHT_GREY = (200, 200, 200)
COLOR_DARK_GREY = pygame.Color('gray12')
# Game loop
game_loop = True
game_clock = pygame.time.Clock()
# Create image
def img(name):
img_path = "./assets/natanael.lucena_" + name + ".png"
return pygame.image.load(img_path).convert_alpha()
# Set object coordinates
def set_obj_coordinates(obj, x, y):
obj.x = x
obj.y = y
# Check player key press
def check_player_key(b):
global snake_direction
if event.key == pygame.K_w or event.key == pygame.K_s or event.key == pygame.K_a or event.key == pygame.K_d:
snake_direction[event.key] = b
# Check key events in-game
def event_conditional():
global game_loop
if event.type == pygame.QUIT:
game_loop = False
elif event.type == pygame.KEYDOWN:
check_player_key(True)
elif event.type == pygame.KEYUP:
check_player_key(False)
# Check if the snake collided and the game is over
def game_over():
if snake.y < 0 or snake.y > 720 or snake.x < 0 or snake. x > 1280:
return True
# Snake
snake_img = img("snake")
snake = snake_img.get_rect()
move_keys = [pygame.K_w, pygame.K_d, pygame.K_s, pygame.K_a]
snake_direction = {k: False for k in move_keys}
snake_score = 0
snake_vel = 10
set_obj_coordinates(snake, center[0], center[1])
# Apple
apple_img = img("apple")
apple = apple_img.get_rect()
apple_eaten = False
set_obj_coordinates(apple, 40, 40)
# Main game loop
while game_loop:
for event in pygame.event.get():
event_conditional()
# score_text = text_render(snake_score)
if not game_over():
for i in range(4):
if i % 2:
coord_aux = "x "
else:
coord_aux = "y "
if i % 3:
op = "+= "
else:
op = "-= "
if snake_direction[move_keys[i]]:
exec("snake." + coord_aux + op + "snake_vel")
# the for loop above is equivalent to :
# if snake_direction[move_keys[0]]:
# snake.y -= snake_vel
# if snake_direction[move_keys[1]]:
# snake.x += snake_vel
# if snake_direction[move_keys[2]]:
# snake.y += snake_vel
# if snake_direction[move_keys[3]]:
# snake.x -= snake_vel
screen.fill(COLOR_DARK_GREY)
screen.blit(snake_img, snake)
screen.blit(apple_img, apple)
# Update screen
pygame.display.flip()
game_clock.tick(60)
pygame.quit()
If someone can tell me the reason for the problem, I really appreciate it.
Edit: looks like the problem is just happening with me

Thats a common problem with pygame, especially since pygame 2/sdl 2 where you can't use the directx video driver and enable vsync anymore.
You should do the following:
keep track of the sub-pixel coordinates of your moving game objects. A Rect can only store integer values in its x and y attributes, so you'll need another variable to store this. I usually use a Vector2 because it's easy to use and the performance hit usually does not matter anyway.
use an accurate clock. Pygame's clock object only uses milliseconds, which is not accurate enough for really smooth movement. If you're on windows, usually the best method for timing is the GetSystemTimePreciseAsFileTime function.
use a delta timing.
You could also use different threads for the parts of your game that needs different timing methods (e.g. your game logic expects fixed 30 or 60 FPS and your drawing code wants to run as fast as possible), but that's overkill for your small game.
So here's an example I hacked together that gives you smooth movement (at least that's what I use usually since it works fine for me. Note that it's windows specific):
import pygame
import ctypes.wintypes
pygame.init()
screen = pygame.display.set_mode((1280, 720))
center = screen.get_rect().center
pygame.display.set_caption("Snake")
game_loop = True
# https://stackoverflow.com/a/28574340/142637
def utcnow_microseconds():
system_time = ctypes.wintypes.FILETIME()
ctypes.windll.kernel32.GetSystemTimePreciseAsFileTime(ctypes.byref(system_time))
large = (system_time.dwHighDateTime << 32) + system_time.dwLowDateTime
return large // 10 - 11644473600000000
# Snake
snake_img = pygame.Surface((40, 40))
snake_img.fill('white')
snake = snake_img.get_rect()
snake_vel = 10
snake_pos = pygame.Vector2(center[0], center[1])
snake.topleft = snake_pos.x, snake_pos.y
# Apple
apple_img = pygame.Surface((40, 40))
apple_img.fill('red')
apple = apple_img.get_rect(topleft=(40, 40))
dt = 0
while game_loop:
t1 = utcnow_microseconds()
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_loop = False
keys = pygame.key.get_pressed()
snake_pos.x += (keys[pygame.K_d] - keys[pygame.K_a]) * snake_vel * dt
snake_pos.y += (keys[pygame.K_s] - keys[pygame.K_w]) * snake_vel * dt
snake.topleft = snake_pos.x, snake_pos.y
screen.fill('darkgrey')
screen.blit(snake_img, snake)
screen.blit(apple_img, apple)
pygame.display.flip()
t2 = utcnow_microseconds()
dt = (t2 - t1) / 1000. / 1000. * 30
pygame.quit()
Further reading.

Very likely the bottleneck is the line
exec("snake." + coord_aux + op + "snake_vel")
exec has to parse and interpret the text in the argument.
This code can be easily improved
if not game_over():
for i in range(4):
if snake_direction[move_keys[i]]:
sign = 1 if i % 3 else -1
if i % 2:
snake.x += sign * snake_vel
else:
snake.y += sign * snake_vel
Since snake is a pygame.Rect object, you can even do the following:
if not game_over():
for i in range(4):
if snake_direction[move_keys[i]]:
sign = 1 if i % 3 else -1
snake[(i+1) % 2] += sign * snake_vel
However:
The keyboard events (see pygame.event module) occur only once when the state of a key changes. The KEYDOWN event occurs once every time a key is pressed. KEYUP occurs once every time a key is released. Use the keyboard events for a single action or a step-by-step movement.
pygame.key.get_pressed() returns a list with the state of each key. If a key is held down, the state for the key is True, otherwise False. Use pygame.key.get_pressed() to evaluate the current state of a button and get continuous movement.
Use pygame.key.get_pressed() for a smooth continuous movement:
# Main game loop
while game_loop:
for event in pygame.event.get():
event_conditional()
# score_text = text_render(snake_score)
if not game_over():
keys = pygame.key.get_pressed()
snake.x += (keys[pygame.K_d] - keys[pygame.K_a]) * snake_vel
snake.y += (keys[pygame.K_s] - keys[pygame.K_w]) * snake_vel
# [...]

Related

Can't add multiple enemies in pygame

I'm making a Space Invaders like game and I recently got an error saying that "i" is undefined, when I stored the enemy's variables into lists in order add more enemies to the game trough a "for i in range(num_of_enemies) loop. I'm using Python 3.8 and PyCharm. I said everything right now but I have to keep writing in order to be able to post this.
import pygame
import random
import math
# Initiate pygame
pygame.init()
# Display the game window
screen = pygame.display.set_mode((800, 600))
# Enemies
enemySprite = []
enemyX = []
enemyY = []
enemyX_change = []
enemyY_change = []
num_of_enemies = 6
for i in range(num_of_enemies):
enemySprite.append(pygame.image.load('enemy.png'))
enemyX.append(random.randint(0, 736))
enemyY.append(random.randint(50, 150))
enemyX_change.append(4)
enemyY_change.append(20)
def enemy(x, y, i):
screen.blit(enemySprite[i], (x, y))
def isCollision(enemyX, enemyY, bulletX, bulletY):
distance = math.sqrt((math.pow(enemyX - bulletX, 2)) + (math.pow(enemyY - bulletY, 2)))
if distance < 27:
return True
else:
return False
# Game Loop
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
for i in range(num_of_enemies):
# Enemies movement
enemyX[i] += enemyX_change[i]
# Prevents the enemies from going off the border
if enemyX[i] <= 0 or enemyX[i] >= 736:
enemyX_change[i] *= -1
enemyY[i] += enemyY_change[i]
# Collision
collision = isCollision(enemyX[i], enemyY[i], bulletX, bulletY)
if collision:
bulletY = 480
bullet_state = 'ready'
score += 1
print(score)
enemyX[i] = random.randint(0, 736)
enemyY[i] = random.randint(50, 150)
enemy(enemyX[i], enemyY[i], i)
pygame.display.update()
It's a matter of Indentation. You have to the enemy logic in the for loop, which iterates the enemies, rather than the main application loop.
Furthermore bulletX, bulletY are not defined. You have to define this variables before the main application loop.
Clear the display or draw the background image before you draw the enemies and draw the enemies in a separate loop.
Use pygame.time.Clock() to control the frames per second.
bulletX, bulletY = 0, 0
# Game Loop
clock = pygame.time.Clock()
running = True
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
for i in range(num_of_enemies):
# Enemies movement
enemyX[i] += enemyX_change[i]
# Prevents the enemies from going off the border
if enemyX[i] <= 0 or enemyX[i] >= 736:
enemyX_change[i] *= -1
enemyY[i] += enemyY_change[i]
# Collision
collision = isCollision(enemyX[i], enemyY[i], bulletX, bulletY)
if collision:
bulletY = 480
bullet_state = 'ready'
score += 1
print(score)
enemyX[i] = random.randint(0, 736)
enemyY[i] = random.randint(50, 150)
# draw background image
# [...]
# d raw enemies
for i in range(num_of_enemies):
enemy(enemyX[i], enemyY[i], i)
# update display
pygame.display.update()

Pacman - Collision Detection Problems (Pygame)

I am relatively new to Pygame and I'm attempting to create a Pacman game for a project. I'm having trouble with collision detection between Pacman and this block (imported image). Pacman goes right through the block which I don't want to happen; I want it so that Pacman can't pass through it. I've searched many websites and many forums and tried many different methods that I've seen other people have used but I can't seem to get my head around collision detection. Below is my current code.
def Pacman():
pygame.init()
# Creating screen
global screen
screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
screen = pygame.display.set_mode((0,0), pygame.RESIZABLE)
# Setting window caption
pygame.display.set_caption('Pacman')
# Loading two images
pacman_image = pygame.image.load("pacmanphoto.png").convert_alpha()
block_image = pygame.image.load("blockphoto.png").convert_alpha()
rect1 = pacman_image.get_rect()
rect2 = block_image.get_rect()
rect1.x = 100
rect1.y = 200
rect2.x = 300
rect2.y = 400
clock = pygame.time.Clock()
x = 10
y = 10
pygame.key.set_repeat(10, 10)
# Movement
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
y -= 10
if event.key == pygame.K_DOWN:
y += 10
if event.key == pygame.K_RIGHT:
x += 10
if event.key == pygame.K_LEFT:
x -= 10
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if rect1.colliderect(rect2):
y -=10
##### where collision detection should be ####
# Displaying images on screen
screen.blit(pacman_image, (x,y))
screen.blit(block_image, (50,50))
clock.tick(12)
pygame.display.update()
screen.fill(BLACK)
If anyone can help me in any way please let me know :)
Thanks
First of all, your code is fairly unorganized. I recommend using classes so that the Pacman function looks a lot clearer, readable and everything isn't squeezed inside only one function.
If you don't want the Pacman to go through the blocks, you should know what you're up to. You don't want the Pacman to pass through the blocks, which basically means that if the Pacman's y(y-axis value) is between the block's y-axis and the same for the x-axis, then collision occurred, from here on you can just set the pacman_x and pacman's y to the block's x and y.
# Inside The Pacman Function
pac_x = 10
pac_y = 10
block_x = 50
block_y = 50
# getting the block's height and width
block_width, block_height = block_image.get_size()
##### where collision detection should be ####
if block_x + block_width > pac_x > block_x:
if block_y + block_height > pac_y > block_y:
print('Collision occurred.')
pac_x = block_x
pac_y = block_y
# Here you can add more functionality to your game
# Displaying images on screen
screen.blit(pacman_image, (pac_x,pac_y))
screen.blit(block_image, (block_x, block_y))

Is there any way to make the pygame window follow the mouse? [duplicate]

I want to create a pygame window that doesn't have a frame and that moves when the user clicks on it and moves the mouse.
I tried this script but when I click on the windows, '0' is printed but not '1'
Something is wrong in my script.
# coding : utf-8
import pygame
from pygame.locals import *
from random import randint
from os import environ
from math import sqrt
pygame.init()
max_fps = 250
clock = pygame.time.Clock()
window_size_x, window_size_x = 720, 360
infos = pygame.display.Info()
environ['SDL_VIDEO_WINDOW_POS'] = str(int(infos.current_w / 2)) + ',' + str(int(infos.current_h / 2)) # center the window
screen = pygame.display.set_mode((window_size_x, window_size_x), pygame.NOFRAME)
def move_window(): # move the windows when custom bar is hold
window_x, window_y = eval(environ['SDL_VIDEO_WINDOW_POS'])
mouse_x, mouse_y = pygame.mouse.get_pos()
dist_x , dist_y = mouse_x - window_x, mouse_y - window_y # calculate the distance between mouse and window origin
for event in pygame.event.get():
if event.type != MOUSEBUTTONUP: # while bar is hold
print('1')
mouse_x, mouse_y = pygame.mouse.get_pos()
environ['SDL_VIDEO_WINDOW_POS'] = str(mouse_x - dist_x) + ',' + str(mouse_x - dist_x)
screen = pygame.display.set_mode((window_size_x, window_size_x), pygame.NOFRAME) # rebuild window
def main():
run = True
while run :
screen.fill((255, 255, 255))
pygame.display.update()
clock.tick(60) # build frame with 60 frame per second limitation
for event in pygame.event.get():
if event.type == MOUSEBUTTONDOWN:
print('0')
move_window()
if __name__ == '__main__':
main()
Write a function, which moves the window from dependent on a previous mouse position (start_x, start_y) and a mouse position (new_x, new_y)
def move_window(start_x, start_y, new_x, new_y):
global window_size_x, window_size_y
window_x, window_y = eval(environ['SDL_VIDEO_WINDOW_POS'])
dist_x, dist_y = new_x - start_x, new_y - start_y
environ['SDL_VIDEO_WINDOW_POS'] = str(window_x + dist_x) + ',' + str(window_y + dist_y)
# Windows HACK
window_size_x += 1 if window_size_x % 2 == 0 else -1
screen = pygame.display.set_mode((window_size_x, window_size_y), pygame.NOFRAME)
In this function is a very important line:
window_size_x += 1 if window_size_x % 2 == 0 else -1
this line changes the width of the window from alternately by +1 and -1. On Windows systems there seems to be a bug, which ignores the new position parameter, if the size of the window didn't change.
This "hack" is a workaround, which slightly change the size of the window whenever the position is changed.
A different approach, with no flickering, may look as follows. Note, though, that this version is significantly slower:
def move_window(start_x, start_y, new_x, new_y):
global window_size_x, window_size_y
buffer_screen = pygame.Surface((window_size_x, window_size_y))
buffer_screen.blit(pygame.display.get_surface(), pygame.display.get_surface().get_rect())
window_x, window_y = eval(environ['SDL_VIDEO_WINDOW_POS'])
dist_x, dist_y = new_x - start_x, new_y - start_y
environ['SDL_VIDEO_WINDOW_POS'] = str(window_x + dist_x) + ',' + str(window_y + dist_y)
window_size_x += 1 if window_size_x % 2 == 0 else -1
screen = pygame.display.set_mode((window_size_x, window_size_y), pygame.NOFRAME)
screen.blit(buffer_screen, buffer_screen.get_rect())
pygame.display.flip()
Change the position on MOUSEMOTION and MOUSEBUTTONUP:
def main():
run = True
pressed = False
start_pos = (0,0)
while run :
# [...]
for event in pygame.event.get():
if event.type == MOUSEBUTTONDOWN:
pressed = True
start_pos = pygame.mouse.get_pos()
elif event.type == MOUSEMOTION:
if pressed:
new_pos = pygame.mouse.get_pos()
move_window(*start_pos, *new_pos)
pygame.event.clear(pygame.MOUSEBUTTONUP)
elif event.type == MOUSEBUTTONUP:
pressed = False
new_pos = pygame.mouse.get_pos()
move_window(*start_pos, *new_pos)
Full example program:
# coding : utf-8
import pygame
from pygame.locals import *
from os import environ
pygame.init()
clock = pygame.time.Clock()
window_size_x, window_size_y = 720, 360
infos = pygame.display.Info()
environ['SDL_VIDEO_WINDOW_POS'] = str(int(infos.current_w/2)) + ',' + str(int(infos.current_h/2))
screen = pygame.display.set_mode((window_size_x, window_size_x), pygame.NOFRAME)
def move_window(start_x, start_y, new_x, new_y):
global window_size_x, window_size_y
window_x, window_y = eval(environ['SDL_VIDEO_WINDOW_POS'])
dist_x, dist_y = new_x - start_x, new_y - start_y
environ['SDL_VIDEO_WINDOW_POS'] = str(window_x + dist_x) + ',' + str(window_y + dist_y)
window_size_x += 1 if window_size_x % 2 == 0 else -1
screen = pygame.display.set_mode((window_size_x, window_size_y), pygame.NOFRAME)
def main():
run = True
pressed = False
start_pos = (0,0)
while run :
screen.fill((255, 255, 255))
pygame.display.update()
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
run = False
if event.type == MOUSEBUTTONDOWN:
pressed = True
start_pos = pygame.mouse.get_pos()
elif event.type == MOUSEMOTION:
if pressed:
new_pos = pygame.mouse.get_pos()
move_window(*start_pos, *new_pos)
pygame.event.clear(pygame.MOUSEBUTTONUP)
elif event.type == MOUSEBUTTONUP:
pressed = False
new_pos = pygame.mouse.get_pos()
move_window(*start_pos, *new_pos)
if __name__ == '__main__':
main()
This solution no longer works under Windows systems and with Pygame 2.0. The position of a window can, however, be changed with the WINAPI function MoveWindow:
import pygame
from ctypes import windll
pygame.init()
screen = pygame.display.set_mode((400, 400), pygame.NOFRAME)
clock = pygame.time.Clock()
def moveWin(new_x, new_y):
hwnd = pygame.display.get_wm_info()['window']
w, h = pygame.display.get_surface().get_size()
windll.user32.MoveWindow(hwnd, new_x, new_y, w, h, False)
window_pos = [100, 100]
moveWin(*window_pos)
run = True
while run :
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
run = False
elif event.type == pygame.MOUSEMOTION:
if pygame.mouse.get_pressed()[0]:
window_pos[0] += event.rel[0]
window_pos[1] += event.rel[1]
moveWin(*window_pos)
screen.fill((255, 255, 255))
pygame.display.update()
clock.tick(60)
This code use only one for event loop with MOUSEBUTTONDOWN to set moving = True, MOUSEBUTTONUP to set moving = False and MOUSEMOTION which changes window's position when moving is True.
After move I use pygame.event.clear(pygame.MOUSEBUTTONUP) to remove this type of events because new window was getting this even and it was stoping window.
import pygame
from os import environ
# --- constants --- (UPPER_CASE_NAMES)
WINDOW_WIDTH = 720
WINDOW_HEIGHT = 360
# --- main ---
def main():
pygame.init()
infos = pygame.display.Info()
environ['SDL_VIDEO_WINDOW_POS'] = '{},{}'.format(infos.current_w//2, infos.current_h//2) # center the window
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT), pygame.NOFRAME)
moving = False
clock = pygame.time.Clock()
run = True
while run:
screen.fill((255, 255, 255))
pygame.display.update()
clock.tick(60) # build frame with 60 frame per second limitation
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if not moving:
print('MOUSEBUTTONDOWN')
moving = True
# remeber start distance
#window_x, window_y = eval(environ['SDL_VIDEO_WINDOW_POS'])
window_x, window_y = map(int, environ['SDL_VIDEO_WINDOW_POS'].split(','))
dist_x = event.pos[0] # mouse x
dist_y = event.pos[1] # mouse y
elif event.type == pygame.MOUSEBUTTONUP:
if moving:
print('MOUSEBUTTONUP')
moving = False
elif event.type == pygame.MOUSEMOTION:
if moving:
print('moving')
mouse_x, mouse_y = pygame.mouse.get_pos()
diff_x = dist_x - mouse_x
diff_y = dist_y - mouse_y
window_x -= diff_x
window_y -= diff_y
environ['SDL_VIDEO_WINDOW_POS'] = "{},{}".format(window_x, window_y)
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT), pygame.NOFRAME) # rebuild window
pygame.event.clear(pygame.MOUSEBUTTONUP) # to remove MOUSEBUTTONUP event which stops moving window
if __name__ == '__main__':
main()
The code's not ready, but I was getting there.
I had to abandon it, but it might help someone save some time.
Press f for full-screen mode. The part that is not done,
supposed to be the window-mode resize part. You must have an in-depth
look at toogle_resize() function for that.
The resize it's taking full desktop resolution and compare it
to the space between clik (MOUSEBUTTONDOWN) and (MOUSEBUTTONUP).
Or at least that's how I wanted it to work. Good luck!
Also the code needs to be optimized, it is raw.
import pyautogui
import pygame
import sys
from pygame.locals import *
from pyinput.mouse import Controller
Mouse controller gets the position on desktop, but I had to run it once more inside the while loop to get the updated values.
mouse = Controller()
standard = current_mouse_position = mouse.position
pygame.init()
Silkscreen = False
blueGray = (73, 111, 135)
width, height = pyautogui.size()
w = width / 4
h = height / 4
ww = width - w
hh = height - h
wi = ww - 4
hi = hh - 4
Set the background and the flags
room = pygame.image.load('img.png')
full_flags = pygame.FULLSCREEN | pygame.SCALED |
pygame.NOFRAME
normal_flags = pygame.NOFRAME | pygame.RESIZABLE |
pygame.SCALED
def toggle_fullscreen(f):
if f:
return pygame.display.set_mode((ww, hh), full_flags)
# pygame.display.set_mode(size, normal_flags) # uncomment
this to see issue being fixed as a workaround
return pygame.display.set_mode((ww, hh), normal_flags)
def toggle_resize(click):
if click:
return pygame.display.set_mode((ww + movement, hh),
normal_flags) # Expands by resize_y
# Set up the drawing window
screen = pygame.display.set_mode((ww, hh), normal_flags)
def bg():
screen.blit(room, (0, 0))
def border():
pygame.draw.rect(screen, blueGray, (0, 0, ww, hh), 2) #
width = 3
clock = pygame.time.Clock()
# Run until the user asks to quit
running = True
while running:
current_mouse_position = mouse.position
# print(current_mouse_position[0])
bg()
mw, mh = pygame.mouse.get_pos() # 0 - 1439(window size)
In the next line, I checked if the mouse is on the window border. Apply only to the left, right border. You must create code for the top, bottom border. Left, right borders, 3 pixels range each side.
if mw <= 3 or (mw > wi and mw < ww):
active = True
moveOne = 0
# print("data", moveOne)
# print(type(moveOne))
moveTwo = 0
# event handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
running = False
sys.exit()
# print(mw, mh)
If the user clicks, the standard variable at the beginning of the code it gets the mouse current position. You must run the active bool and check if it is True if you want to start resizing from the border of the window.
The code is very complicated and needs some optimization, but I bet you gonna get it right. Good luck!;)
if event.type == pygame.MOUSEBUTTONDOWN:
standard = current_mouse_position[0]
print(standard)
if event.type == pygame.MOUSEBUTTONUP:
moveTwo = current_mouse_position[0]
movement = standard - moveTwo
print("This is:", moveTwo)
toggle_resize(click=MOUSEBUTTONUP)
active = False
Full-screen handler from here down. Press f for full screen and back to window mode.
if event.type == pygame.KEYDOWN:
Silkscreen = not Silkscreen
if event.key == K_f:
screen = toggle_fullscreen(Silkscreen)
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
border()
# Flip the display
pygame.display.flip()
clock.tick(144) # framerate
# Done! Time to quit.
pygame.quit()'
Here is a version of #Rabbid76's answer for Pygame 2. Note that this example may break as the module _sdl2.video, used to set the window position, is experimental.
move_window.py
import pygame
from pygame._sdl2.video import Window
start_pos = pygame.Vector2(0, 0) #Initial mouse position
pressed = False #Flag that denotes when the mouse is being continuously pressed down
def move_window(window : Window, start_mouse_pos : pygame.Vector2, new_mouse_pos : pygame.Vector2) -> None:
"""Moves the window by the offset between start_mouse_pos and new_mouse_pos"""
screen = pygame.display.get_surface()
buffer_screen = pygame.Surface((window.size[0], window.size[1]))
buffer_screen.blit(screen, screen.get_rect())
window_pos_Vec2 = pygame.Vector2(window.position)
window.position = window_pos_Vec2 + new_mouse_pos - start_mouse_pos
screen.blit(buffer_screen, buffer_screen.get_rect())
pygame.display.flip()
def check_event(window : Window, event : pygame.event, move_area : pygame.Rect = pygame.Rect(-1, -1, 1, 1)) -> None:
"""Takes a window and event and updates the window position accordingly. \n
move_area can be used to set what area of the screen can be clicked in order to move the window. \n
move_area defaults to a dummy rect which is then internally changed to the full window."""
global start_pos, pressed
if move_area == pygame.Rect(-1, -1, 1, 1):
move_area = pygame.Rect((0, 0), window.size)
mouse_pos = pygame.Vector2(pygame.mouse.get_pos())
if move_area.collidepoint(mouse_pos):
if event.type == pygame.MOUSEBUTTONDOWN:
pressed = True
start_pos = mouse_pos
elif event.type == pygame.MOUSEMOTION and pressed:
move_window(window, start_pos, mouse_pos)
elif event.type == pygame.MOUSEBUTTONUP:
pressed = False
move_window(window, start_pos, mouse_pos)
else:
pressed = False
And in your main file:
import pygame
from pygame._sdl2.video import Window
screen = pygame.display.set_mode(...)
window = Window.from_display_module()
#...
while True:
for event in pygame.event.get():
#...
move_window.check_event(window, event)

How to generate the food for a snake game

I can not figure out how to generate the food for the snake to eat. I know the position of the snake at line 97 and 98, I have created a class to generate a pixel where I want to draw a peace of food at line 22 (EDIT: should probably be a function, commented #def (?) in the code). All I have to do is add 15 pixels at the x and y coordinates from the position that is randomly allocated and print it to get a block.
The problem is to check if I eat it or not. It should be something like:
if x >= x_food && x <= x_food + 15 || y >= y_food && y <= y_food + 15:
...add a point and make snake longer...
The problem is putting it all together for some reason.. Can some one give me a hint or solve how I should write this class so I can continue with other problems? Thank you!
import pygame
import random
#Global variables
#Color
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
#Start length of snake
snake_length = 3
#Set the width of the segments of the snake
segment_width = 15
segment_height = 15
# Margin within each segment
segment_margin = 3
#Set initial speed
x_change = segment_width + segment_margin
y_change = 0
#def (?)
class Food():
#Class to print food
x_food = random.randint(0, 785)
y_food = random.randint(0, 585)
class Segment(pygame.sprite.Sprite):
""" Class to represent the segment of the snake. """
# Methods
# Constructer function
def __init__(self, x, y):
#Call the parents constructor
super().__init__()
#Set height, width
self.image = pygame.Surface([segment_width, segment_height])
self.image.fill(WHITE)
#Make our top-left corner the passed-in location.
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
#Call this function so the Pygame library can initialize itself
pygame.init()
#Create an 800x600 size screen
screen = pygame.display.set_mode([800, 600])
#Set the title of the window
pygame.display.set_caption("Snake")
allspriteslist = pygame.sprite.Group()
#Create an initial snake
snake_segments = []
for i in range(snake_length):
x = 250 - (segment_width + segment_margin) * i
y = 30
segment = Segment(x, y)
snake_segments.append(segment)
allspriteslist.add(segment)
clock = pygame.time.Clock()
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
#Set the speed based on the key pressed
#We want the speed to be enough that we move a full
#Segment, plus the margin
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = (segment_width + segment_margin) * -1
y_change = 0
if event.key == pygame.K_RIGHT:
x_change = (segment_width + segment_margin)
y_change = 0
if event.key == pygame.K_UP:
x_change = 0
y_change = (segment_height + segment_margin) * -1
if event.key == pygame.K_DOWN:
x_change = 0
y_change = (segment_width + segment_margin)
#Get rid of last segment of the snake
#.pop() command removes last item in list
old_segment = snake_segments.pop()
allspriteslist.remove(old_segment)
#Figure out where new segment will be
x = snake_segments[0].rect.x + x_change
y = snake_segments[0].rect.y + y_change
segment = Segment(x, y)
#Insert new segment to the list
snake_segments.insert(0, segment)
allspriteslist.add(segment)
#Draw
#Clear screen
screen.fill(BLACK)
allspriteslist.draw(screen)
#Flip screen
pygame.display.flip()
#Pause
clock.tick(5)
pygame.quit()
i took your code and i think i work something out, pls note that this only monitors when the snake goes over the block, then it prints: yummy, so you will have to add the detail, also note that i dont use your class to generate the food:
import pygame
import random
#Global variables
#Color
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
#Start length of snake
snake_length = 3
#Set the width of the segments of the snake
segment_width = 15
segment_height = 15
# Margin within each segment
segment_margin = 3
#Set initial speed
x_change = segment_width + segment_margin
y_change = 0
#def (?)
class Food():
#Class to print food
x_food = random.randint(0, 785)
y_food = random.randint(0, 585)
class Segment(pygame.sprite.Sprite):
""" Class to represent the segment of the snake. """
# Methods
# Constructer function
def __init__(self, x, y):
#Call the parents constructor
super().__init__()
#Set height, width
self.image = pygame.Surface([segment_width, segment_height])
self.image.fill(WHITE)
#Make our top-left corner the passed-in location.
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
#Call this function so the Pygame library can initialize itself
pygame.init()
#Create an 800x600 size screen
screen = pygame.display.set_mode([800, 600])
#Set the title of the window
pygame.display.set_caption("Snake")
allspriteslist = pygame.sprite.Group()
#Create an initial snake
snake_segments = []
for i in range(snake_length):
x = 250 - (segment_width + segment_margin) * i
y = 30
segment = Segment(x, y)
snake_segments.append(segment)
allspriteslist.add(segment)
clock = pygame.time.Clock()
done = False
x_food = random.randint(0, 785)
y_food = random.randint(0, 585)
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
#Set the speed based on the key pressed
#We want the speed to be enough that we move a full
#Segment, plus the margin
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = (segment_width + segment_margin) * -1
y_change = 0
if event.key == pygame.K_RIGHT:
x_change = (segment_width + segment_margin)
y_change = 0
if event.key == pygame.K_UP:
x_change = 0
y_change = (segment_height + segment_margin) * -1
if event.key == pygame.K_DOWN:
x_change = 0
y_change = (segment_width + segment_margin)
if y < y_food+30:
if x > x_food and x < x_food+30 or x+20 > x_food and x+20<x_food+30:
print('yummy')
#Get rid of last segment of the snake
#.pop() command removes last item in list
old_segment = snake_segments.pop()
allspriteslist.remove(old_segment)
#Figure out where new segment will be
x = snake_segments[0].rect.x + x_change
y = snake_segments[0].rect.y + y_change
segment = Segment(x, y)
#Insert new segment to the list
snake_segments.insert(0, segment)
allspriteslist.add(segment)
#Draw
#Clear screen
screen.fill(BLACK)
pygame.draw.rect(screen, WHITE, [x_food, y_food, 30, 30])
allspriteslist.draw(screen)
#Flip screen
pygame.display.flip()
#Pause
clock.tick(5)
pygame.quit()
hope this helped, thanks!
if x >= x_food && x <= x_food + 15 || y >= y_food && y <= y_food + 15:
Why do you OR these pairs of conditions? Don't all 4 tests have to be true at the same time?
if x >= x_food && x <= x_food + 15 && y >= y_food && y <= y_food + 15:
how about using:
if snake_headx == foodx and snake_heady == foody:
food_eated()
snake_grow()
just a suggestion though
didnt read all ur code, just thought u might find it usefull.
actuely ive came to a solution, so basicly what you want is a square, and when the snake comes near that square something should happen?, well ive got a racegame that makes you crash when you hit a square car, so il just copy the code here:
if y < thing_starty+thing_height:
if x > thing_startx and x < thing_startx+thing_width or x+car_width > thing_startx and x+car_width<thing_startx+thing_width:
snake_eated()
snake_grow()
this monitors the x and y of your car (or snake) and checks when the thing (or food) 's y is smaller than your car, then it checks the x's and alot of other things, and basicly it creates a big line all around your square that you cannot cross in your case you'd just need to add the rest, would this work?
Make your food a sprite with a simple filled rectangle as the image, and then use sprite collision pygame.sprite.spritecollide() to check if your snake collides with your food. Pygame will take care of the actual logic whether two rectangles overlap for you.
Also, since you are already using sprite groups, I suggest you write an update function for your snake segments which moves them instead of creating a new segment every turn. Then you can simply call allspriteslist.update() in your main game loop, which will call the update function for every snake segment.
Finally, you might want to have a look at the numerous snake examples on the pygame website.

Advice on my pong code

I am doing a clone of pong. It is just a barebone version of pong. And It would be nice if you guys could help me refine it. I am mainly doing it as a learning exercise, So i have just kept it to a basic functions.
import time
import pygame
done = False
pygame.init()
myfont = pygame.font.SysFont("monospace", 15)
screen_size = [320,240]
white = [255,255,255]
black = [0,0,0]
gutter = 10
score_1 = 0
score_2 = 0
ball_pos = [160,120]
ball_vel = [1,1]
paddle_1 = [0,0]
paddle_2 = [screen_size[0]-5,0]
vel_1 = [0,0]
vel_2 = [0,0]
P1 = False
P2 = False
screen = pygame.display.set_mode(screen_size)
pygame.display.set_caption("mygame")
while not done:
time.sleep(0.02)
screen.fill(black)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
if pygame.key.get_pressed()[pygame.K_DOWN]:
vel_2[1] += 2
P1 = True
if pygame.key.get_pressed()[pygame.K_UP]:
P1 = True
vel_2[1] -= 2
if pygame.key.get_pressed()[pygame.K_s]:
P2 = True
vel_1[1] += 2
if pygame.key.get_pressed()[pygame.K_w]:
P2 = True
vel_1[1] -= 2
if event.type == pygame.KEYUP:
if(P1):
vel_2 = [0,0]
P1 = False
if (P2):
vel_1 = [0,0]
P2 = False
if ball_pos[1] in range(paddle_1[1],paddle_1[1]+30) and ball_pos[0] in range(0,gutter+10) :
ball_vel[0] *= -1
if ball_pos[1] in range(paddle_2[1],paddle_2[1]+30) and ball_pos[0] in range(310,320):
ball_vel[0] *= -1
if ball_pos[0] > 320:
score_1 += 1
ball_pos = [160,120]
if ball_pos[0] < 5:
score_2 += 1
ball_pos = [160,120]
if ball_pos[1] > 230 or ball_pos[1] < 5:
ball_vel[1] *= -1
paddle_1[0] += vel_1[0]
paddle_1[1] += vel_1[1]
paddle_2[0] += vel_2[0]
paddle_2[1] += vel_2[1]
ball_pos[0] += ball_vel[0]
ball_pos[1] += ball_vel[1]
## pygame.draw.line(screen,white,(gutter,0),(gutter,screen_size[1]))
## pygame.draw.line(screen,white,(screen_size[0]-gutter,0),(screen_size[0]-gutter,screen_size[1]))
pygame.draw.line(screen,white,(screen_size[0]/2,0),(screen_size[0]/2,screen_size[1]),1)
pygame.draw.circle(screen,white,ball_pos,10,0)
pygame.draw.line(screen,white,[paddle_1[0]+5,paddle_1[1]],[paddle_1[0]+5,paddle_1[1]+50],gutter)
pygame.draw.line(screen,white,paddle_2,[paddle_2[0],paddle_2[1]+50],gutter)
label_1 = myfont.render(str(score_1), 1, (255,255,0))
label_2 = myfont.render(str(score_2), 1, (255,255,0))
screen.blit(label_1, (100, 100))
screen.blit(label_2, (220, 100))
pygame.display.flip()
pygame.quit()
There are a few things that you should improve in your code:
1) Constants vs Variables
When you define
white = [255,255,255]
black = [0,0,0]
you should instead do:
WHITE = (255,255,255)
BLACK = ( 0, 0, 0)
Constants should usually be created as tuples so that you do not mistakenly change their values. Also, since you only ever read a constant and never change its value a tuple is more appropriate. It also runs slightly faster
2) Creating Classes
Whenever you make a game, it is recommended that you create the objects as classes. In this case, you would have classes such as Player and Ball. In these classes you can define functions such as update and draw. This will allow you to easily draw an object or update its location, speed etc by a single line of code. Eg:
player.update()
player.draw()
ball.draw()
3) Time vs Timer
Instead of using import time, I recommend that you use the pygame built-in Timer option for controlling the FPS of the game. Here's an example:
timer = pygame.time.Clock()
then at the start of your while loop, you can simply do:
timer.tick(60) #if you want 60 to be the number of frames per second
I know it is a lot to take in but in the end the effort will be more than worth it and it will make future game making with pygame much cleaner.
GREAT RESOURCE BOOK
HAPPY PYGAMING!
Try to encapsulate the ball and the paddles in proper Ball and Paddle classes. This way, you make your code cleaner and more reusable, since both paddles will share the same code.

Resources