So, I was watching a video from youtube on making the game Snake on Python with the pygame module.. and it was quite confusing.. and now I'm having an issue.. The pygame window opened and closed suddenly and there was no errors in the output.
I ran this pile of code :
import os
os.environ['DISPLAY'] = ': 0.0'
import pygame
pygame.init()
import random
from enum import Enum
from collections import namedtuple
font = pygame.font.Font('PermanentMarker-Regular.ttf', 25)
#font = pygame.font.SysFont('arial', 25)
class Direction(Enum):
RIGHT = 1
LEFT = 2
UP = 3
DOWN = 4
Point = namedtuple('Point', ('x','y'))
WHITE = (255, 255, 255)
RED = (247,33,25)
BLUE = (0,0,255)
CYAN = (255, 255, 1)
BLACK = [0,0,0]
BLOCK_SIZE = 20
SPEED = 40
class SnakeGame:
def __init__(self, w=640, h=480):
self.w = w
self.h = h
# init display
self.display = pygame.display.set_mode((self.w, self.h))
pygame.display.set_caption("Snake")
self.clock = pygame.time.Clock()
# init game state
self.direction = Direction.RIGHT
self.head = Point(self.w/2, self.h/2)
self.snake = [self.head, Point(self.head.x-BLOCK_SIZE, self.head.y), Point(self.head.x-(2*BLOCK_SIZE), self.head.y)]
self.score = 0
self.food = None
self._place_food()
def _place_food(self):
x = random.randint(0, (self.w-BLOCK_SIZE)//BLOCK_SIZE) * BLOCK_SIZE
y = random.randint(0, (self.h-BLOCK_SIZE)//BLOCK_SIZE) * BLOCK_SIZE
self.food = Point(x, y)
if self.food in self.snake:
self._place_food()
self.head = Point(x,y)
def _move(self, direction):
x = self.head.x
y = self.head.y
if direction == Direction.RIGHT:
x += BLOCK_SIZE
elif direction == Direction.LEFT:
x -= BLOCK_SIZE
elif direction == Direction.UP:
y -= BLOCK_SIZE
elif direction == Direction.DOWN:
y += BLOCK_SIZE
def _is_collision(self):
# hits boundary
if self.head.x > self.w - BLOCK_SIZE or self.head.x < 0 or self.head.y > self.h - BLOCK_SIZE or self.head.y < 0:
return True
# hits itself
if self.head in self.snake[1:]:
return True
return False
pygame.display.flip()
def play_step(self):
# collect user input
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
self.direction = Direction.LEFT
elif event.key == pygame.K_RIGHT:
self.direction = Direction.RIGHT
elif event.key == pygame.K_UP:
self.direction = Direction.UP
elif event.key == pygame.K_DOWN:
self.direction = Direction.DOWN
#2. move
self._move(self.direction)
self.snake.insert(0, self.head)
#3. check if game over
game_over = False
if self._is_collision():
game_over = True
#4. place new food or move snake
if self.head == self.food:
self.score += 1
self._place_food()
else:
self.snake.pop()
#5. update ui and clock
self._update_ui()
self.clock.tick(SPEED)
#6. return game over and score
return game_over, self.score
def _update_ui(self):
self.display.fill(BLACK)
for pt in self.snake:
pygame.draw.rect(self.display, CYAN, pygame.Rect(pt.x, pt.y, BLOCK_SIZE, BLOCK_SIZE))
pygame.draw.rect(self.display, BLUE, pygame.Rect(pt.x+4, pt.y+4, 12, 12))
pygame.draw.rect(self.display, RED, pygame.Rect(self.food.x, self.food.y, BLOCK_SIZE, BLOCK_SIZE))
text = font.render("Score:", str(self.score), True, WHITE)
self.display.blit(text, [0,0])
if __name__ == '__main__':
game = SnakeGame()
# game loop
while True:
game_over, score = game.play_step()
if game_over == True:
break
print("Final score {}".format(score))
# break if game over
pygame.quit()
The issue is probably because I misplaced a method or did something in the script..
There are 4 issues:
Update the head attribute after in the move() method:
class SnakeGame:
# [...]
def _move(self, direction):
x = self.head.x
y = self.head.y
if direction == Direction.RIGHT:
x += BLOCK_SIZE
elif direction == Direction.LEFT:
x -= BLOCK_SIZE
elif direction == Direction.UP:
y -= BLOCK_SIZE
elif direction == Direction.DOWN:
y += BLOCK_SIZE
self.head = Point(x, y) # <--- this is missing
You have to concatenate the strings for the score text (see TypeError: Invalid foreground RGBA argument):
text = font.render("Score:", str(self.score), True, WHITE)
text = font.render("Score:" + str(self.score), True, WHITE)
Update the display after drawing the scene:
def _update_ui(self):
self.display.fill(BLACK)
for pt in self.snake:
pygame.draw.rect(self.display, CYAN, pygame.Rect(pt.x, pt.y, BLOCK_SIZE, BLOCK_SIZE))
pygame.draw.rect(self.display, BLUE, pygame.Rect(pt.x+4, pt.y+4, 12, 12))
pygame.draw.rect(self.display, RED, pygame.Rect(self.food.x, self.food.y, BLOCK_SIZE, BLOCK_SIZE))
text = font.render("Score:" + str(self.score), True, WHITE)
self.display.blit(text, [0,0])
pygame.display.flip() # <--- this is missing
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.
if __name__ == '__main__':
game = SnakeGame()
# game loop
clock = pygame.time.Clock()
while True:
clock.tick(10)
game_over, score = game.play_step()
if game_over == True:
break
print("Final score {}".format(score))
Related
I wrote simple snake game. Code here:
import pygame
import random
from collections import deque
import time
DISPLAY_WIDTH = 100
DISPLAY_HEIGHT = 100
HEAD_SIZE = 10
FOOD_SIZE = HEAD_SIZE
TAIL_SIZE = 5
BLACK = (0,0,0)
WHITE = (255,255,255)
ONE_MOVE = 5
pygame.init()
gameDisplay = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
pygame.display.set_caption("Snake")
clock = pygame.time.Clock()
class Snake:
def __init__(self):
self.x = 0
self.y = 0
self.snake_lenght = 0
self.tail = []
def draw_head(self):
pygame.draw.rect(gameDisplay, BLACK, [self.x, self.y, HEAD_SIZE, HEAD_SIZE])
def draw_tail(self):
self.tail.append([self.x, self.y])
for i in self.tail:
pygame.draw.rect(gameDisplay, BLACK, [int(i[0]), int(i[1]), TAIL_SIZE, TAIL_SIZE])
def rise(self):
foodx = random.randrange(ONE_MOVE, DISPLAY_WIDTH - ONE_MOVE, 5) # random food in a distance from the wall
foody = random.randrange(ONE_MOVE, DISPLAY_HEIGHT - ONE_MOVE, 5)
for i in self.tail:
if foodx == i[0] and foody == i[1]: # avoid food in tail
self.rise()
break
self.snake_lenght += 20
return foodx, foody, self.snake_lenght
def direction_of_movement(self, direction):
if direction == "turn_right":
return [ONE_MOVE, 0, 0, 0]
elif direction == "turn_left":
return [0, ONE_MOVE, 0, 0]
elif direction == "turn_down":
return [0, 0, ONE_MOVE, 0]
elif direction == "turn_up":
return [0, 0, 0, ONE_MOVE]
def crash(self): # restart game after failing
time.sleep(2)
self.game_loop()
def game_loop(self):
self.x = (DISPLAY_WIDTH * 0.5)
self.y = (DISPLAY_HEIGHT * 0.5)
start = 0
self.snake_lenght = 10 # reset snake's lenght after calling game_loop again
self.tail = deque([], maxlen=self.snake_lenght)
while True:
if start == 0: # start to move
speed = self.direction_of_movement("turn_right")
foodx, foody, self.snake_lenght = self.rise()
start += 1
for event in pygame.event.get(): # quit if exit pressed
if event.type == pygame.QUIT:
quit()
if event.type == pygame.KEYDOWN: # arrow navigation
if event.key == pygame.K_RIGHT:
speed = self.direction_of_movement("turn_right")
elif event.key == pygame.K_LEFT:
speed = self.direction_of_movement("turn_left")
elif event.key == pygame.K_DOWN:
speed = self.direction_of_movement("turn_down")
elif event.key == pygame.K_UP:
speed = self.direction_of_movement("turn_up")
# game logic
self.x += speed[0] #moving directions
self.x -= speed[1]
self.y += speed[2]
self.y -= speed[3]
if self.x < 0 or self.x + ONE_MOVE > DISPLAY_WIDTH: # hit into wall
self.crash()
elif self.y < 0 or self.y + ONE_MOVE > DISPLAY_HEIGHT:
self.crash()
for i in self.tail: # bite itself
if self.x == i[0]:
if self.y == i[1]:
self.crash()
if self.x == foodx and self.y == foody:
foodx, foody, self.snake_lenght = self.rise()
self.tail = deque(self.tail, maxlen=self.snake_lenght)
# draw
gameDisplay.fill(WHITE)
self.draw_head()
self.draw_tail()
pygame.draw.rect(gameDisplay, BLACK, [foodx, foody, FOOD_SIZE, FOOD_SIZE])
pygame.display.update()
clock.tick(10) # game speed
snake = Snake()
snake.game_loop()
In another file I'm importing whole class, adding constants and run it. In shortcut:
from snake_file import Snake
class Snake_for_AI(Snake):
def __init__(self):
pass
def trying(self):
print("hey")
thing = Snake_for_AI()
thing.trying()
Here is my question. It's never printing "hey". Moreover, it runs game_loop and starts the game when I create class object thing = Snake_for_AI, no need to call thing.game_loop(). Why?
Any code review is very appreciated too.
When importing, you will execute the two lines at the bottom of the file. If that is not the desired behavior, surround them as such:
if __name__ == "__main__":
snake = Snake()
snake.game_loop()
That will cause them to be interpreted only if you actually run that file.
as part of my Pygame learning course I have to create simple helicopter game, similar to flappy bird.
It also should have a score count - so everytime you pass gap between blocks with helicopter the score should add 1.
Game logic and everything else works fine, except everytime helicopter pases thru the blocks the score is not added, so all the time score shows 0.
Below is the code, can anyone please help
import pygame
import time
from random import randint
black = (0,0,0)
white = (255,255,255)
pygame.init()
surfaceWidth = 1280
surfaceHeight = 720
imageHeight = 30
imageWidth = 60
surface = pygame.display.set_mode((surfaceWidth,surfaceHeight))
pygame.display.set_caption('EVGENY GAME')
#clock = frames per second
clock = pygame.time.Clock()
img = pygame.image.load('Helicopter.jpg')
def score(count):
font = pygame.font.Font('freesansbold.ttf', 20)
text = font.render("Score: "+str(count), True, white)
surface.blit(text, [0,0])
def blocks(x_block, y_block, block_width, block_height, gap):
pygame.draw.rect(surface, white, [x_block, y_block, block_width, block_height])
pygame.draw.rect(surface, white, [x_block, y_block+block_height+gap, block_width, surfaceHeight])
def replay_or_quit():
for event in pygame.event.get([pygame.KEYDOWN, pygame.KEYUP, pygame.QUIT]):
if event.type == pygame.QUIT:
pygame.quit()
quit()
elif event.type == pygame.KEYDOWN:
continue
return event.key
return None
def makeTextObjs(text, font):
textSurface = font.render(text, True, white)
return textSurface, textSurface.get_rect()
def msgSurface(text):
smallText = pygame.font.Font('freesansbold.ttf', 20)
largeText = pygame.font.Font('freesansbold.ttf', 150)
titleTextSurf, titleTextRect = makeTextObjs(text, largeText)
titleTextRect.center = surfaceWidth / 2, surfaceHeight / 2
surface.blit(titleTextSurf, titleTextRect)
typTextSurf, typTextRect = makeTextObjs('Press any key to continue', smallText)
typTextRect.center = surfaceWidth / 2, ((surfaceHeight / 2) + 100)
surface.blit(typTextSurf, typTextRect)
pygame.display.update()
time.sleep(1)
while replay_or_quit() == None:
clock.tick()
main()
def gameover():
msgSurface('GAME OVER')
def helicopter(x, y, image):
surface.blit(img, (x,y,))
# first constance = game over, while not true we are playing game
def main():
x = 50
y = 100
y_move = 0
x_block = surfaceWidth
y_block = 0
block_width = 100
block_height = randint(0, surfaceHeight/2)
gap = imageHeight * 8
block_move = 3
current_score = 0
game_over = False
while not game_over:#this will keep running until it hits pygame.quit
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_over = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
y_move = -10
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
y_move = 5
y += y_move
surface.fill(black)
helicopter(x,y,img)
score(current_score)
blocks(x_block, y_block, block_width, block_height, gap)
x_block -= block_move
if y > surfaceHeight-40 or y < 0:
gameover()
if x_block < (-1*block_width):
x_block = surfaceWidth
block_height = randint(0, surfaceHeight/2)
if x + imageWidth > x_block:
if x < x_block + block_width:
if y < block_height:
if x - imageWidth < block_width + x_block:
gameover()
if x + imageWidth > x_block:
if y + imageHeight > block_height+gap:
gameover()
if x < x_block and x > x_block-block_move:
current_score += 1
pygame.display.update()#update, updates specific area on display, flip - updates whole display
clock.tick(100)#100 frames per second
main()
pygame.quit()
quit() enter code here
Your score counter never updates because your score counter condition is never reached. Change
if x < x_block and x > x_block-block_move:
to
if x_block >= x > x_block-block_move:
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")
This question already has answers here:
How to get keyboard input in pygame?
(11 answers)
How can I make a sprite move when key is held down
(6 answers)
Closed 2 years ago.
I'm starting a grid based game in Pygame, and I've gotten to the point where I have a grid on the screen, and I'm trying to let the user use the arrow keys to move the grid around. I tried to do it by adding a method to my Grid Object, but it doesn't seem to be doing anything. Help would be appreciated
#IMPORTING MODULES
import pygame
from pygame.locals import *
import random
import sys
white = (250,250,250)
black = (0,0,0)
#SETTING UP STUFF
pygame.init()
#ADDING FONTS AND TEXT
myfont = pygame.font.SysFont("monospace", 25)
title = myfont.render("X-COM: UFO DEFENSE", 1, (black))
#MORE SETTING UP
display_width = 800
display_height = 600
SIZE = 25
keys = [False,False,False,False]
scene = "start"
screen = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption('X-COM UFO DEFENSE')
#SETTING UP BUTTON CLASS
class Button(object):
def __init__(self, x, y, width, height,text,touching,clicked):
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
self.rect = pygame.Rect([int(self.x),int(self.y),int(self.width),int(self.height)])
self.touching = touching
self.clicked = clicked
def draw(self):
#DRAW FUNCTION(MAY NEED WORK ON THE TEXT FORMULA)
text1 = myfont.render(str(self.text), 1, (black))
pygame.draw.rect(screen,[255,255,255],self.rect,0)
screen.blit(text1, (self.x + self.width/3,self.y + self.height/3))
def click(self):
#EASY CLICK DETECTION
if self.rect.collidepoint(pygame.mouse.get_pos()):
self.touching = True
else:
self.touching = False
#Creating GRID class
class GridSquare(object):
def __init__(self, internal_row, internal_column, touching,clicked):
self.internal_row = internal_row
self.internal_column = internal_column
self.touching = touching
self.clicked = clicked
self.right = 0
self.up = 0
self.rect = pygame.Rect([int(self.internal_row),int(self.internal_column),25,25])
def draw(self):
pygame.draw.rect(screen,[255,0,255],self.rect,0)
def camera(self):
self.internal_row = self.right+self.internal_row
self.internal_column = self.up+self.internal_column
if keys[0] == True:
self.up += 1
elif keys[1] == True:
self.right -= 1
elif keys[2] == True:
self.up -= 1
elif keys[3] == True:
self.right += 1
#BUTTONS
grid = []
for i in range(100,200,26):
for k in range(100,200,26):
grid.append(GridSquare(i,k,False,False))
titlescreen = Button(300,300,200,100,"Start",False,False)
#MAIN LOOP STARTS HERE
while True:
if scene == "start":
screen.fill(white)
screen.blit(title, (300,50))
titlescreen.draw()
titlescreen.click()
#EVENTS
for event in pygame.event.get():
if titlescreen.touching == True and event.type == pygame.MOUSEBUTTONDOWN:
titlescreen.clicked = True
else:
titlescreen.clicked = False
if event.type == KEYDOWN:
if event.key == pygame.K_w:
keys[0] = True
elif event.key == pygame.K_a:
keys[1] = True
elif event.key == pygame.K_s:
keys[2] = True
elif event.key == pygame.K_d:
keys[3] = True
if titlescreen.clicked == True:
scene = "game"
if scene == "game":
screen.fill(black)
for grids in grid:
grids.draw()
for grids in grid:
grids.camera()
pygame.display.flip()
I am currently making a top down racing game and need a way to detect when a vehicle has completed a full lap. I have chosen to do this by adding images around the circuit, acting as checkpoints, which match with the track surface. When driven over, they output true, all must have output true in order for a lap to count. However, I cannot find a way to detect a collision between my vehicles and an image.
I have tried adding rects to the vehicles and checking if an output can be produced when the two vehicles collide but I just get this error:
AttributeError: 'pygame.Surface' object has no attribute 'rect'
Is there any way I can do this? My code can be seen below.
import pygame
from pygame.locals import *
import math
import time
pygame.init()
F1image = pygame.image.load("F1image.png")
sportsimage = pygame.image.load("sportsimage.png")
bikeimage = pygame.image.load("bikeimage.png")
muscleimage = pygame.image.load("muscleimage.png")
truckimage = pygame.image.load("truckimage.png")
screen = pygame.display.set_mode((1280,720))
xpos = 280
xpos_2 = 280
ypos = 50
ypos_2 = 85
keys = [False, False, False, False]
keys_2 = [False, False, False, False]
direction = 0
direction_2 = 0
forward = 0
forward_2 = 0
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
BackGround = Background('track.png', [0,0])
class Vehicle:
'Base class for all vehicles (Cars and Motorbikes) in the game'
vehicleCount = 0
def __init__(self, max_speed, acceleration, turning_radius, image):
pygame.sprite.Sprite.__init__(self)
self.max_speed = max_speed
self.acceleration = acceleration
self.turning_radius = turning_radius
self.image = image
self.rect = self.image.get_rect()
Vehicle.vehicleCount = Vehicle.vehicleCount + 1
def displayAmount():
print ("Total number of Vehicle enteries: ", Vehicle.vehicleCount)
def displayVehicle(self):
print ("max speed: ", self.max_speed, "acceleration: ", self.acceleration, "turning radius: ", self.turning_radius)
def checkCollision(self, sprite1, sprite2):
col = pygame.sprite.collide_rect(sprite1, sprite2)
if col == True:
print ("True")
F1 = Vehicle(5.0, 0.1, 2.84, F1image)
sportscar = Vehicle(4.5, 0.2, 2.01, sportsimage)
bike = Vehicle(4.0, 0.15, 2.64, bikeimage)
musclecar = Vehicle(3.5, 0.25, 1.76, muscleimage)
truck = Vehicle(3.0, 0.3, 1.20, truckimage)
print (F1.max_speed)
player1choice = input("Input player 1 choice").lower()
player2choice = input("Input player 2 choice").lower()
if player1choice == ("f1"):
choice1 = F1
elif player1choice == ("sports"):
choice1 = sportscar
elif player1choice == ("muscle"):
choice1 = musclecar
elif player1choice == ("truck"):
choice1 = truck
else:
choice1 = bike
if player2choice == ("f1"):
choice2 = F1
elif player2choice == ("sports"):
choice2 = sportscar
elif player2choice == ("muscle"):
choice2 = musclecar
elif player2choice == ("truck"):
choice2 = truck
else:
choice2 = bike
running = True
while running:
pygame.display.set_caption("Speed Wars")
WHITE = (255, 255, 255)
screen.fill(WHITE)
screen.blit(BackGround.image, BackGround.rect)
#Vehicle 1
if keys[0] == True:
direction += (choice1).turning_radius
if keys[1] == True:
direction -= (choice1).turning_radius
if keys[2] == True and forward <= (choice1).max_speed:
forward += (choice1).acceleration
if keys[3] == True and forward >= 0:
forward -= (choice1).acceleration
#Vehicle 2
if keys_2[0] == True:
direction_2 += (choice2).turning_radius
if keys_2[1] == True:
direction_2 -= (choice2).turning_radius
if keys_2[2] == True and forward_2 <= (choice2).max_speed:
forward_2 += (choice2).acceleration
if keys_2[3] == True and forward_2 >= 0:
forward_2 -= (choice2).acceleration
movex = math.cos(direction / 57.29) * forward
movey = math.sin(direction / 57.29) * forward
xpos += movex
ypos -= movey
movex_2 = math.cos(direction_2 / 57.29) * forward_2
movey_2 = math.sin(direction_2 / 57.29) * forward_2
xpos_2 += movex_2
ypos_2 -= movey_2
rotation = pygame.transform.rotate((choice1).image, direction)
rotation_2 = pygame.transform.rotate((choice2).image, direction_2)
screen.blit(rotation, (xpos, ypos))
screen.blit(rotation_2, (xpos_2, ypos_2))
pygame.display.flip()
time.sleep(0.01)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit(0)
if event.type == pygame.KEYDOWN:
if event.key == K_LEFT:
keys[0] = True
elif event.key == K_RIGHT:
keys[1] = True
elif event.key == K_UP:
keys[2] = True
elif event.key == K_DOWN:
keys[3] = True
if event.key == K_a:
keys_2[0] = True
elif event.key == K_d:
keys_2[1] = True
elif event.key == K_w:
keys_2[2] = True
elif event.key == K_s:
keys_2[3] = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
keys[0] = False
elif event.key == pygame.K_RIGHT:
keys[1] = False
elif event.key == pygame.K_UP:
keys[2] = False
elif event.key == pygame.K_DOWN:
keys[3] = False
if event.key == pygame.K_a:
keys_2[0] = False
elif event.key == pygame.K_d:
keys_2[1] = False
elif event.key == pygame.K_w:
keys_2[2] = False
elif event.key == pygame.K_s:
keys_2[3] = False
#Collision detection
(choice1).checkCollision((choice2).image, (choice1).image)
The issue is that your code passes in two images into the checkCollision method in your Vehicle class. Then you are passing these two images into the collide_rect function which expects two Sprites.
As a result, you get an error telling you that the two objects passed in(Surfaces in this case), do not contain rects.
To fix this problem:
Use the superclass Sprite for your Vehicle class.
Simply pass in the other sprite into the checkCollision method.
As a result, your checkCollision function should look something like this:
def checkCollision(self, sprite2):
col = pygame.sprite.collide_rect(self, sprite2)
if col == True:
print ("True")
And the call to it should look something like this:
choice1.checkCollision(choice2)
Also, your Vehicle class header should look like this:
class Vehicle(pygame.sprite.Sprite)
Some other issues in your code that should be fixed:
You are receiving input from the keyboard. This is very weird in a game. Instead, you should look at handling this through keyboard input.
You use brackets around choice1 and choice2. This is not needed.
There is code in your main game loop that doesn't need to be ran every frame such as pygame.display.set_caption(). This again is not needed and code like this should go before the main game loop.
The order of you main game loop is different to how it is normally done. First, you should do event handling. Second, do your logic and lastly, do your rendering.
Also, you are making 5 objects and loading many images where only two will be used. Instead, create and load the objects that will be used in the game after the user has decided which car they will play as.
Never use time.sleep() inside a pygame script. This function is evil when used with pygame and causes many errors and bugs. If you would like to use a framerate cap then use a Clock.
I highly recommend you follow these items.
I hope this answer helped you and if you have any further questions please feel free to post a comment below!
To implement checkpoints in a game I would use a solution similar to this: Define a list, group, etc. which contains your checkpoints and set the start point and the active point to the first checkpoint in the list. You can use an itertools.cycle iterator to easily cycle through the points. When the player touches a checkpoint, you set the active_checkpoint to the next point in the iterator and check if it was the start point, if yes, increment your laps counter.
If you want pixel-perfect collision detection, you can give the sprites a self.mask attribute and use pygame.sprite.collide_mask.
Here's a simplified example. I just swap out the images of the sprites here to show which one is active.
import itertools
import pygame as pg
CHECKPOINT_IMG = pg.Surface((120, 20), pg.SRCALPHA)
CHECKPOINT_IMG.fill((120, 60, 0))
CHECKPOINT2_IMG = pg.Surface((120, 20), pg.SRCALPHA)
CHECKPOINT2_IMG.fill((220, 110, 0))
class Player(pg.sprite.Sprite):
def __init__(self, pos, checkpoints):
super().__init__()
self.image = pg.Surface((60, 60), pg.SRCALPHA)
pg.draw.polygon(self.image, (0, 100, 240), [(30, 0), (60, 60), (0, 60)])
self.rect = self.image.get_rect(center=pos)
self.mask = pg.mask.from_surface(self.image)
self.checkpoints = itertools.cycle(checkpoints)
self.active_checkpoint = next(self.checkpoints)
self.start_point = self.active_checkpoint
self.active_checkpoint.image = self.active_checkpoint.image_active
self.laps = -1 # I start at -1 because the start is the first checkpoint.
def handle_event(self, event):
if event.type == pg.MOUSEMOTION:
self.rect.center = event.pos
if pg.sprite.collide_mask(self, self.active_checkpoint):
if self.active_checkpoint == self.start_point: # Completed a round.
self.laps += 1
pg.display.set_caption('Laps: {}'.format(self.laps))
# I change the images of the previous and next checkpoint
# to show which one is active.
self.active_checkpoint.image = self.active_checkpoint.image_inactive
# Switch to the next checkpoint.
self.active_checkpoint = next(self.checkpoints)
self.active_checkpoint.image = self.active_checkpoint.image_active
class Checkpoint(pg.sprite.Sprite):
def __init__(self, pos, angle=0):
super().__init__()
self.image_inactive = pg.transform.rotate(CHECKPOINT_IMG, angle)
self.image_active = pg.transform.rotate(CHECKPOINT2_IMG, angle)
self.image = self.image_inactive
self.rect = self.image.get_rect(center=pos)
self.mask = pg.mask.from_surface(self.image)
class Game:
def __init__(self):
self.screen = pg.display.set_mode((640, 480))
self.done = False
self.clock = pg.time.Clock()
self.checkpoints = (
Checkpoint((100, 200), 0),
Checkpoint((300, 100), 60),
Checkpoint((500, 300), 10),
Checkpoint((200, 300), 30),
)
self.player = Player((20, 20), self.checkpoints)
self.all_sprites = pg.sprite.Group(self.player)
self.all_sprites.add(self.checkpoints)
def run(self):
while not self.done:
self.event_loop()
self.update()
self.draw()
pg.display.flip()
self.clock.tick(60)
def event_loop(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
self.player.handle_event(event)
def update(self):
pass
def draw(self):
self.screen.fill((30, 30, 30))
self.all_sprites.draw(self.screen)
if __name__ == '__main__':
pg.init()
game = Game()
game.run()
pg.quit()