Problem
So, i'm trying to render text that holds the player's score, but when I writ the code, it doesn't work.
Python Code
class Player(pygame.sprite.Sprite):
def __init__(self, x, color):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((10, 100))
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.y = HEIGHT / 2 - self.rect.height / 2
self.rect.centerx = x
self.displacement = 8
self.score = 0
def update(self):
# update function
self.constrain(0, HEIGHT - self.rect.height)
def constrain(self, min, max):
# constrian player to walls
if self.rect.y > max:
self.rect.y = max
if self.rect.y < min:
self.rect.y = min
class Text():
def __init__(self, x, y, color, text, font):
self.x = x
self.y = y
self.color = color
self.text = text
self.font = font
def draw(self):
text = self.font.render(self.text, False, self.color)
text.blit(text, (self.x, self.y))
class Game(pygame.sprite.Sprite):
def __init__(self):
# get sprite object
pygame.sprite.Sprite.__init__(self)
# init pygame
pygame.init()
# init pygame modules
pygame.mixer.init()
pygame.font.init()
# set window
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
# set title
pygame.display.set_caption(TITLE)
# holds whether the game is running for not
self.running = True
# get timer function
self.clock = pygame.time.Clock()
# set fonts
self.scoreText = pygame.font.Font("assets/fonts/square.TTF", 25)
self.titleFontLight = pygame.font.Font("assets/fonts/square.TTF", 50)
# group for sprites
self.allSprites = pygame.sprite.Group()
def new(self):
# creates a new game
# make players
self.player1 = Player(20, RED)
self.player2 = Player(WIDTH - 30, BLUE)
self.ball = Ball()
self.p1Score = Text(100, 100, (RED), str(self.player1.score), self.scoreText)
self.p2Score = Text(WIDTH - 10, 10, (WHITE), str(self.player2.score), self.scoreText)
# put players inside groups
self.allSprites.add(self.player1)
self.allSprites.add(self.player2)
# run game loop
self.gameLoop()
def gameLoop(self):
# main game loop
while self.running:
self.allSprites.update()
self.update()
self.clock.tick(FPS)
def update(self):
# updates game
self.draw()
pygame.display.update()
def draw(self):
# draws to screen
# set background color
self.screen.fill(BLACK)
# draw to screen
self.ball.draw(self.screen)
self.p1Score.draw()
self.allSprites.draw(self.screen)
if __name__ == '__main__':
Game().new()
What I want to happen
I want text to display on the screen at the x and y coordinates that I put in my code.
What I Am Getting
Currently, the text is not being displayed, also there are no errors.
What I have tried
I have tried to put in the absolute path to the font, ex: C:/name/file/font.TTF
In your current code, this line is the problem:
text.blit(text, (self.x, self.y)). This code is drawing some text onto itself. You want to draw the text onto the screen. Replace that line with this line:
screen.blit(text, (self.x, self.y)).
This means that you need to have a screen argument for your Text's draw function. So def draw(self): should be def draw(self,screen):. Finally, you must pass the self.screen argument, so self.p1Score.draw() should be self.p1Score.draw(self.screen). The final code, with all changes made should be:
class Player(pygame.sprite.Sprite):
def __init__(self, x, color):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((10, 100))
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.y = HEIGHT / 2 - self.rect.height / 2
self.rect.centerx = x
self.displacement = 8
self.score = 0
def update(self):
# update function
self.constrain(0, HEIGHT - self.rect.height)
def constrain(self, min, max):
# constrian player to walls
if self.rect.y > max:
self.rect.y = max
if self.rect.y < min:
self.rect.y = min
class Text():
def __init__(self, x, y, color, text, font):
self.x = x
self.y = y
self.color = color
self.text = text
self.font = font
def draw(self,screen):
text = self.font.render(self.text, False, self.color)
screen.blit(text, (self.x, self.y))
class Game(pygame.sprite.Sprite):
def __init__(self):
# get sprite object
pygame.sprite.Sprite.__init__(self)
# init pygame
pygame.init()
# init pygame modules
pygame.mixer.init()
pygame.font.init()
# set window
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
# set title
pygame.display.set_caption(TITLE)
# holds whether the game is running for not
self.running = True
# get timer function
self.clock = pygame.time.Clock()
# set fonts
self.scoreText = pygame.font.Font("assets/fonts/square.TTF", 25)
self.titleFontLight = pygame.font.Font("assets/fonts/square.TTF", 50)
# group for sprites
self.allSprites = pygame.sprite.Group()
def new(self):
# creates a new game
# make players
self.player1 = Player(20, RED)
self.player2 = Player(WIDTH - 30, BLUE)
self.ball = Ball()
self.p1Score = Text(100, 100, (RED), str(self.player1.score), self.scoreText)
self.p2Score = Text(WIDTH - 10, 10, (WHITE), str(self.player2.score), self.scoreText)
# put players inside groups
self.allSprites.add(self.player1)
self.allSprites.add(self.player2)
# run game loop
self.gameLoop()
def gameLoop(self):
# main game loop
while self.running:
self.allSprites.update()
self.update()
self.clock.tick(FPS)
def update(self):
# updates game
self.draw()
pygame.display.update()
def draw(self):
# draws to screen
# set background color
self.screen.fill(BLACK)
# draw to screen
self.ball.draw(self.screen)
self.p1Score.draw(self.screen)
self.allSprites.draw(self.screen)
if __name__ == '__main__':
Game().new()
Related
My collide_rect function isn't working properly. It always returns True, when it's not suppose to. I have tried looking on the internet but nothing is working for me. I think the collide rect somehow did not use the actual coordinates for the two sprites. Can anyone help with this?
import pygame
import pygame.sprite
import sys
gameDisplay = pygame.display.set_mode((800,600))
pygame.display.set_caption("test_collision")
clock = pygame.time.Clock()
crashed = False
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect()
self.x = 280
self.y = 475
self.col = False
def update(self):
gameDisplay.blit(self.image, (self.x,self.y))
self.rect = self.image.get_rect()
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.x = 1000
self.y = 483
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect()
def change_x(self):
self.time = pygame.time.get_ticks()
self.x = -(self.time/5) + 800
def update(self):
self.rect = self.image.get_rect()
gameDisplay.blit(self.image,(self.x,self.y))
obstacle = Obstacle()
ball = Ball()
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
gameDisplay.fill((255,255,255))
ball.update()
obstacle.change_x()
obstacle.update()
ball.test_collisions(obstacle)
if ball.col:
print("colided")
pygame.display.flip()
clock.tick(1000)
pygame.quit()
sys.exit()
P.S This is my first post :)
pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, but it returns a rectangle that always starts at (0, 0) since a Surface object has no position.
The Surface is placed at a position on the display with the blit function.
You've to set the location of the rectangle, either by a keyword argument, e.g:
self.rect = self.image.get_rect(topleft = (self.x, self.y))
or an assignment to a virtual attribute (see pygame.Rect), e.g:
self.rect = self.image.get_rect()
self.rect.topleft = (self.x, self.y)
It is absolutely unnecessary to add some extra attributes self.x and self.y. Use the location of the rectangle instead. e.g:
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect(topleft = (280, 475))
self.col = False
def update(self):
gameDisplay.blit(self.image, self.rect)
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect(topleft = (1000, 483))
def change_x(self):
self.time = pygame.time.get_ticks()
self.rect.x = -(self.time/5) + 800
def update(self):
gameDisplay.blit(self.image, self.rect)
Further note, that you can get rid of the methods Ball.update() respectively Obstacle.update() (you can delete them), if you use a pygame.sprite.Group and call .draw(), which uses the .image and .rect properties of the contained sprites, to draw them. e.g.:
obstacle = Obstacle()
ball = Ball()
all_sprites = pygame.sprite.Group([obstacle, ball])
while not crashed:
# [...]
gameDisplay.fill((255,255,255))
all_sprites.draw(gameDisplay)
pygame.display.flip()
clock.tick(1000)
My collide_rect function isn't working properly. It always returns True, when it's not suppose to. I have tried looking on the internet but nothing is working for me. I think the collide rect somehow did not use the actual coordinates for the two sprites. Can anyone help with this?
import pygame
import pygame.sprite
import sys
gameDisplay = pygame.display.set_mode((800,600))
pygame.display.set_caption("test_collision")
clock = pygame.time.Clock()
crashed = False
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect()
self.x = 280
self.y = 475
self.col = False
def update(self):
gameDisplay.blit(self.image, (self.x,self.y))
self.rect = self.image.get_rect()
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.x = 1000
self.y = 483
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect()
def change_x(self):
self.time = pygame.time.get_ticks()
self.x = -(self.time/5) + 800
def update(self):
self.rect = self.image.get_rect()
gameDisplay.blit(self.image,(self.x,self.y))
obstacle = Obstacle()
ball = Ball()
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
gameDisplay.fill((255,255,255))
ball.update()
obstacle.change_x()
obstacle.update()
ball.test_collisions(obstacle)
if ball.col:
print("colided")
pygame.display.flip()
clock.tick(1000)
pygame.quit()
sys.exit()
P.S This is my first post :)
pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, but it returns a rectangle that always starts at (0, 0) since a Surface object has no position.
The Surface is placed at a position on the display with the blit function.
You've to set the location of the rectangle, either by a keyword argument, e.g:
self.rect = self.image.get_rect(topleft = (self.x, self.y))
or an assignment to a virtual attribute (see pygame.Rect), e.g:
self.rect = self.image.get_rect()
self.rect.topleft = (self.x, self.y)
It is absolutely unnecessary to add some extra attributes self.x and self.y. Use the location of the rectangle instead. e.g:
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect(topleft = (280, 475))
self.col = False
def update(self):
gameDisplay.blit(self.image, self.rect)
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect(topleft = (1000, 483))
def change_x(self):
self.time = pygame.time.get_ticks()
self.rect.x = -(self.time/5) + 800
def update(self):
gameDisplay.blit(self.image, self.rect)
Further note, that you can get rid of the methods Ball.update() respectively Obstacle.update() (you can delete them), if you use a pygame.sprite.Group and call .draw(), which uses the .image and .rect properties of the contained sprites, to draw them. e.g.:
obstacle = Obstacle()
ball = Ball()
all_sprites = pygame.sprite.Group([obstacle, ball])
while not crashed:
# [...]
gameDisplay.fill((255,255,255))
all_sprites.draw(gameDisplay)
pygame.display.flip()
clock.tick(1000)
I followed a PyGame tile-based tutorial for a project in school, but I never intended to make a game, but a simulation of an ecosystem. Unfortunately, when I run my program the performance is very bad and it only manages to run for a few seconds, before the windows to stop answering.
The only thing I want to do at the moment is to place a new patch of grass, when the energy of a grass patch reaches 80.
What is there to do? Is it bad that everything is inside of the update method? Can I use events or something to make the checks happen with a greater interval? I know there is a lot of maths going on each frame, but don't know how to do it another way.
Here is my code:
main.py:
#!/usr/bin/python3
#Importing necessary libraries
import pygame as pg, sys, random
from settings import *
from sprites import *
class Sim:
#Initialize the game window, etc.
def __init__(self):
pg.init()
self.screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption(TITLE)
self.clock = pg.time.Clock()
self.running = True
def new_grass(self, pos):
for g in self.grass:
if pos != g.pos:
Grass(self, pos)
#Start a new generation
def new(self):
self.all_sprites = pg.sprite.Group()
self.grass = pg.sprite.Group()
Grass(self, (10, 15))
self.run()
#Main loop
def run(self):
self.simulating = True
while self.simulating:
self.clock.tick(FPS)
self.events()
self.update()
self.draw()
#Update things on screen
def update(self):
self.all_sprites.update()
#Draw a grid on screen
def draw_grid(self):
for x in range(0, WIDTH, TILESIZE):
pg.draw.line(self.screen, BLACK, (x, 0), (x, HEIGHT))
for y in range(0, HEIGHT, TILESIZE):
pg.draw.line(self.screen, BLACK, (0, y), (WIDTH, y))
#Draw things on screen
def draw(self):
pg.display.set_caption("{:.2f}".format(self.clock.get_fps()))
self.screen.fill(DARK_GREEN)
self.draw_grid()
self.all_sprites.draw(self.screen)
#After drawing everything, flip the display
pg.display.flip()
#Events that might happen
def events(self):
for event in pg.event.get():
#Check for the user closing the window
if event.type == pg.QUIT:
if self.simulating:
self.simulating = False
self.running = False
s = Sim()
while s.running:
s.new()
pg.quit()
sprites.py:
#!/usr/bin/python3
import pygame as pg, random
from settings import *
vec = pg.math.Vector2
class Grass(pg.sprite.Sprite):
def __init__(self, sim, cord):
self.groups = sim.all_sprites, sim.grass
pg.sprite.Sprite.__init__(self, self.groups)
self.sim = sim
self.image = pg.Surface((TILESIZE/2, TILESIZE/2))
self.image.fill(GREEN)
self.cord = cord
self.rect = self.image.get_rect()
self.pos = vec(cord) * TILESIZE / 2
self.rect.topleft = self.pos
self.spread = vec(random.randint(-1, 1), random.randint(-1, 1))
self.energy = 20
def update(self):
if self.energy <= 80:
self.energy += 10
if self.energy >= 80:
self.sim.new_grass((self.cord + self.spread))
settings.py:
#Options/settings
TITLE = "EcoSim"
WIDTH = 480
HEIGHT = 600
FPS = 30
TILESIZE = 32
GRID_WIDTH = WIDTH / TILESIZE
GRID_HEIGHT = HEIGHT / TILESIZE
#Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
DARK_GREEN = (0, 100, 0)
BROWN = (150,75,0)
The problem is here:
def new_grass(self, pos):
for g in self.grass:
if pos != g.pos:
Grass(self, pos)
This is because you'll add a Grass object for every time there is a grass that already exists on another position. I think you meant to add one if the position isn't present at all.
You have a few bugs in your program, mainly the one mentioned above, but also, the parameter pos should actually be coord. I've highlighted your code with some comments on improvements:
#!/usr/bin/python3
import pygame as pg, random
TITLE = "EcoSim"
WIDTH = 480
HEIGHT = 600
FPS = 30
TILESIZE = 32
GRID_WIDTH = WIDTH / TILESIZE
GRID_HEIGHT = HEIGHT / TILESIZE
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
DARK_GREEN = (0, 100, 0)
BROWN = (150,75,0)
vec = pg.math.Vector2
class Grass(pg.sprite.Sprite):
# This creates the image just once!
IMAGE = pg.Surface((TILESIZE/2, TILESIZE/2))
IMAGE.fill(GREEN)
def __init__(self, cord): # Remove 'sim'.
# self.groups = sim.all_sprites, sim.grass
# pg.sprite.Sprite.__init__(self, self.groups)
# self.sim = sim
super().__init__()
self.image = Grass.IMAGE # All reference the same image.
self.cord = cord
self.rect = self.image.get_rect()
self.pos = vec(cord) * TILESIZE / 2
self.rect.topleft = self.pos
self.energy = 20
self.spread = vec(random.randint(-1, 1), random.randint(-1, 1))
def update(self):
if self.energy <= 80:
self.energy += 10
# Make Sim check for this.
# if self.energy >= 80:
# self.sim.new_grass((self.cord + self.spread))
class Sim:
def __init__(self):
pg.init()
pg.display.set_caption(TITLE)
self.screen = pg.display.set_mode((WIDTH, HEIGHT))
self.clock = pg.time.Clock()
self.all_sprites = pg.sprite.Group() # Create *ALL* attributes in `__init__`
self.grass = pg.sprite.Group()
self.running = True
self.simulating = False
# You're passing coord here, not pos! And you also want to add
# the grass only if the coord is not already present in the list.
def new_grass(self, coord):
if coord not in (x.cord for x in self.grass):
grass = Grass(coord)
self.grass.add(grass)
self.all_sprites.add(grass)
def new(self):
self.all_sprites = pg.sprite.Group()
self.grass = pg.sprite.Group()
grass = Grass((10, 15)) # Grass is now pure and doesn't have any side-effects, which makes the code much clearer.
self.grass.add(grass)
self.all_sprites.add(grass)
self.run()
def run(self):
self.simulating = True
while self.simulating:
self.clock.tick(FPS)
self.events()
self.update()
self.draw()
def update(self):
self.all_sprites.update()
# Let Sim decide the fate of the grass. Don't let Grass add
# itself.
for grass in self.grass:
if grass.energy >= 80:
self.new_grass((grass.cord + grass.spread))
def draw_grid(self):
for x in range(0, WIDTH, TILESIZE):
pg.draw.line(self.screen, BLACK, (x, 0), (x, HEIGHT))
for y in range(0, HEIGHT, TILESIZE):
pg.draw.line(self.screen, BLACK, (0, y), (WIDTH, y))
def draw(self):
pg.display.set_caption("{:.2f}".format(self.clock.get_fps()))
self.screen.fill(DARK_GREEN)
self.draw_grid()
self.all_sprites.draw(self.screen)
pg.display.flip()
def events(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.simulating = False # There was an unnecessary if here.
self.running = False
s = Sim()
while s.running:
s.new()
pg.quit()
The major issue is the method Sim.new_grass:
class Sim:
# [...]
def new_grass(self, pos):
for g in self.grass:
if pos != g.pos:
Grass(self, pos)
The method generates many more Grass objects than expected. It event generates multiple grass objects at the same location. For every object (g) where pos != g.pos a new instance of Grass is constructed.
You need to create a new instance of Grass if there is not any Grass object in the desired location:
class Sim:
# [...]
def new_grass(self, pos):
if not any(pos == g.pos for g in self.grass):
Grass(self, pos)
I am currently writing a program for my first python game (a simple top down shooter) and I have so far gotten to here using tutorials and examples online to help me build the code.
My character is able to rotate using vectors and that is all working how I want it to.
However, I cannot seem to make the player move using the arrow keys. I converted the velocity to a vector and when running the code and using the arrow keys, the players coordinates are actually changing and so in my head the player should be moving but isn't.
My initial thoughts are that player isn't being redrawn to the surface each time and hence staying in the same position, but I am not sure if this is correct.
What amendments/changes can I make in order to make this work as this problem has been driving me crazy the last few days.
import math
from random import randint
import pygame
from pygame.math import Vector2
pygame.init()
screen = pygame.display.set_mode((600, 600))
screen_width = 600
screen_height = 600
black = (0, 0, 0)
pygame.display.set_caption("gang")
clock = pygame.time.Clock()
class Character:
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
class Player(pygame.sprite.Sprite):
def __init__(self, pos):
super().__init__()
self.image = pygame.Surface((50, 30), pygame.SRCALPHA)
pygame.draw.polygon(self.image, pygame.Color('steelblue2'),
[(0, 0), (50, 15), (0, 30)])
self.orig_image = self.image
self.rect = self.image.get_rect(center=pos)
self.pos = Vector2(pos)
self.velocityx = Vector2(12, 0)
self.velocityy = Vector2(0, 12)
def update(self):
self.rotate()
def rotate(self):
direction = pygame.mouse.get_pos() - self.pos
radius, angle = direction.as_polar()
self.image = pygame.transform.rotate(self.orig_image, -angle)
self.rect = self.image.get_rect(center=self.rect.center)
class Enemy(Character):
def __init__(self, x, y, width, height):
Character.__init__(self, x, y, width, height)
def draw(self, win):
pygame.draw.rect(win, (0, 255, 0), (jeff.x, jeff.y, jeff.width, jeff.height))
def draw_window():
screen.fill((30, 30, 30))
jeff.draw(screen)
all_sprites.update()
all_sprites.draw(screen)
pygame.display.update()
def xor(a, b):
if bool(a) != bool(b):
return True
else:
return False
def game_end():
pygame.font.init()
text = pygame.font.Font('freesansbold.ttf', 32)
text_surface = text.render('Game Over', False, (0, 0, 0))
text_rect = text_surface.get_rect()
text_rect.center = (86, 86)
screen.blit(text_surface, (172, 172))
pygame.display.update()
mag = Player((150, 150))
playersprite = pygame.sprite.RenderPlain(mag)
all_sprites = pygame.sprite.Group(Player((300, 220)))
jeff = Enemy(randint(300, 500), randint(300, 500), 60, 60)
bullets = []
def main():
run = True
while run:
clock.tick(60)
keys = pygame.key.get_pressed()
if xor(keys[pygame.K_a], keys[pygame.K_LEFT]):
mag.pos -= mag.velocityx
print(mag.pos)
elif xor(keys[pygame.K_d], keys[pygame.K_RIGHT]):
mag.pos += mag.velocityx
print(mag.pos)
elif xor(keys[pygame.K_w], keys[pygame.K_UP]):
mag.pos -= mag.velocityy
print(mag.pos)
elif xor(keys[pygame.K_s], keys[pygame.K_DOWN]):
mag.pos += mag.velocityy
print(mag.pos)
draw_window()
main()
There are 2 issues in your code.
The Player object mag is not rendered at all, because you've created a 2nd Player object:
mag = Player((150, 150))
playersprite = pygame.sprite.RenderPlain(mag)
all_sprites = pygame.sprite.Group(Player((300, 220)))
Create 1 Player object and add it to the pygame.sprite.Group all_sprites:
mag = Player((150, 150))
playersprite = pygame.sprite.RenderPlain(mag)
all_sprites = pygame.sprite.Group(mag)
When the pygame.sprite.Sprite of a .sprite.Group are drawn by draw(), then the .image is drawn at the location which is defined by .rect.
You've to update the position of the .rect attribute by .pos in .update:
class Player(pygame.sprite.Sprite):
# [...]
def update(self):
# update position
self.rect.center = (int(self.pos.x), int(self.pos.y))
# update orientation
self.rotate()
def rotate(self):
direction = pygame.mouse.get_pos() - self.pos
radius, angle = direction.as_polar()
self.image = pygame.transform.rotate(self.orig_image, -angle)
self.rect = self.image.get_rect(center=self.rect.center)
Problem
My problem is that I have a game that generates 8 obstacles at the start of the game. The issue is that when I loop through the obstacles list, and update the sprites group, it only generates 1 sprite.
What I Want To Happen
When the game loads, I want 8 squares to fly down from the top of the window at random speeds, and starting at random positions.
What Is Currently Happening
Currently, when the game loads, only one square is falling from the screen.
PYthon Code
OBSTICLES_AMOUNT = 8
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((30, 30))
self.image.fill(BLUE)
self.rect = self.image.get_rect()
self.rect.x = random.randrange(0, WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.velY = 6
def animate(self):
self.rect.y += self.velY
class Game(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
pygame.init()
pygame.mixer.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption(TITLE)
self.running = True
self.clock = pygame.time.Clock()
self.obstaclesList = []
self.allSprites = pygame.sprite.Group()
self.obstacles = pygame.sprite.Group()
def new(self):
# create a new game
# add obstacles to list
for i in range(OBSTICLES_AMOUNT):
self.obstacle = Obstacle()
self.obstaclesList.append(self.obstacle)
# make new sprite using list
for i in self.obstaclesList:
self.allSprites.add(i)
self.obstacles.add(i)
self.gameLoop()
def gameLoop(self):
# main game loop
while self.running:
self.draw()
def draw(self):
self.screen.fill(WHITE)
self.allSprites.draw(self.screen)
for sprites in self.obstaclesList:
sprites.update()
self.allSprites.update()
Your code is fixed by
adding missing imports
adding missing constants
renaming animate to update in the Obstacle class
calling pygame.display.update after drawing
using a Clock to limit the framerate
adding event handling
adding code to create a Game instance
Some more improvements:
no need for obstaclesList if you already have obstacles
you can pass Groups directly to Sprite's __init__ function
remove a Sprite when it's no longer on the screen
Here's the code:
import pygame
import random
OBSTICLES_AMOUNT = 8
WIDTH, HEIGHT = 800,600
TITLE='some game of falling stuff'
BLUE = pygame.color.THECOLORS['blue']
WHITE = pygame.color.THECOLORS['white']
class Obstacle(pygame.sprite.Sprite):
def __init__(self, *args):
pygame.sprite.Sprite.__init__(self, *args)
self.image = pygame.Surface((30, 30))
self.image.fill(BLUE)
self.rect = self.image.get_rect()
self.rect.x = random.randrange(0, WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.velY = 6
def update(self):
self.rect.y += self.velY
if self.rect.y > HEIGHT:
self.kill()
class Game(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
pygame.init()
pygame.mixer.init()
self.clock = pygame.time.Clock()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption(TITLE)
self.running = True
self.clock = pygame.time.Clock()
self.allSprites = pygame.sprite.Group()
self.obstacles = pygame.sprite.Group()
def new(self):
# create a new game
# add obstacles to list
for i in range(OBSTICLES_AMOUNT):
Obstacle(self.allSprites, self.obstacles)
while self.running:
self.allSprites.update()
for e in pygame.event.get():
if e.type == pygame.QUIT:
self.running = False
self.draw()
self.clock.tick(60)
def draw(self):
self.screen.fill(WHITE)
self.allSprites.draw(self.screen)
for sprites in self.obstacles:
sprites.update()
pygame.display.update()
if __name__ == '__main__':
Game().new()