PyGame Snake decreases speed as window size increases - python

I've been struggling with this problem for a couple days now. I want to eventually convert my snake game script to an exe, for my friends to play. I want the snake to move at the same speed, no matter the size of the window.
For Example: My window size right now is (400, 400). If I increase the size to (800, 800), the snake will move slower. However, the snake velocity is constant at 20 pixels. It almost seems like my main game loop is looping at a slower pace as the window size increases.
I know that the snake has more pixels to traverse with a bigger window size, but how does that affect the snake velocity at all? I'm thinking the solution lies somewhere in the speed at which I'm drawing the snake to the screen, but can't be sure.
import pygame
import sys
import random
import math
import time
pygame.display.set_caption('Snake')
pygame.font.init()
game_running = True
width = 400
height = 400
size = (width, height)
window = pygame.display.set_mode(size) # our surface type
pygame.display.set_caption("Snake Game by Nick Rinaldi")
class Food:
def __init__(self, block_size, surface, x_loc, y_loc): # pass in color and random_x/random_y. block size is a constant
self.block_size = block_size
self.surface = surface # green
self.x_loc = x_loc
self.y_loc = y_loc
self.mask = pygame.mask.from_surface(self.surface)
def draw(self, window):
window.blit(self.surface, (self.x_loc, self.y_loc))
class Snake:
def __init__(self, block_size, surface, x_loc, y_loc):
self.block_size = block_size
self.surface = surface # red
self.x_loc = x_loc
self.y_loc = y_loc
self.body = []
self.direction = None
self.velocity = 20
self.mask = pygame.mask.from_surface(self.surface)
def draw(self, color, window, block_size):
self.seg = []
self.head = pygame.Rect(self.x_loc, self.y_loc, block_size, block_size)
pygame.draw.rect(window, color, self.head)
if len(self.body) > 0:
for unit in self.body:
segment = pygame.Rect(unit[0], unit[1], block_size, block_size)
pygame.draw.rect(window, color, segment)
self.seg.append(segment)
def add_unit(self):
if len(self.body) != 0:
index = len(self.body) - 1
x = self.body[index][0]
y = self.body[index][1]
self.body.append([x, y])
else:
self.body.append([1000, 1000])
def move(self, step):
for index in range(len(self.body) -1, 0, -1):
x = self.body[index-1][0]
y = self.body[index-1][1]
self.body[index] = [x, y]
if len(self.body) > 0:
self.body[0] = [self.x_loc, self.y_loc]
if self.direction == "right": # if specific constant, keep moving in direction
self.x_loc += self.velocity * step
if self.direction == "left":
self.x_loc -= self.velocity * step
if self.direction == "down":
self.y_loc += self.velocity * step
if self.direction == "up":
self.y_loc -= self.velocity * step
def collision(self, obj):
return collide(food)
def gameOver(snake):
white = pygame.Color(255, 255, 255)
display = True
while display:
window.fill(white)
score_font = pygame.font.SysFont("Courier New", 16)
score_label = score_font.render("Your score was: " + str(len(snake.body) + 1), 1, (0, 0, 0))
replay_label = score_font.render("To replay, click the mouse button", 1, (0, 0, 0))
window.blit(score_label, (50, 100))
window.blit(replay_label, (50, 130))
pygame.display.update()
for event in pygame.event.get(): # if we hit "x" to close out the game, close out the game.
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == pygame.MOUSEBUTTONDOWN:
main()
pygame.quit()
sys.exit()
clock = pygame.time.Clock()
def main():
game_over = False
x = 20 # x position
y = 20 # y position
block_snakes = []
pygame.init()
clock = pygame.time.Clock()
red = pygame.Color(255, 0, 0)
green = pygame.Color(0, 255, 0)
white = pygame.Color(255, 255, 255)
black = pygame.Color(0, 0, 0)
block_size = 20
randx_green = random.randrange(0, width, 20)
randy_green = random.randrange(0, height, 20)
randx_red = random.randrange(0, width, 20)
randy_red = random.randrange(0, height, 20)
red_square = pygame.Surface((block_size, block_size))
red_square.fill(red)
green_square = pygame.Surface((block_size, block_size))
green_square.fill(green)
snake = Snake(block_size, red_square, 20, 20) # create snake instance
food = Food(block_size, green_square, randx_green, randy_green) # create food instance
def redraw_window():
draw_grid(window, height, width, white)
while game_running:
dt = clock.tick(30) # time passed between each call
step = dt/1000
print(step)
FPS = 60
window.fill(black)
food.draw(window)
snake.draw(red, window, block_size)
redraw_window()
for event in pygame.event.get(): # if we hit "x" to close out the game, close out the game.
if event.type == pygame.QUIT:
pygame.quit()
exit()
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]: # sets direction attribute as a constant
snake.direction = "right"
if keys[pygame.K_LEFT]:
snake.direction = "left"
if keys[pygame.K_DOWN]:
snake.direction = "down"
if keys[pygame.K_UP]:
snake.direction = "up"
snake.move(step)
collision = collide_food(snake.x_loc, snake.y_loc, food.x_loc, food.y_loc)
if collision:
ac_rand_x = random.randrange(0, width, 20) # after collision, random x
ac_rand_y = random.randrange(0, height, 20) # after collision, random y
# check snake.direction.
food = Food(block_size, green_square, ac_rand_x, ac_rand_y)
food.draw(window)
snake.add_unit()
wall_collide = collide_wall(snake.x_loc, snake.y_loc)
if wall_collide:
gameOver(snake)
# break
for block in snake.body:
if snake.x_loc == block[0] and snake.y_loc == block[1]:
gameOver(snake)
pygame.display.update()
# clock.tick(FPS)
def collide_food(snake_x, snake_y, obj_x, obj_y):
distance = math.sqrt((math.pow(snake_x - obj_x, 2)) + (math.pow(snake_y - obj_y, 2)))
if distance < 20:
return True
else:
return False
def collide_wall(snake_x, snake_y):
if snake_x > width:
game_over = True
return game_over
if snake_y > height:
game_over = True
return game_over
if snake_x < 0:
game_over = True
return game_over
if snake_y < 0:
game_over = True
return game_over
def collide_self(snake_x, snake_y, body_x, body_y):
if (snake_x and snake_y) == (body_x and body_y):
return True
else:
return False
def draw_grid(window, height, width, color):
x = 0
y = 0
grid_blocks = 20
for i in range(height):
x += 20
pygame.draw.line(window, color, (x, 0), (x, height), 1)
for j in range(width):
y += 20
pygame.draw.line(window, color, (0, y), (height, y), 1)
# pygame.display.update()
def display_score():
score_font = pygame.font.SysFont()
def main_menu(width, height):
clock = pygame.time.Clock()
FPS = 60
width = width
height = height
run = True
title_font = pygame.font.SysFont("Courier New", 16)
title_font.set_bold(True)
white = pygame.Color(255, 255, 255)
while run:
window.fill(white)
title_label = title_font.render("Snake Game by Nick Rinaldi ", 1, (0, 0, 0))
sponser_label = title_font.render("Sponsored by #goodproblemsnyc", 1, (0, 0, 0))
window.blit(title_label, ((width/4, height/4)))
window.blit(sponser_label, ((width/4, height/4 + 30)))
pygame.display.update()
for event in pygame.event.get(): # if we hit "x" to close out the game, close out the game.
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == pygame.MOUSEBUTTONDOWN:
main()
pygame.quit()
main_menu(width, height)```

The bottleneck in your game is the function draw_grid, which draws far too many lines out of the window.
def draw_grid(window, height, width, color):
x = 0
y = 0
grid_blocks = 20
for i in range(height):
x += 20
pygame.draw.line(window, color, (x, 0), (x, height), 1)
for j in range(width):
y += 20
pygame.draw.line(window, color, (0, y), (height, y), 1)
If you draw a line outside the window, the statement does not draw anything, nevertheless the nested for loops still run.
Furthermore, you don't need a nested loops. You don't want to draw 19 horizontal lines for each vertical line. You want to draw 19 vertical and 19 horizontal lines. Hence 1 for-loop is enough.
Use the step argument of range to define the list of positions for the vertical and horizontal lines
def draw_grid(window, height, width, color):
tile_size = 20
for p in range(tile_size, height, tile_size):
pygame.draw.line(window, color, (p, 0), (p, height), 1)
pygame.draw.line(window, color, (0, p), (height, p), 1)

if the size is 400 * 400 pixels, the total pixels 160000 so moving at a 20 pixel rate, because there are less pixels the an 800 * 800 board (320000 pixels) it looks like you are going faster because there are less pixels. Find an equation to calculate the correct speed on differently sized boards.
Sincerely,
Zac

Related

How do my make my image (rect) collide with my randomly generated circles to end the game? [duplicate]

This question already has answers here:
How do I detect collision in pygame?
(5 answers)
Closed 1 year ago.
I have been trying to solve this for weeks. This is a free-falling pit game, where my character (in this case a chimpanzee png) falls from the top of the screen to the bottom while trying to dodge the random black circles. I have tried so many angles at tackling this, I have tried the standard collision I was taught (pygame.sprite.groupcollide(Group1, Group2, False, True, collided = None) I have tried doing collisions with the colour black only, different formats of spawning my image and all that, and I haven't been able to find anything that works with my code. It has resulted in the code being very messy. I have tried to clean it up for this, but it still might be hard to understand, but if anybody has any solution to this, please let me know as I have been stumped for weeks. I just want the game to close or "game over" when they touch a circle Code:
import sys
import pygame as pg
RED = (255, 0, 0)
GRAY = (150, 150, 150)
GREEN =(34, 177, 76)
BLACK = (0,0,0)
import random
import math
def main():
width, height = 1024, 768
hbox, vbox = 80, 80
w, h = 640, 240
screen = pg.display.set_mode((width, height))
BG = pg.image.load('jungle.jpg').convert()
img = pg.image.load('MONKEY.png')
img = pg.transform.scale(img, (80, 80))
img.convert()
rect = img.get_rect()
rect.center = w//2, h//2
clock = pg.time.Clock()
Score = 0
class Circle(pg.sprite.Sprite):
def __init__(self):
#You can initialise the size to a random value
self.pos = [random.randint(0, 1000), random.randint(180, 600)]
self.color = (0,0, 0)
self.radius = 20
def draw(self):
pg.draw.circle(screen, self.color, (self.pos[0], self.pos[1]), self.radius)
circles = []
for i in range(20):
circles.append(Circle())
def checkIntersection(c1, c2):
dx = c1.pos[0] - c2.pos[0]
dy = c1.pos[1] - c2.pos[1]
d = math.hypot(dx, dy)
if d < c1.radius + c2.radius:
return True
return False
for i in range(19):
while checkIntersection(circles[i], circles[i + 1]):
circles[i].pos = random.randint(0, 1000), random.randint(0, 700)
velocity = (0, 0)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
keys = pg.key.get_pressed()
# booster
move = 8 if keys[pg.K_LSHIFT] else 4
if keys[pg.K_a]: #to move left
rect.x -= move
if rect.x < 0 : rect.x = 0
if keys[pg.K_d]: #to move right
rect.x += move
if rect.x > width-hbox : rect.x = width - hbox
Score += 1
rect.y += 2.5
screen.blit(BG, (0,0))
screen.blit(img,rect)
pg.draw.rect(screen, RED, rect, 1)
pg.draw.polygon(screen, GREEN, ((1024,768), (0,768), (0,640),(1024,640)))
font = pg.font.SysFont("comicsansms", 45)
text = font.render (" " + str(Score), 1, BLACK)
screen.blit(text,(480,700))
pg.event.get()
for circle in circles:
circle.draw()
pg.display.update()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()
Use a "mask" collision. See How can I made a collision mask? and Pygame mask collision
Create a Sprite class with mask (see pygame.mask.from_surface):
class Circle(pg.sprite.Sprite):
def __init__(self):
super().__init__()
self.pos = [random.randint(0, 1000), random.randint(180, 600)]
self.color = (0,0, 0)
self.radius = 20
self.image = pg.Surface((self.radius*2, self.radius*2), pg.SRCALPHA)
pg.draw.circle(self.image , self.color, (self.radius, self.radius), self.radius)
self.rect = self.image.get_rect(center = self.pos)
self.mask = pg.mask.from_surface(self.image)
Create a Sprite class for the player (also with a mask)
class Player(pg.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pg.image.load('MONKEY.png')
self.image = pg.transform.scale(self.image, (80, 80)).convert()
self.rect = self.image.get_rect()
self.rect.center = x, y
self.mask = pg.mask.from_surface(self.image)
Manage the Sprites in pygame.sprite.Group and use pygame.sprite.spritecollide and pygame.sprite.collide_mask() for the collision test:
if pg.sprite.spritecollide(player, circles, False, collided = pg.sprite.collide_mask):
done = True
Complete example:
import sys
import pygame as pg
RED = (255, 0, 0)
GRAY = (150, 150, 150)
GREEN =(34, 177, 76)
BLACK = (0,0,0)
import random
import math
class Circle(pg.sprite.Sprite):
def __init__(self):
super().__init__()
self.pos = [random.randint(0, 1000), random.randint(180, 600)]
self.color = (0,0, 0)
self.radius = 20
self.image = pg.Surface((self.radius*2, self.radius*2), pg.SRCALPHA)
pg.draw.circle(self.image , self.color, (self.radius, self.radius), self.radius)
self.rect = self.image.get_rect(center = self.pos)
self.mask = pg.mask.from_surface(self.image)
class Player(pg.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pg.image.load('MONKEY.png')
self.image = pg.transform.scale(self.image, (80, 80)).convert()
self.rect = self.image.get_rect()
self.rect.center = x, y
self.mask = pg.mask.from_surface(self.image)
def main():
width, height = 1024, 768
hbox, vbox = 80, 80
w, h = 640, 240
screen = pg.display.set_mode((width, height))
BG = pg.image.load('jungle.jpg').convert()
clock = pg.time.Clock()
Score = 0
player = Player(w//2, h//2)
all_sprites = pg.sprite.Group(player)
circles = pg.sprite.Group()
for i in range(20):
circle = Circle()
circles.add(circle)
all_sprites.add(circle)
def checkIntersection(c1, c2):
dx = c1.pos[0] - c2.pos[0]
dy = c1.pos[1] - c2.pos[1]
d = math.hypot(dx, dy)
if d < c1.radius + c2.radius:
return True
return False
c = circles.sprites()
for i in range(19):
while checkIntersection(c[i], c[i + 1]):
c[i].pos = random.randint(0, 1000), random.randint(0, 700)
velocity = (0, 0)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
keys = pg.key.get_pressed()
# booster
move = 8 if keys[pg.K_LSHIFT] else 4
if keys[pg.K_a]: #to move left
player.rect.x -= move
if player.rect.x < 0 : player.rect.x = 0
if keys[pg.K_d]: #to move right
player.rect.x += move
if player.rect.x > width-hbox : player.rect.x = width - hbox
Score += 1
player.rect.y += 2.5
screen.blit(BG, (0,0))
pg.draw.rect(screen, RED, player.rect, 1)
pg.draw.polygon(screen, GREEN, ((1024,768), (0,768), (0,640),(1024,640)))
font = pg.font.SysFont("comicsansms", 45)
text = font.render (" " + str(Score), 1, BLACK)
screen.blit(text,(480,700))
pg.event.get()
all_sprites.draw(screen)
pg.display.update()
clock.tick(30)
if pg.sprite.spritecollide(player, circles, False, collided = pg.sprite.collide_mask):
done = True
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()

Collision for Pygame Game Map

I am trying to make a maze game in Pygame but am unable to achieve collision for the 1 (maze wall) in the array. I tried to put the collision detection in the loop creating the map but it is not working. I also put the collision detection in the main loop but only the top left rect detected the collision, not all the 1 rects. How would I go about fixing this? Thank you!
import pygame
pygame.init()
screen = pygame.display.set_mode((700,700))
pygame.display.set_caption("Game")
speed = 20
x = 200
y = 600
def game_map():
global rect_one
surface = pygame.Surface((100, 100), pygame.SRCALPHA)
rect_one = pygame.draw.rect(surface, (0, 0, 255), (0, 0, 50, 50))
global rect_two
surface_one = pygame.Surface((80, 80), pygame.SRCALPHA)
rect_two = pygame.draw.rect(surface_one, (255, 255, 255), (0, 0, 50, 50))
tileX = 0
tileY = 0
global tile_list
tile_list = []
map = [
[1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,0,1,0,1,0,0,0,0,0,1],
[1,0,1,0,0,0,1,0,1,1,1,0,1],
[1,0,0,0,1,1,1,0,0,0,0,0,1],
[1,0,1,0,0,0,0,0,1,1,1,0,1],
[1,0,1,0,1,1,1,0,1,0,0,0,1],
[1,0,1,0,1,0,0,0,1,1,1,0,1],
[1,0,1,0,1,1,1,0,1,0,1,0,1],
[1,0,0,0,0,0,0,0,0,0,1,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1]
]
for y, row in enumerate(map):
tileX = 0
for x, cell in enumerate(row):
image = surface if cell == 1 else surface_one
screen.blit(image, [x*50, y*50])
tile_list.append(rect_one)
pygame.display.update()
def player():
player = pygame.draw.rect(screen, (255,0,0), (x, y, 20, 20))
for i in tile_list:
if player.colliderect(i):
print("hello")
loop = True
while loop:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
loop = False
#player controls
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
x -= speed
if keys[pygame.K_RIGHT]:
x += speed
if keys[pygame.K_UP]:
y -= speed
if keys[pygame.K_DOWN]:
y += speed
screen.fill((255,255,255))
game_map()
player()
pygame.display.update()
pygame.quit()
Your tile_list only contains one Rect multiple times.
I simplified your code a little bit and use a Rect with the correct coordinates for each 1 in your map. Also note the comments:
import pygame
pygame.init()
screen = pygame.display.set_mode((700,700))
pygame.display.set_caption("Game")
speed = 10
player_x = 200
player_y = 600
# Use a constant. There's not need to make big Surfaces and then draw a smaller rect on them to create the map.
TILESIZE = 50
tile_list = []
map = [
[1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,0,1,0,1,0,0,0,0,0,1],
[1,0,1,0,0,0,1,0,1,1,1,0,1],
[1,0,0,0,1,1,1,0,0,0,0,0,1],
[1,0,1,0,0,0,0,0,1,1,1,0,1],
[1,0,1,0,1,1,1,0,1,0,0,0,1],
[1,0,1,0,1,0,0,0,1,1,1,0,1],
[1,0,1,0,1,1,1,0,1,0,1,0,1],
[1,0,0,0,0,0,0,0,0,0,1,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1]
]
# let's create a single Surface for the map and reuse that
grid = pygame.Surface((len(map[0]) * TILESIZE, len(map) * TILESIZE), pygame.SRCALPHA)
for y, row in enumerate(map):
for x, cell in enumerate(row):
# if we want a wall, we draw it on the new Surface
# also, we store the Rect in the tile_list so collision detection works
if cell:
rect = pygame.draw.rect(grid, 'blue', (x*TILESIZE, y*TILESIZE, TILESIZE, TILESIZE))
tile_list.append(rect)
loop = True
clock = pygame.time.Clock()
while loop:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
loop = False
#player controls
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
player_x -= speed
if keys[pygame.K_RIGHT]:
player_x += speed
if keys[pygame.K_UP]:
player_y -= speed
if keys[pygame.K_DOWN]:
player_y += speed
screen.fill((255,255,255))
# draw the map surface to the screen
screen.blit(grid, (0 ,0))
player = pygame.draw.rect(screen, (255,0,0), (player_x, player_y, 20, 20))
# now collision detection works because for each 1 in the map
# there's a Rect in tile_list with the correct coordinates
for i in tile_list:
if player.colliderect(i):
print("colliding")
break
else:
print('not colliding')
pygame.display.update()
pygame.quit()
Save the position of the player before moving it:
pos = x, y
Compute the row and column after the player has moved:
row = y // 50
column = x // 50
Reset the player's position if the new position is on a wall:
if map[row][column] == 1:
x, y = pos
Additionally you have to move the map variable to global namespace. The speed should a integral divider of the tile size. Change the starting position to a position in the grid:
speed = 25
x = 50
y = 50
Complete code:
import pygame
pygame.init()
screen = pygame.display.set_mode((700,700))
pygame.display.set_caption("Game")
speed = 25
x = 50
y = 50
map = [
[1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,0,1,0,1,0,0,0,0,0,1],
[1,0,1,0,0,0,1,0,1,1,1,0,1],
[1,0,0,0,1,1,1,0,0,0,0,0,1],
[1,0,1,0,0,0,0,0,1,1,1,0,1],
[1,0,1,0,1,1,1,0,1,0,0,0,1],
[1,0,1,0,1,0,0,0,1,1,1,0,1],
[1,0,1,0,1,1,1,0,1,0,1,0,1],
[1,0,0,0,0,0,0,0,0,0,1,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1]
]
def game_map():
global rect_one
surface = pygame.Surface((100, 100), pygame.SRCALPHA)
rect_one = pygame.draw.rect(surface, (0, 0, 255), (0, 0, 50, 50))
global rect_two
surface_one = pygame.Surface((80, 80), pygame.SRCALPHA)
rect_two = pygame.draw.rect(surface_one, (255, 255, 255), (0, 0, 50, 50))
tileX = 0
tileY = 0
for y, row in enumerate(map):
tileX = 0
for x, cell in enumerate(row):
image = surface if cell == 1 else surface_one
screen.blit(image, [x*50, y*50])
pygame.display.update()
def player():
player = pygame.draw.rect(screen, (255,0,0), (x, y, 25, 25))
loop = True
while loop:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
loop = False
#player controls
keys = pygame.key.get_pressed()
pos = x, y
if keys[pygame.K_LEFT]:
x -= speed
if keys[pygame.K_RIGHT]:
x += speed
if keys[pygame.K_UP]:
y -= speed
if keys[pygame.K_DOWN]:
y += speed
row = y // 50
column = x // 50
if map[row][column] == 1:
x, y = pos
screen.fill((255,255,255))
game_map()
player()
pygame.display.update()
pygame.quit()

how to generate random rectangles in a pygame and make them move like flappy bird?

I am a python beginner. I want to recreate chrome dino game. the random rectangle won't stop and the loop runs forever...please help me to stop the loop and make rectangles move.
Code:
import pygame
import random
pygame.init()
win = pygame.display.set_mode((500, 500))
#red rectangle(dino)
x = 20
y = 400
width = 30
height = 42
gravity = 5
vel = 18
black = (0, 0, 0)
#ground
start_pos = [0, 470]
end_pos = [500, 470]
#cactus
x1 = 20
y1 = 30
white = (2, 200, 200)
run = True
clock = pygame.time.Clock()
while run:
clock.tick(30)
pygame.time.delay(10)
win.fill(black)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#random rectangle generation
for i in range(1):
width2 = random.randint(25, 25)
height2 = random.randint(60, 60)
top = random.randint(412, 412)
left = random.randint(300, 800)
rect = pygame.draw.rect(win, white, (left, top, width2,height2))
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
y = y - vel
else:
y = min(428, y + gravity)
pygame.draw.rect(win, (255, 0, 0), (x, y, width, height))
pygame.draw.line(win, white, start_pos, end_pos, 2)
pygame.display.update()
pygame.display.flip()
pygame.quit()
pygame.draw.rect() des not "generate" a rectangle, it draws a rectangle on a surface.
pygame.Rect is a rectangle object. Create an instance of pygame.Rect before the main application loop:
obstracle = pygame.Rect(500, random.randint(0, 412), 25, 60)
Change the position of the rectangle:
obstracle.x -= 3
if obstracle.right <= 0:
obstracle.y = random.randint(0, 412)
And draw the rectangle to the window surface:
pygame.draw.rect(win, white, obstracle)
Example:
import pygame
import random
pygame.init()
win = pygame.display.set_mode((500, 500))
start_pos = [0, 470]
end_pos = [500, 470]
gravity = 5
vel = 18
black = (0, 0, 0)
white = (2, 200, 200)
hit = 0
dino = pygame.Rect(20, 400, 30, 40)
obstracles = []
number = 5
for i in range(number):
ox = 500 + i * 500 // number
oy = random.randint(0, 412)
obstracles.append(pygame.Rect(ox, oy, 25, 60))
run = True
clock = pygame.time.Clock()
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
dino.y -= vel
else:
dino.y = min(428, dino.y + gravity)
for obstracle in obstracles:
obstracle.x -= 3
if obstracle.right <= 0:
obstracle.x = 500
obstracle.y = random.randint(0, 412)
if dino.colliderect(obstracle):
hit += 1
win.fill(black)
color = (min(hit, 255), max(255-hit, 0), 0)
pygame.draw.rect(win, color, dino)
for obstracle in obstracles:
pygame.draw.rect(win, white, obstracle)
pygame.draw.line(win, white, start_pos, end_pos, 2)
pygame.display.update()
pygame.quit()

How to detect a key press once and use '!=' symbol?

I am trying to figure out how to increase the intensity of my shapes moving when holding the 'up' arrow key. When I hold space bar, I wanted my shapes to rumble based on the intensity, which I've done. What I also would like to do is be able to increase the intensity by pressing the 'up' arrow key once (NOTE: Once. Currently when I press the arrow key once, it loops way too fast and multiplies the intensity by like 5000x. I would like it just to add one number each time.) This is the code that does not work how I'd like.
import pygame
import random
import time
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
# Edit the intensity of the shake (Must be one number apart)
# Ex: a = -100, b = 101. A is negative, B is positive
a = -4
b = 5
up = 10
intensity = (a, b)
pygame.init()
size = (700, 500)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My Game")
done = False
clock = pygame.time.Clock()
class Rectangle():
def __init__(self):
self.x = random.randrange(0, 700)
self.y = random.randrange(0, 500)
self.height = random.randrange(20, 70)
self.width = random.randrange(20, 70)
self.x_change = random.randrange(-3, 3)
self.y_change = random.randrange(-3, 3)
self.color = random.sample(range(250), 4)
def draw(self):
pygame.draw.rect(screen, self.color, [self.x, self.y, self.width, self.height])
def move(self):
self.x += self.x_change
self.y += self.y_change
class Ellipse(Rectangle):
pass
def draw(self):
pygame.draw.ellipse(screen, self.color, [self.x, self.y, self.width, self.height])
def move(self):
self.x += self.x_change
self.y += self.y_change
my_list = []
for number in range(600):
my_object = Rectangle()
my_list.append(my_object)
for number in range(600):
my_object = Ellipse()
my_list.append(my_object)
# -------- Main Program Loop -----------
while not done:
keys = pygame.key.get_pressed()
# --- Main event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(BLACK)
for rect in my_list:
rect.draw()
rect.move()
for rectElli in my_list:
rectElli.draw()
if keys[pygame.K_SPACE]:
rectElli.y_change = random.randrange(a, b)
rectElli.x_change = random.randrange(a, b)
rectElli.move()
print(a)
print(b)
if keys[pygame.K_UP] and up / 10 == 1:
up += 1
a -= 1
b -= -1
print(a)
print(b)
print(up)
if up / 10 != 1:
up += 1
pygame.display.flip()
clock.tick(60)
pygame.quit()
This is the code that worked before I wanted to add the increasing of intensity.
import pygame
import random
import time
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
# Edit the intensity of the shake (Must be one number apart)
# Ex: a = -100, b = 101. A is negative, B is positive
a = -4
b = 5
up = 10
intensity = (a, b)
pygame.init()
size = (700, 500)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My Game")
done = False
clock = pygame.time.Clock()
class Rectangle():
def __init__(self):
self.x = random.randrange(0, 700)
self.y = random.randrange(0, 500)
self.height = random.randrange(20, 70)
self.width = random.randrange(20, 70)
self.x_change = random.randrange(-3, 3)
self.y_change = random.randrange(-3, 3)
self.color = random.sample(range(250), 4)
def draw(self):
pygame.draw.rect(screen, self.color, [self.x, self.y, self.width, self.height])
def move(self):
self.x += self.x_change
self.y += self.y_change
class Ellipse(Rectangle):
pass
def draw(self):
pygame.draw.ellipse(screen, self.color, [self.x, self.y, self.width, self.height])
def move(self):
self.x += self.x_change
self.y += self.y_change
my_list = []
for number in range(600):
my_object = Rectangle()
my_list.append(my_object)
for number in range(600):
my_object = Ellipse()
my_list.append(my_object)
# -------- Main Program Loop -----------
while not done:
keys = pygame.key.get_pressed()
# --- Main event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(BLACK)
for rect in my_list:
rect.draw()
rect.move()
for rectElli in my_list:
rectElli.draw()
if keys[pygame.K_SPACE]:
rectElli.y_change = random.randrange(a, b)
rectElli.x_change = random.randrange(a, b)
rectElli.move()
pygame.display.flip()
clock.tick(60)
pygame.quit()
The first issue is, that you do the increasing of the intensity in the for-loop which traverse the ellipses. So the intensity is not increased once, it is increased once per ellipse.
If you don't want to increase the intensity in every 10th frame, then you've to verify if the increment the frame counter up in every frame, but you've to verify if it is divisible by 10. Verify if the remainder of an integral division by 10 is 0. Use the modulo operator (%) to calculate the remainder:
if up % 10 == 0:
See the example, when UP is pressed, the the intensity increases and when it is released then the intensity returns to normal:
while not done:
keys = pygame.key.get_pressed()
# --- Main event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(BLACK)
for rect in my_list:
rect.draw()
rect.move()
for rectElli in my_list:
rectElli.draw()
if keys[pygame.K_SPACE]:
rectElli.y_change = random.randrange(a, b)
rectElli.x_change = random.randrange(a, b)
rectElli.move()
if keys[pygame.K_UP]:
up += 1
if up % 10 == 0:
a -= 1
b -= -1
else:
a, b = -4, 5
pygame.display.flip()
clock.tick(60)

Update one sprite among a group of sprites [Pygame]

First post here. So I am trying to implement a Civilization type of movement game. At the moment, I have one sprite in a cell. I can click it and then if I click another grid, the sprite moves there. What I now want is to spawn 5-6 such sprites, and then do the same thing. Click on a sprite and then click another grid, and that specific sprite moves there without affecting the other sprites. I cannot seem to do that. I can spawn 5-6 random sprites at different grids, but when I click on one of them and then click another grid, all the other sprites are gone. The code is below (not the best as I am learning Pygame). I understand that I have to somehow only update the sprite that was clicked, but I am not sure how to do that.
import pygame
import random
WIDTH = 900
HEIGHT = 700
FPS = 2
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
TURN = "TeamOne"
def main():
# Pygame sprite Example
global x_lines
global y_lines
x_lines = [WIDTH-i*WIDTH/20 for i in range(20,0, -1)]
y_lines = [HEIGHT-j*HEIGHT/20 for j in range(20,0, -1)]
class TeamOne(pygame.sprite.Sprite):
# sprite for the Player
def __init__(self):
# this line is required to properly create the sprite
pygame.sprite.Sprite.__init__(self)
# create a plain rectangle for the sprite image
self.image = pygame.Surface((WIDTH / 20, HEIGHT / 20))
self.image.fill(GREEN)
# find the rectangle that encloses the image
self.rect = self.image.get_rect()
# center the sprite on the screen
self.rect.center = ((random.randint(1,19)*2+1)* WIDTH/ 40, (random.randint(1,19)*2+1)*HEIGHT/40)
def update(self, position):
# any code here will happen every time the game loop updates
(a, b) = position
for index, i in enumerate(x_lines):
if i > a:
self.rect.x = x_lines[index-1]
break
for index, j in enumerate(y_lines):
if j > b:
self.rect.y = y_lines[index-1]
break
# initialize pygame and create window
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("A Game")
clock = pygame.time.Clock()
clicked_sprites = pygame.sprite.Group()
teamone_sprites = pygame.sprite.Group()
for i in range(5):
mob1 = TeamOne()
teamone_sprites.add(mob1)
# Game loop
running = True
j=0
while running:
# keep loop running at the right speed
clock.tick(FPS)
# Process input (events)
for event in pygame.event.get():
# check for closing window
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN and j == 0:
pos = pygame.mouse.get_pos()
for s in teamone_sprites:
if s.rect.collidepoint(pos):
#teamone_sprites.add(s)
clicked_sprites.add(s)
print (clicked_sprites)
j = 1
elif event.type == pygame.MOUSEBUTTONDOWN and j == 1:
new_pos = pygame.mouse.get_pos()
#teamone_sprites.update(new_pos)
clicked_sprites.update(new_pos)
j = 0
# Update
# Draw / render
## screen.fill(BLACK)
## draw_grid(screen)
##
## teamone_sprites.draw(screen)
##
##
##
## # *after* drawing everything, flip the display
## pygame.display.flip()
# Draw / render
screen.fill(BLACK)
draw_grid(screen)
teamone_sprites.draw(screen)
pygame.display.flip()
pygame.quit()
def draw_grid(screen):
for i in range(1, HEIGHT, int(HEIGHT/20)):
pygame.draw.line(screen, GREEN, (1,i) ,(WIDTH,i), 2)
for j in range(1, WIDTH, int(WIDTH/20)):
pygame.draw.line(screen, GREEN, (j,1) ,(j,HEIGHT), 2)
if __name__ == '__main__':
main()
Some tips for you:
Keep your main loop clean
Put logic where it belongs
Only call pygame.display.flip()/pygame.display.update() once
Don't use variable names like j
Since your game is grid based, you should have a way to translate between grid coordinates and screen coordinates
Here's a simple runnable example I hacked together (see the comments for some explanations):
import pygame
import random
WIDTH = 900
HEIGHT = 700
ROWS = 20
COLUMNS = 20
TILE_SIZE = WIDTH / COLUMNS, HEIGHT / ROWS
TILE_W, TILE_H = TILE_SIZE
FPS = 60
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
TURN = "TeamOne"
# some functions to translate grid <-> screen coordinates
def posToScreen(pos):
column, row = pos
return column * TILE_W, row * TILE_H
def screenToPos(pos):
column, row = pos
return column / TILE_W, row / TILE_H
def draw_grid(screen):
for i in range(1, HEIGHT, TILE_H):
pygame.draw.line(screen, GREEN, (1,i) ,(WIDTH,i), 2)
for j in range(1, WIDTH, TILE_W):
pygame.draw.line(screen, GREEN, (j,1) ,(j,HEIGHT), 2)
# a class that handles selecting units
class Cursor(pygame.sprite.Sprite):
def __init__(self, units, *groups):
pygame.sprite.Sprite.__init__(self, *groups)
# group of the units that can be controlled
self.units = units
# we create two images
# to indicate if we are selecting or moving
self.image = pygame.Surface(TILE_SIZE)
self.image.set_colorkey((43,43,43))
self.image.fill((43,43,43))
self.rect = self.image.get_rect()
self.selected_image = self.image.copy()
pygame.draw.rect(self.image, pygame.Color('red'), self.image.get_rect(), 4)
pygame.draw.rect(self.selected_image, pygame.Color('purple'), self.image.get_rect(), 4)
self.base_image = self.image
self.selected = None
def update(self):
# let's draw the rect on the grid, based on the mouse position
pos = pygame.mouse.get_pos()
self.rect.topleft = posToScreen(screenToPos(pos))
def handle_click(self, pos):
if not self.selected:
# if we have not selected a unit, do it now
for s in pygame.sprite.spritecollide(self, self.units, False):
self.selected = s
self.image = self.selected_image
else:
# if we have a unit selected, just set its target attribute, so it will move on its own
self.selected.target = posToScreen(screenToPos(pos))
self.image = self.base_image
self.selected = None
class TeamOne(pygame.sprite.Sprite):
def __init__(self, *groups):
pygame.sprite.Sprite.__init__(self, *groups)
self.image = pygame.Surface(TILE_SIZE)
self.image.fill(GREEN)
self.pos = random.randint(0, COLUMNS), random.randint(0, ROWS)
self.rect = self.image.get_rect(topleft = posToScreen(self.pos))
self.target = None
def update(self):
# do nothing until target is set
# (maybe unset it if we reached our target)
if self.target:
if self.rect.x < self.target[0]:
self.rect.move_ip(1, 0)
elif self.rect.x > self.target[0]:
self.rect.move_ip(-1, 0)
elif self.rect.y < self.target[1]:
self.rect.move_ip(0, 1)
elif self.rect.y > self.target[1]:
self.rect.move_ip(0, -1)
self.pos = screenToPos(self.rect.topleft)
def main():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("A Game")
clock = pygame.time.Clock()
all_sprites = pygame.sprite.LayeredUpdates()
team_ones = pygame.sprite.Group()
for i in range(5):
TeamOne(all_sprites, team_ones)
cursor = Cursor(team_ones, all_sprites)
# a nice, simple, clean main loop
running = True
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# we could also pass all events to all sprites
# so we would not need this special clause for the cursor...
if event.type == pygame.MOUSEBUTTONDOWN:
cursor.handle_click(event.pos)
all_sprites.update()
screen.fill(BLACK)
draw_grid(screen)
all_sprites.draw(screen)
pygame.display.flip()
if __name__ == '__main__':
main()
I have pretty much the same Problem i have a healthbar for my enemie but i want all enemys to have one so its a spritegroup now if i want to change an attribute of one object out of my spritegroup i dont know how to properly access it. The Problem lays in the Healthbaranimation function. I tried self.healthbar.sprites() self.healthbar.sprites and spritedict nothing really semms to work. Is there an easy way to fix this? P.s sorry for my bad code It is my first real attempt making a small game
from os import path
import pygame
from elements.ammo import AMMO
from elements.bigenemy import BIGENEMY
from elements.enemy import ENEMY
from elements.player import PLAYER
from .base import BaseState
from elements.healthbar import HEALTHBAR
class Gameplay(BaseState):
def __init__(self):
super(Gameplay, self).__init__()
self.next_state = "GAME_OVER"
self.x, self.y = 100, 1030
self.playersprite = PLAYER((self.x, self.y))
self.bigenemy = pygame.sprite.GroupSingle(BIGENEMY())
self.bottomrect = pygame.Rect((0, 1030), (1920, 50))
self.enemysprite = ENEMY()
self.ammosprite = AMMO()
self.healthbar = pygame.sprite.Group(HEALTHBAR())
self.displayedimage = self.playersprite.image
self.displayedrect = self.playersprite.rect
self.highscore = self.load_data()
self.points = 0
self.scoretext = f"SCORE: {self.points}"
self.scoresurf = self.font.render(self.scoretext, True, "red")
self.nhstext = "NEW HIGHSCORE!"
self.nhssurf = self.font.render(self.nhstext, True, "red")
self.ammotext = f"AMMO:{self.playersprite.ammunition}"
self.ammosurf = self.font.render(self.ammotext, True, "red")
self.bulletgroup = pygame.sprite.Group()
self.time_active = 0
self.bigenemyexisting = True
def get_event(self, event):
if event.type == pygame.QUIT:
self.quit = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LCTRL:
self.playersprite.crouching = True
elif event.key == pygame.K_SPACE:
self.playersprite.jumping = True
elif event.key == pygame.K_q and self.playersprite.ammunition != 0:
self.playersprite.shooting = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_ESCAPE:
self.done = True
elif event.key == pygame.K_LCTRL:
self.playersprite.crouching = False
elif event.key == pygame.K_q:
self.playersprite.shooting = False
def draw(self, surface):
surface.fill(pygame.Color("black"))
pygame.draw.rect(surface, "red", self.bottomrect)
surface.blit(self.displayedimage, (self.displayedrect))
surface.blit(self.enemysprite.image, (self.enemysprite.rect))
surface.blit(self.ammosprite.image, (self.ammosprite.rect))
self.healthbar.draw(surface)
self.bigenemy.draw(surface)
self.bulletgroup.draw(surface)
surface.blit(self.scoresurf, (0, 0))
surface.blit(self.ammosurf, (0, 1000))
if self.points > self.highscore: surface.blit(self.nhssurf, (1920 / 2 - 100, 1080 / 2))
def lost(self):
self.enemysprite.startposx = 1920
self.enemysprite.startposy = self.enemysprite.gettypeenemy()
self.enemysprite.speed = 10
self.highscorefunc()
self.points = 0
self.playersprite.ammunition = 30
def collidecheck(self):
self.playermask = pygame.mask.from_surface(self.displayedimage)
self.enemymask = pygame.mask.from_surface(self.enemysprite.image)
offsetx = self.enemysprite.rect.left - self.displayedrect.left
offsety = self.enemysprite.rect.top - self.displayedrect.top
if self.displayedrect.colliderect(self.enemysprite.rect):
if self.playermask.overlap(self.enemymask, (offsetx, offsety)):
self.lost()
self.done = True
elif self.enemysprite.rect.x < 0 and self.enemysprite.speed < 25:
self.points += 1
self.enemysprite.speed += 1
elif self.enemysprite.speed > 25:
self.enemysprite.speed += .5
elif self.displayedrect.colliderect(self.ammosprite.rect):
self.ammosprite.startposx = 2300
self.playersprite.ammunition += 30
elif pygame.sprite.groupcollide(self.bigenemy,self.bulletgroup,False,True):
self.bigenemy.sprite.health -= 10
def shooting(self, dt):
if self.playersprite.ammunition != 0:
if self.playersprite.shooting and not self.playersprite.jumping and not self.playersprite.crouching:
self.time_active += dt
if self.time_active >= 100:
self.bulletgroup.add(self.playersprite.createbullet())
self.time_active = 0
self.playersprite.ammunition -= 1
else:
self.playersprite.shooting = False
def highscorefunc(self):
if self.points > self.highscore:
self.highscore = self.points
with open(path.join(self.dir, self.HS_FILE), 'w') as f:
f.write(str(self.highscore))
def animation(self):
if not self.playersprite.shooting and not self.playersprite.jumping and not self.playersprite.crouching:
if self.playersprite.index >= len(self.playersprite.basicanimation):
self.playersprite.index = 0
self.displayedimage = self.playersprite.basicanimation[int(self.playersprite.index)]
self.playersprite.index += .1
elif self.playersprite.shooting and not self.playersprite.jumping:
if self.playersprite.index >= len(self.playersprite.shootanimation):
self.playersprite.index = 0
self.displayedimage = self.playersprite.shootanimation[int(self.playersprite.index)]
self.playersprite.index += .1
elif self.playersprite.jumping:
self.displayedimage = self.playersprite.imagejump
elif self.playersprite.crouching:
self.displayedimage = self.playersprite.slidingimage
def healthbaranimation(self):
if self.bigenemy.sprite.health < 90:
self.healthbar.spritedict.index = 1
if self.bigenemy.sprite.health < 80:
self.healthbar.sprite.index = 2
if self.bigenemy.sprite.health < 70:
self.healthbar.sprite.index = 3
if self.bigenemy.sprite.health < 60:
self.healthbar.sprite.index = 4
if self.bigenemy.sprite.health < 50:
self.healthbar.sprite.index = 5
if self.bigenemy.sprite.health < 40:
self.healthbar.sprite.index = 6
if self.bigenemy.sprite.health < 30:
self.healthbar.sprite.index = 7
if self.bigenemy.sprite.health < 20:
self.healthbar.sprite.index = 8
if self.bigenemy.sprite.health < 10:
self.healthbar.sprite.index = 9
def spawnbigenemies(self):
if self.bigenemyexisting:
if self.bigenemy.sprite.health < 3:
self.bigenemy.add(BIGENEMY())
self.bigenemyexisting = True
def update(self, dt):
try:
self.bigenemy.sprite.update()
except:
pass
self.healthbaranimation()
self.healthbar.update()
self.playersprite.jump()
self.animation()
self.shooting(dt)
self.bulletgroup.update()
self.enemysprite.update()
self.ammosprite.update()
self.collidecheck()
self.spawnbigenemies()
self.scoretext = f"SCORE: {self.points}"
self.scoresurf = self.font.render(self.scoretext, True, "black")
self.ammotext = f"AMMO:{self.playersprite.ammunition}"
self.ammosurf = self.font.render(self.ammotext, True, "red")

Categories

Resources