I am coding a game of snake using the pygame module in python and am very near the end yet running into some unforeseen issues. My code runs yet my snake won't turn left and when I collide with an apple I do not grow in size and the apple does not disappear. Additionally, when my character hits the wall the end screen also does not come up. I am positive this is due to some small issue in my code and would appreciate for anyone to take a look and see if they can root it out since I have been unable to do so. Also, any tips for improving the code would be greatly appreciated.
import sys, time, random, pygame
from random import randrange
pygame.init()
fps_controller = pygame.time.Clock()
#Screen Dimensions
screen_width = 800
screen_height = 800
#Screen Set Up
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Snake by Bela Zumbansen")
pygame.mouse.set_visible(0)
#Colours
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLACK = (0, 0, 0)
GREY = (200, 200, 200)
BLUE = (0, 0, 255)
WHITE = (255, 255, 255)
LIGHTBLUE = (0, 155, 155)
#Directions
RIGHT = 1
LEFT = 2
UP = 3
DOWN = 4
#Game Set Up
snake_pos = [100, 100]
snake_body = [[100, 100], [190, 100], [180, 400]]
snake_speed = 10
apple_pos = [random.randrange(1,80)*10, random.randrange(1,80)*10]
apple_spawn = True
direction = RIGHT
update = direction
score = 0
def over():
run = False
return run
def game_over():
screen.fill(LIGHTBLUE)
draw_text("GAME OVER", 48, WHITE, screen_width/2, screen_height/4)
draw_text("Score: " + str(score), 22, WHITE, screen_width / 2, screen_height / 3)
draw_text("Press SPACE to play again or ESC to exit", 22, WHITE, screen_width/2, screen_height / 4)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
run = True
if event.key == pygame.K_ESCAPE:
pygame.quit()
return run
def draw_text(text, size, color, x, y):
font = pygame.font.Font('freesansbold.ttf', size)
TextSurf, TextRect = text_objects(text, font, color)
TextRect.center = (x, y)
screen.blit(TextSurf, TextRect)
def text_objects(text, font, color):
textSurface = font.render(text, True, color)
return textSurface, textSurface.get_rect()
def eating_apple():
global score, apple_spawn
score += 1
apple_spawn = False
def spawnApple():
global apple_pos
apple_pos = [random.randrange(1,80)*10, random.randrange(1,80)*10]
def score(score):
font = pygame.font.SysFont(None, 25)
text = font.render("Score: "+str(score), True, WHITE)
screen.blit(text,(10,10))
def main():
global update, direction, run, snake_pos, snake_speed, apple_spawn, apple_pos
pygame.time.delay(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
update = 'LEFT'
elif keys[pygame.K_RIGHT]:
update = RIGHT
elif keys[pygame.K_UP]:
update = UP
elif keys[pygame.K_DOWN]:
update = DOWN
if update == RIGHT and direction != LEFT:
direction = RIGHT
if update == LEFT and direction != RIGHT:
direction = LEFT
if update == UP and direction != DOWN:
direction = UP
if update == DOWN and direction != UP:
direction = DOWN
if direction == RIGHT:
snake_pos[0] += snake_speed
if direction == LEFT:
snake_pos[0] -= snake_speed
if direction == UP:
snake_pos[1] -= snake_speed
if direction == DOWN:
snake_pos[1] += snake_speed
snake_body.insert(0, list(snake_pos))
if snake_pos[0] == apple_pos[0] and snake_pos[1] == apple_pos[1]:
eating_apple()
else:
snake_body.pop()
if not apple_spawn:
spawnApple()
screen.fill(BLACK)
for pos in snake_body:
# Snake body
# .draw.rect(play_surface, color, xy-coordinate)
# xy-coordinate -> .Rect(x, y, size_x, size_y)
pygame.draw.rect(screen, GREEN, pygame.Rect(pos[0], pos[1], 20, 20))
pygame.draw.rect(screen, RED, pygame.Rect(apple_pos[0], apple_pos[1], 20, 20))
if snake_pos[0] < 0 or snake_pos[0] > screen_width-20:
over()
if snake_pos[1] < 0 or snake_pos[1] > screen_height-20:
over()
for block in snake_body[1:]:
if snake_pos[0] == block[0] and snake_pos[1] == block[1]:
over()
# score(score)
pygame.display.update()
fps_controller.tick(25)
#main loop
run = True
while run:
main()
while not run:
game_over()
[...] my snake won't turn left [...]
It has to be update = LEFT rather than update = 'LEFT'
[...] when I collide with an apple I do not grow in size and the apple does not disappear
There is a variable and a function with the name score. Rename the variable e.g. scoreval. See also [Python: function and variable with same name](Python: function and variable with same name):
scoreval = 0
def eating_apple():
global scoreval, apple_spawn
scoreval += 1
apple_spawn = False
Use pygame.Rect.colliderect(), to check if the head of the snake eats the apple:
if pygame.Rect(*snake_pos, 20, 20).colliderect(*apple_pos, 20, 20):
eating_apple()
else:
snake_body.pop()
When the apple has been spawned, the the state apple_spawn has to be reset:
def spawnApple():
global apple_pos, apple_spawn
apple_pos = [random.randrange(1,80)*10, random.randrange(1,80)*10]
apple_spawn = True
Referring to the comment:
[...] I'm still running into are that the game doesn't end if the snake collides with itself or runs into the wall. I have if statements for those so I'm a little lost as to why nothing is happening [...]
You've to use the global statement to change the value of the variable run in global scope in the function over:
def over():
global run
run = False
Related
I've tried to add gravity using the quadratic formula but didn't seem to work and I tried to turn it into a function and that still didn't seem to work. I tried using a class but I don't quite understand how they work so can someone help me add gravity to my platformer. I've tried many videos but didn't seem to help so any help would be much appreciated
import pygame
from pygame.locals import *
from pygame import mixer
from typing import Tuple
import pickle
from os import path
import time
import sys
import os
pygame.init()
white = 255,255,255
black = 0,0,0
green = 0, 255, 0
blue = 0,0,255
font = pygame.font.SysFont('Comic Sans MS', 50, 70)
screen_width = 650
screen_height = 800
screen = pygame.display.set_mode([screen_width,screen_height])
screen.fill((white))
pygame.display.set_caption("Darsh's Game")
pygame.display.update()
pygame.draw.rect(screen,white,(200,150,100,50))
running = 1
speed = 1
x = 25
y = 669
isJump = False
jumpCount = 10
left = False
right = True
def draw_text(text, font, text_col, x, y):
img = font.render(text, True, text_col)
screen.blit(img, (x, y))
def draw_rect():
rect1 = pygame.Rect(0, 0, 25, 800)
pygame.draw.rect(screen, black, rect1)
rect2 = pygame.Rect(625, 0, 25, 800)
pygame.draw.rect(screen, black, rect2)
rect3 = pygame.Rect(0, 775, 650, 25)
pygame.draw.rect(screen, green, rect3)
pygame.display.flip()
def direction(left, right):
if left == True:
screen.blit(mainleft_img, (x, y))
elif right == True:
screen.blit(mainright_img, (x, y))
draw_rect()
draw_text("George is dumb", font, green, 100, 400)
pygame.display.update()
mainright_img = pygame.image.load('newgameimg/mainright.png')
mainleft_img = pygame.image.load('newgameimg/mainleft.png')
screen.blit(mainright_img, (300, 400))
run = True
while run:
draw_text("George is dumb", font, green, 100, 400)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_a] and x > speed - 16:
x -= speed
left = True
right = False
draw_text("George is dumb", font, green, 100, 400)
pygame.display.update()
if keys[pygame.K_d] and x < screen_width - speed - 89:
x += speed
left = False
right = True
draw_text("George is dumb", font, green, 100, 400)
pygame.display.update()
if keys[pygame.K_ESCAPE]:
pygame.quit()
if not (isJump):
if keys[pygame.K_SPACE]: # jumping code
isJump = True
else:
if jumpCount >= -10:
time.sleep(0.02)
y -= (jumpCount * abs(jumpCount)) * 0.5
jumpCount -= 1
else:
jumpCount = 10
isJump = False # jumping code
screen.fill(white)
direction(left, right)
draw_text("George is dumb", font, green, 100, 400)
draw_rect()
pygame.display.update()
You don't need the quadratic formula to get a simple gravity going; a simple gravity variable increasing every frame should do the trick.
...
gravity = 0
while run:
...
...
if keys[pygame.K_SPACE]:
gravity += 1
y -= gravity
...
Note that I don't speak your language; I mainly code in JavaScript, but I did try python once and I got the syntax down pretty quick. Hopefully this suits your needs, with a bit of tweaking of course.
Add a variable fall = 0. Increment the variable if the player does not jump (fall += 0.2). Change the y coordinate of the player through the variable fall in each frame. Get the bounding rectangle of the player with pygame.Surface.get_rect(). If the bottom of the rectangle reaches the floor then stop falling, and restricting the bottom of the rectangle on the floor. Set fall = 0 if the player starts to jump:
while run:
# [...]
if not (isJump):
fall += 0.2
y += fall
player_rect = mainleft_img.get_rect(topleft = (x, y))
if player_rect.bottom > 775:
player_rect.bottom = 775
y = player_rect.top
fall = 0
if keys[pygame.K_SPACE]: # jumping code
isJump = True
fall = 0
else:
# [...]
Multiple calls to pygame.display.update() or pygame.display.flip() cause flickering. Remove all calls to pygame.display.update() from your code, but call it once at the end of the application loop.
In addition, draw each object only once.
Do not delay, wait or sleep. 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:
clock = pygame.time.Clock()
run = True
while run:
clock.tick(60)
runs 60 times per second.
See pygame.time.Clock.tick():
This method should be called once per frame. It will compute how many milliseconds have passed since the previous call.
Complete example:
import pygame
from pygame.locals import *
pygame.init()
white = 255,255,255
black = 0,0,0
green = 0, 255, 0
blue = 0,0,255
screen_width = 650
screen_height = 800
screen = pygame.display.set_mode([screen_width,screen_height])
pygame.display.set_caption("Darsh's Game")
font = pygame.font.SysFont('Comic Sans MS', 50, 70)
running = 6
speed = 2
fall = 0
x = 25
y = 669
isJump = False
jumpCount = 10
left = False
right = True
def draw_text(text, font, text_col, x, y):
img = font.render(text, True, text_col)
screen.blit(img, (x, y))
def draw_rect():
rect1 = pygame.Rect(0, 0, 25, 800)
pygame.draw.rect(screen, black, rect1)
rect2 = pygame.Rect(625, 0, 25, 800)
pygame.draw.rect(screen, black, rect2)
rect3 = pygame.Rect(0, 775, 650, 25)
pygame.draw.rect(screen, green, rect3)
def direction(left, right):
if left == True:
screen.blit(mainleft_img, (x, y))
elif right == True:
screen.blit(mainright_img, (x, y))
draw_text("George is dumb", font, green, 100, 400)
mainright_img = pygame.image.load('newgameimg/mainright.png')
mainleft_img = pygame.image.load('newgameimg/mainleft.png')
clock = pygame.time.Clock()
run = True
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_a] and x > speed - 16:
x -= speed
left = True
right = False
if keys[pygame.K_d] and x < screen_width - speed - 89:
x += speed
left = False
right = True
if keys[pygame.K_ESCAPE]:
pygame.quit()
if not (isJump):
fall += 0.2
y += fall
player_rect = mainleft_img.get_rect(topleft = (x, y))
if player_rect.bottom > 775:
player_rect.bottom = 775
y = player_rect.top
fall = 0
if keys[pygame.K_SPACE]: # jumping code
isJump = True
fall = 0
else:
if jumpCount >= -10:
y -= (jumpCount * abs(jumpCount)) * 0.5
jumpCount -= 1
else:
jumpCount = 10
isJump = False # jumping code
screen.fill(white)
draw_rect()
direction(left, right)
pygame.display.update()
for some reason when I collide with the enemy it randomly spawns elsewhere which is all fine but when I un-collide the enemy goes back to its original position
here is the full code down below. If you run this code see what happens. Im looking for the red block to spawn in a random other location when the blue box collides with it.
import pygame
import random
# places where enemies can spawn (2 to make it simple at first)
enemy_locations = [100, 200]
pygame.init()
# clock
clock = pygame.time.Clock()
# frames per second
fps = 30
# colors
background_color = (255, 255, 255)
player_color = (0, 0, 255)
enemy_color = (255, 0, 0)
# width and height of screen
width = 1000
height = 800
# screen
screen = pygame.display.set_mode((width, height))
# x, y coordinates player
player_x = 300
player_y = 300
# ememy x, y coordinates
enemy_x = random.choice(enemy_locations)
enemy_y = random.choice(enemy_locations)
# new x, y coordinates for enemy player
new_x = random.choice(enemy_locations)
new_y = random.choice(enemy_locations)
# draw player
def draw():
enemy_rect = pygame.Rect(enemy_x, enemy_y, 25, 25)
player_rect = pygame.Rect(player_x, player_y, 25, 25)
if player_rect.colliderect(enemy_rect):
enemy = pygame.draw.rect(screen, enemy_color, (new_x, new_y, 25, 25))
else:
enemy = pygame.draw.rect(screen, enemy_color, enemy_rect)
player = pygame.draw.rect(screen, player_color, player_rect)
# pygame loop (always include)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# controls for player object
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player_x -= 10
if event.key == pygame.K_RIGHT:
player_x += 10
if event.key == pygame.K_UP:
player_y -= 10
if event.key == pygame.K_DOWN:
player_y += 10
draw()
pygame.display.update()
screen.fill(background_color)
clock.tick(fps)
The draw method explicitly states to draw enemy rect either in the initial point or in some ONE random point. The random point is selected only once - during the start of an application. You should set the random point every time there is a collision in a draw method, e.g.
if player_rect.colliderect(enemy_rect):
enemy_x = random.choice(enemy_locations)
enemy_y = random.choice(enemy_locations)
You have to create a new random enemy position when the player collides with the enemy
if player_rect.colliderect(enemy_rect):
enemy_x = random.choice(enemy_locations)
enemy_y = random.choice(enemy_locations)
Use the global statement, to interpreted the variables enemy_x and enemy_y as global. With the statement global it is possible to write to variables in the global namespace within a function:
global enemy_x, enemy_y
Function draw:
def draw():
global enemy_x, enemy_y
enemy_rect = pygame.Rect(enemy_x, enemy_y, 25, 25)
player_rect = pygame.Rect(player_x, player_y, 25, 25)
if player_rect.colliderect(enemy_rect):
enemy_x = random.choice(enemy_locations)
enemy_y = random.choice(enemy_locations)
enemy = pygame.draw.rect(screen, enemy_color, enemy_rect)
player = pygame.draw.rect(screen, player_color, player_rect)
As others have said you either need to add more "locations" to the list or better, use random.randint(), and also enemy's coordinates does not "get random"(or updated in any way) after every collision, but only once when you initialize them.
I added new function get_new_coord() which will return the random number(I use these limits so the enemy won't go off the screen), which gets called once for every coordinate after each collision.
Also I moved player_rect and enemy_rect outside of the loop, and now draw() returns enemy_rect so it can stay updated if collision occurs.
This is a quick fix to your code, you should consider using Classes. And of course if you want to do so, I could help you with transforming the code.
import pygame
import random
# places where enemies can spawn (2 to make it simple at first)
enemy_locations = [100, 200]
pygame.init()
# clock
clock = pygame.time.Clock()
# frames per second
fps = 30
# colors
background_color = (255, 255, 255)
player_color = (0, 0, 255)
enemy_color = (255, 0, 0)
# width and height of screen
width = 1000
height = 800
# screen
screen = pygame.display.set_mode((width, height))
# x, y coordinates player
player_x = 300
player_y = 300
# ememy x, y coordinates
enemy_x = random.choice(enemy_locations)
enemy_y = random.choice(enemy_locations)
# new x, y coordinates for enemy player
new_x = random.choice(enemy_locations)
new_y = random.choice(enemy_locations)
def get_new_coord():
return random.randint(0, 800-25)
# draw player
def draw(player_rect, enemy_rect):
screen.fill(background_color)
if player_rect.colliderect(enemy_rect):
enemy_rect = pygame.Rect(get_new_coord(), get_new_coord(), 25, 25)
enemy = pygame.draw.rect(screen, enemy_color, enemy_rect)
else:
enemy = pygame.draw.rect(screen, enemy_color, enemy_rect)
player = pygame.draw.rect(screen, player_color, player_rect)
pygame.display.update()
return enemy_rect
# pygame loop (always include)
running = True
enemy_rect = pygame.Rect(enemy_x, enemy_y, 25, 25)
player_rect = pygame.Rect(player_x, player_y, 25, 25)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# controls for player object
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player_rect.x -= 10
if event.key == pygame.K_RIGHT:
player_rect.x += 10
if event.key == pygame.K_UP:
player_rect.y -= 10
if event.key == pygame.K_DOWN:
player_rect.y += 10
enemy_rect = draw(player_rect, enemy_rect)
clock.tick(fps)
Alright, so yes, I get that it is a lot of code I am about to display.
My problem: I am making a zombie shooter game where you are the main character on top of a building and you have to click on zombies as they come in waves. My current problem is whenever multiple zombies overlap on top of each other, I can kill both of them (or as many are overlapping) in one click because technically, all of their hitboxes are colliding.
If this is a bigger problem than sought out to be, I would like someone to just say that.
import pygame
import random
import time
pygame.init()
#Setting Variables
screenW = 1020
screenH = 630
x = 125
y = 164
width = 50
height = 50
velocity = 5
wave = 2
GOLD = (255, 215, 0)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 128, 0)
wallHealth = 0
zombieKilled = 0
class ZombieChars():
def __init__(self):
self.damage = 0
self.vel = 5
self.x_change = random.randrange(2,5)
self.y_change = 1
self.height = 120
self.width = 149
self.color = random.sample(range(250), 4)
self.image = pygame.Surface([self.width, self.height], pygame.HWSURFACE, 32)
self.rect = self.image.get_rect(topleft = (random.randrange(900, 1150), 330))
#pygame.draw.rect(self.image, (self.color), (self.x, self.y, self.width, self.height))
def draw(self):
window.blit(ZombieWalking, self.rect.topleft)
def update(self):
if self.rect.x >= 364:
self.rect.x -= self.x_change
else:
self.rect.x -= 0
def wallHP(self):
global wallHealth
if self.rect.x < 365:
self.damage += 1
if self.damage == 30:
self.damage = 0
wallHealth += 1
def death(self):
global zombieKilled
if event.type == pygame.MOUSEBUTTONDOWN:
gunShot.play()
mouse_pos = event.pos
if self.rect.collidepoint(mouse_pos):
self.rect.x = 5000
self.rect.x -= self.x_change
zombieHit.play()
zombieKilled += 1
print(zombieKilled)
def waveCounter(self):
global wave
print(wave)
if wave == zombieKilled / 2:
wave = 2
#FPS
clock = pygame.time.Clock()
clock.tick(60)
#Screen
window = pygame.display.set_mode((screenW,screenH))
pygame.display.set_caption(("Zombie Shooter"))
#Image Loading
bg = pygame.image.load("bg.jpg")
mainmenu = pygame.image.load("mainmenu.jpg")
ZombieWalking = pygame.image.load("Sprites/AAIdle.png")
#Sound Loading
gunShot = pygame.mixer.Sound('sounds/gunShot.wav')
zombieHit = pygame.mixer.Sound('sounds/zombieHit.wav')
gameMusic = pygame.mixer.music.load('sounds/gameMusic.mp3')
menuMusic = pygame.mixer.music.load('sounds/menuMusic.mp3')
zombies = ZombieChars()
my_list = []
for zombs in range(wave):
my_object = ZombieChars()
my_list.append(my_object)
def text_objects(text, font):
textSurface = font.render(text, True, BLACK)
return textSurface, textSurface.get_rect()
smallText = pygame.font.Font('freesansbold.ttf', 30)
tinyText = pygame.font.Font('freesansbold.ttf', 20)
TextSurf3, TextRect3 = text_objects("Wave: " + str(wave), smallText)
TextRect3.center = ((1020 / 2), (50))
#Main Loop
run = True
mainMenu = True
pygame.mixer.music.play()
global event
while mainMenu == True:
window.blit(mainmenu, (0,0))
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False #if x is pressed dont run game
mainMenu = False
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
mainMenu = False #if a is pressed run game
def wallHPBar():
pygame.draw.rect(window, GREEN, (20, 20, 100, 10))
if wallHealth == 0:
pass
if wallHealth == 1:
pygame.draw.rect(window, RED, (20, 20, 25, 10))
if wallHealth == 2:
pygame.draw.rect(window, RED, (20, 20, 50, 10))
if wallHealth == 3:
pygame.draw.rect(window, RED, (20, 20, 75, 10))
if wallHealth >= 4:
pygame.draw.rect(window, RED, (20, 20, 100, 10))
def overlapKill():
if zombieKilled == 1:
print("oh my goodness we going")
if zombieKilled == 2:
print("we 2 ")
while run:
pygame.mixer.music.stop()
window.blit(bg, (0, 0))
window.blit(TextSurf3, TextRect3)
wallHPBar()
pygame.time.delay(25)
for zombie in my_list:
zombie.draw()
zombie.update()
zombie.death()
zombie.wallHP()
zombie.waveCounter()
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
Thank you.
Remove the event handling from the method death and return a boolean value, that indicates if a zombie was killed:
class ZombieChars():
# [...]
def death(self):
global zombieKilled
mouse_pos = pygame.mouse.get_pos()
if self.rect.collidepoint(mouse_pos):
self.rect.x = 5000
self.rect.x -= self.x_change
zombieHit.play()
zombieKilled += 1
print(zombieKilled)
return True
return False
Do the pygame.MOUSEBUTTONDOWN event handling in the event loop and evaluate if a zombie was killed in a loop. break the loop when a zombie is killed. Thus only one zombie can be get killed on one klick:
while run:
# [...]
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
gunShot.play()
for zombie in (reversed):
if zombie.death():
break
for zombie in my_list:
zombie.draw()
zombie.update()
# zombie.death() <--- DELETE
zombie.wallHP()
zombie.waveCounter()
A Zombie object should not be dealing with user-input. Handle the click outside of the zombie, then the outside code gets to decide if the click is "used up".
class ZombieChars():
[ ... ]
def death( self, mouse_position ):
killed = False
global zombieKilled
if self.rect.collidepoint( mouse_position ):
self.rect.x = 5000
self.rect.x -= self.x_change
zombieHit.play()
zombieKilled += 1
print(zombieKilled)
killed = True
return killed
Then in your main loop, stop processing hits once the first is found:
### Main Loop
while not exiting:
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
exiting = True
elif ( event.type == pygame.MOUSEBUTTONDOWN ):
gunShot.play()
mouse_pos = event.pos
for zombie in my_list:
if ( zombie.death( mouse_pos ) ):
break # stop on first hit
Python 3.
Hello. I made a game which starts off with a main menu and when 'd' is pressed, it will cut to the game screen.
Before I made this main menu, when I would hold space bar, the shapes would rumble. Now when I press 'd' to start the game, the objects are displayed, but holding space bar doesn't do anything, and neither does pressing escape or closing the game. It seems like the keyboard events / game events are not being called anymore once the 'd' is pressed.
Code:
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)
startGame = True
# Image Loading
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
def text_objects(text, font):
textSurface = font.render(text, True, BLACK)
return textSurface, textSurface.get_rect()
def game_intro():
global event
intro = True
keys = pygame.key.get_pressed()
while intro:
for event in pygame.event.get():
print(event)
if event.type == pygame.QUIT:
pygame.quit()
quit()
screen.fill(WHITE)
largeText = pygame.font.Font('freesansbold.ttf', 45)
smallText = pygame.font.Font('freesansbold.ttf', 30)
TextSurf, TextRect = text_objects("Welcome to Crazy Rumble.", largeText)
TextRect.center = ((700 / 2), (100 / 2))
TextSurff, TextRectt = text_objects("Press enter to start", smallText)
TextRectt.center = ((700 / 2), (900 / 2))
TextStart, TextRecttt = text_objects("Hold space to make the shapes shake!", smallText)
TextRecttt.center = ((700 / 2), (225 / 2))
screen.blit(TextSurf, TextRect)
screen.blit(TextSurff, TextRectt)
screen.blit(TextStart, TextRecttt)
pygame.display.update()
if event.type == pygame.KEYUP:
intro = False
startGame = True
global intro
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:
game_intro()
game_intro = True
if event.type == pygame.KEYUP:
game_intro = False
keys = pygame.key.get_pressed()
# --- Main event loop
while game_intro == False:
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]:
print(up)
print(intensity)
up += 1
if up % 10 == 0:
a -= 1
b -= -1
else:
a, b = -4, 5
pygame.display.flip()
clock.tick(60)
You're just setting keys once with
keys = pygame.key.get_pressed()
You need to put that call inside the loop, so it gets updated after every event.
I am currently building a mockup of space invaders.
I want to make the user shoot stuff and the invaders will die (1hit kill), but I can't seem to figure out how to do that. I have tried drawing a small rectangle, but then the background paints over it. Thanks for your help!
import pygame, sys, random
from pygame.locals import *
# set up pygame
pygame.init()
mainClock = pygame.time.Clock()
# set up the window
windowwidth = 800
windowheight = 700
windowSurface = pygame.display.set_mode((windowwidth, windowheight), 0, 32)
pygame.display.set_caption('Sp#c3 inv#d3r5')
# set up movement variables
moveLeft = False
moveRight = False
moveUp = False
moveDown = False
# set up direction variables
DOWNLEFT = 1
DOWNRIGHT = 3
UPLEFT = 7
UPRIGHT = 9
LEFT = 4
RIGHT = 6
UP = 8
DOWN = 2
MOVESPEED = 10
MOVE = 2
# set up counting
score = 0
# set up the colors
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLUE = (135, 206, 250)
# set up font
font = pygame.font.SysFont(None, 50)
def makeship(windowwidth): # set up the bouncer and food data structures
user = pygame.Rect(350, 600, 100, 25)
return user
def drawwalls(walls):
for i in range(6):
wall = pygame.Rect(0,i+1,100,25)
pygame.draw.rect(windowSurface,GREEN,wall)
walls.append(wall)
def makeinvaders(invaders):
y = 0
for i in invaders: # each row
x = 0
for j in range(10):
# create invader
invader = pygame.Rect(75+x, 100+y, 40, 25)
# append invader to invaders[row]
i.append(invader)
# increase invader's x attribute by 50
x += 60
# increase invaders y by 35
y += 45
return invaders
def movepaddle(user):
# move the paddle
if moveLeft and user.left > 0:
user.left -= MOVESPEED
if moveRight and user.right < windowwidth:
user.right += MOVESPEED
return user
def moveinvaders(invaders, invdir):
# move the invaders
for row in invaders:
for invader in row:
if invdir == RIGHT and invaders[1][9].right < windowwidth:
invader.right += MOVE
elif invaders[1][9].right > windowwidth:
invader.left -= MOVE
invdir = LEFT
if invdir == LEFT and invaders[0][0].left > 0:
invader.left -= MOVE
elif invaders[0][0].left < 0:
invader.right += MOVE
invdir = RIGHT
return invdir
def shootbullets(windowSurface,blue2):
x = pygame.Rect(400,595,2,5)
pygame.draw.rect(windowSurface,GREEN,x)
def movebullets(bullets):
for bullet in bullets:
pass
def drawstuff(user, invaders):
# draw the user onto the surface
pygame.draw.rect(windowSurface, BLACK, user)
for i in invaders:
for invader in i:
if invader in invaders[0]:
pygame.draw.rect(windowSurface, BLACK, invader)
elif invader in invaders[1]:
pygame.draw.rect(windowSurface, BLACK, invader)
elif invader in invaders[2]:
pygame.draw.rect(windowSurface, BLACK, invader)
elif invader in invaders[3]:
pygame.draw.rect(windowSurface, BLACK, invader)
elif invader in invaders[4]:
pygame.draw.rect(windowSurface, BLACK, invader)
invaders = [[],[],[],[],[]]
invdir = LEFT
walls = []
drawwalls(walls)
# make the figures
user = makeship(windowwidth)
# make invaders
invaders = makeinvaders(invaders)
# run the game loop
while True:
# draw the black background onto the surface
windowSurface.fill(WHITE)
# check for the QUIT event
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
# change the keyboard variables
if event.key == K_LEFT or event.key == ord('a'):
moveRight = False
moveLeft = True
if event.key == K_RIGHT or event.key == ord('d'):
moveLeft = False
moveRight = True
if event.key == K_SPACE:
shootbullets(windowSurface,RED)
if event.type == KEYUP:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
if event.key == K_LEFT or event.key == ord('a'):
moveLeft = False
if event.key == K_RIGHT or event.key == ord('d'):
moveRight = False
# draw stuff
drawstuff(user, invaders)
# move invaders
invdir = moveinvaders(invaders, invdir)
# paddle movement
user = movepaddle(user)
# draw the window onto the screen
pygame.display.update()
mainClock.tick(80)
Take a look at this minimal space invaders-like game I've written for another question:
import pygame
# you'll be able to shoot every 450ms
RELOAD_SPEED = 450
# the foes move every 1000ms sideways and every 3500ms down
MOVE_SIDE = 1000
MOVE_DOWN = 3500
screen = pygame.display.set_mode((300, 200))
clock = pygame.time.Clock()
pygame.display.set_caption("Micro Invader")
# create a bunch of events
move_side_event = pygame.USEREVENT + 1
move_down_event = pygame.USEREVENT + 2
reloaded_event = pygame.USEREVENT + 3
move_left, reloaded = True, True
invaders, colors, shots = [], [] ,[]
for x in range(15, 300, 15):
for y in range(10, 100, 15):
invaders.append(pygame.Rect(x, y, 7, 7))
colors.append(((x * 0.7) % 256, (y * 2.4) % 256))
# set timer for the movement events
pygame.time.set_timer(move_side_event, MOVE_SIDE)
pygame.time.set_timer(move_down_event, MOVE_DOWN)
player = pygame.Rect(150, 180, 10, 7)
while True:
clock.tick(40)
if pygame.event.get(pygame.QUIT): break
for e in pygame.event.get():
if e.type == move_side_event:
for invader in invaders:
invader.move_ip((-10 if move_left else 10, 0))
move_left = not move_left
elif e.type == move_down_event:
for invader in invaders:
invader.move_ip(0, 10)
elif e.type == reloaded_event:
# when the reload timer runs out, reset it
reloaded = True
pygame.time.set_timer(reloaded_event, 0)
for shot in shots[:]:
shot.move_ip((0, -4))
if not screen.get_rect().contains(shot):
shots.remove(shot)
else:
hit = False
for invader in invaders[:]:
if invader.colliderect(shot):
hit = True
i = invaders.index(invader)
del colors[i]
del invaders[i]
if hit:
shots.remove(shot)
pressed = pygame.key.get_pressed()
if pressed[pygame.K_LEFT]: player.move_ip((-4, 0))
if pressed[pygame.K_RIGHT]: player.move_ip((4, 0))
if pressed[pygame.K_SPACE]:
if reloaded:
shots.append(player.copy())
reloaded = False
# when shooting, create a timeout of RELOAD_SPEED
pygame.time.set_timer(reloaded_event, RELOAD_SPEED)
player.clamp_ip(screen.get_rect())
screen.fill((0, 0, 0))
for invader, (a, b) in zip(invaders, colors):
pygame.draw.rect(screen, (150, a, b), invader)
for shot in shots:
pygame.draw.rect(screen, (255, 180, 0), shot)
pygame.draw.rect(screen, (180, 180, 180), player)
pygame.display.flip()
It uses colliderect to detect collisions, which your code is missing.
Also, the code in general and especially the movement code is a lot simpler than yours; so maybe it can serve as an inspiration for you.