How to display random sprite colour on pygame - python

I want to be able to create a function where if the player collects a point they will change into a random colour out of an option of 4 colours. Im planning to call the changeColour() function once the sprite collides with the point in the main loop ,so the sprite changes colours, but i cant get the sprite to change colour. (note : this is part of the code using pygame)
/// the function used for the colours
def randomColour(): # function for random colour
rand = random.randint(0,3) # random number from 0-3
if(rand == 0): # different numbers will return different colours
return PURPLE # 0 returns purple
elif(rand == 1):
return RED # 1 returns Red
elif(rand == 2):
return TEAL # 2 returns Teal
elif(rand == 3):
return YELLOW # 3 rerurns Yellow
r_colour = randomColour()
/// this cube class is whats used to create the player sprite and food item
class cube(object):
rows = 20 # used to make grdis and draw later
w = 500 # will using squares , this will also be use to calculate space
def __init__(self,start,dirnx=1,dirny=0,color=(r_colour)):
self.pos = start
self.dirnx = 1 # so that player sprite can always be moving
self.dirny = 0
self.color = color # the color of the cube
def draw(self, surface, eyes=False): # draws the cube object used for both the player and the snack
dis = self.w // self.rows
i = self.pos[0]
j = self.pos[1]
pygame.draw.rect(surface, self.color, (i*dis+1,j*dis+1, dis-2, dis-2))
if eyes: # eyes for the player sprite to be drawn on
centre = dis//2
radius = 3
circleMiddle = (i*dis+centre-radius,j*dis+8) # two circles drawn on as eyes / size of eyes
circleMiddle2 = (i*dis + dis -radius*2, j*dis+8)
pygame.draw.circle(surface, (WHITE), circleMiddle, radius) # draws eyes
pygame.draw.circle(surface, (WHITE), circleMiddle2, radius)
/// player object
class player(object): # new class which will contain the contents of the player
body = []
turns = {}
def __init__(self , color, pos):
self.color = randomColour()
self.head = cube(pos) # uses the position of the player to navigate
self.body.append(self.head)
self.dirnx = 0
self.dirny = 1 # starts off with movement / player always moving.
def draw(self, surface): # draw the player onto the screen
for i, c in enumerate(self.body):
if i ==0: # draws the eyes of the player sprite so player can identify the sprite
c.draw(surface, True)
/// redraw window function and the main loop
def redrawWindow(surface): # this will let the screen get updated every time its called
global rows, width, p, snack
surface.fill((BLACK)) # Fills the screen with colour black
p.draw(surface)
snack.draw(surface) # draws snack item
drawGrid(width,rows, surface) # Will draw our grid lines
pygame.display.update()
def main():
global width, rows, p, snack
width = 500
rows = 20
win = pygame.display.set_mode((width, width)) # draws the window for the game
pygame.display.set_caption("Colour Run") # changes name of the window
p = player((randomColour()), (10,10)) # the player
snack = cube(randomSnack(rows, p), color=(0,0,255)) # the point to be collected
run = True # marks runas true so game state will be running
clock = pygame.time.Clock()
while run:
pygame.time.delay(50) #
clock.tick(8) #
p.move() # allows for the player to move
if p.body[0].pos == snack.pos: # checks if player has collided with the point
p.changeColour() # changes the colour of the sprite /// trying to use this
snack = cube(randomSnack(rows, p), color=(0,0,255)) # respawns the point
/// note this isnt the whole code only part of it

I don't have much experience with pygames so there might be something I'm missing here, but your Player class does not have a .changeColour() method ?
If that's the issue then you could just add the method like this :
class player(object): # new class which will contain the contents of the player
body = []
turns = {}
def __init__(self , color, pos):
self.color = randomColour()
self.head = cube(pos) # uses the position of the player to navigate
self.body.append(self.head)
self.dirnx = 0
self.dirny = 1 # starts off with movement / player always moving.
def changeColour(self):
self.color = randomColour()
You can also clean your randomColour() function like so:
def randomColour():
rand = random.randint(0,3)
colors = (PURPLE, RED, TEAL, YELLOW)
return colors[rand]
Note that your function can return you the same color as the one your Player had before change.

Related

center an object/image rect

Ok so I have this code which in def draw, in if self.part == 1: I blit the main image in the middle, but this doesn´t center the image as I want, it just makes the spawning point in the middle, and the image starts from there, so the image always appears on the bottom right side. I want it to blit it in the middle, like the whole thing:
class GameScene(Scene):
def __init__(self, game, images, main_image, next_scene):
super().__init__(next_scene)
self.game = game
self.main_image = main_image
self.game_images = images
# Fade effect set-up
self.fade = False
self.fade_time = 0
self.current_alpha = 255
self.part = 1
self.record_text = font.render('Atiende',True, PURPLE)
self.record_text_rect = self.record_text.get_rect(center=(SCREEN_WIDTH/2, 70))
self.correct_image_rect = None
# Trying to use colliderect so it doesnt overlap
# this is the same code as before but adapted to use the gameimage class and the rects stored there
self.rects = []
# this is the fade stuff from before that was in draw. It really belongs here tbh
def update(self, dt):
if len(self.rects) < len(self.game_images):
i = len(self.rects)
x = random.randint(100,950)
y = random.randint(100,600)
self.game_images[i].rect.x = x
self.game_images[i].rect.y = y
margin = 5
rl = [rect.inflate(margin*2, margin*2) for rect in self.rects]
if len(self.rects) == 0 or self.game_images[i].rect.collidelist(rl) < 0:
self.rects.append(self.game_images[i].rect)
if self.part == 1 and self.fade:
self.fade_time += dt
if self.fade_time > fade_timer:
self.fade_time = 0
self.main_image.set_alpha(self.current_alpha)
self.record_text.set_alpha(self.current_alpha)
# Speed whichin the image dissapears
self.current_alpha -= 5
if self.current_alpha <= 0:
self.fade = False
self.part = 2
else:
# we reset the main image alpha otherwise it will be invisible on the next screen (yeah, this one caught me out lol!)
self.main_image.set_alpha(255)
# draw is similar to before, but a bit more streamlined as the fade stuff is not in update
def draw(self, screen):
super().draw(screen)
if self.part == 1:
screen.blit(self.record_text, self.record_text_rect)
# x = self.main_image.rect.x.center
# y = self.main_image.rect.y.center
screen.blit(self.main_image.image, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
else:
# Second half
text2 = font.render('¿Qué has visto?',True, PURPLE)
screen.blit(text2, (400,5))
# Show all similar images
cont = 0
for game_image in self.game_images:
game_image.draw(screen)
cont += 1
# We associate the correct rect to the correct image, to pass it later to the CORRECT Screen
self.correct_image_rect = self.game_images[self.game_images.index(self.main_image)].rect
The thing is, that the main_image that it comes through a parameter, its a proper class:
class GameImage(object):
def __init__(self, image):
self.image = image
self.rect = self.image.get_rect()
# this class has the set_alpha so the alpha can be passed on to its image
def set_alpha(self, alpha):
self.image.set_alpha(alpha)
# we pass the draw method the surface we want the image drawn on
def draw(self, surf):
surf.blit(self.image, self.rect)
So, as you can see, the GameImage class it´s an object that gets its rect as well, but I´m struggling to center that main image rect and blit into the screen the way I want. So yeah, I know how to do it with texts, as you can see on the self.recrod_text_rect, but I don´t know how to do it with this object and pass it to the screen.blit of the draw def.
You need to set the center of the image by the center of target Surface:
class GameImage(object):
def __init__(self, image):
self.image = image
self.rect = self.image.get_rect()
# this class has the set_alpha so the alpha can be passed on to its image
def set_alpha(self, alpha):
self.image.set_alpha(alpha)
# we pass the draw method the surface we want the image drawn on
def draw(self, surf):
self.rect.center = surf.get_rect().center # <---
surf.blit(self.image, self.rect)
See also How to Center Text in Pygame

Working with classes (creating a class efficiently, drawing and updating all instances of the class every frame)

I've been working on a "bullet hell" game of my own, but I am struggling very much with working with classes (im new to python), below i've enclosed my code(I wouldnt include so much but i dont know howo to go about the problem). At the moment what i've been attempting to
1. Create different instances of the class
2. Draw them every frame
3. Update their position every frame according to the x and y speed
import pygame
# Define colors needed
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
bulletArray = []
BulletSprite = ("Bullet4.png")
pygame.init()
class Bullet:
def __init__(self, sprite, x, y, xSpeed, ySpeed):
pygame.sprite.Sprite.__init__(self)
self.x = x
self.y = y
self.xSpeed = xSpeed
self.ySpeed = ySpeed
bulletArray.append(self)
def update(self):
pass
def draw(screen):
self.screen = screen
screen.blit(screen, (x, y))
#Set the width and height of the game screen
size = (700, 500)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Bullet stuff")
# Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
testBullet = Bullet(BulletSprite, 200, 200, 2, 2)
#Main Program Loop
while not done:
# --- Main event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# --- Game logic should go here
print(bulletArray)
# --- background image.
screen.fill(WHITE)
# --- Drawing code should go here
# --- Updates the screen every frame with whats been drawn
pygame.display.flip()
# --- Limit to 60 frames per second
clock.tick(60)
# Close the window and quit.
pygame.quit()
Much of the this mechanism is already handled automatically if you use the PyGame Sprite Class.
A PyGame sprite is essentially made of:
An Image to draw to represent the object
A rectangle around the object to remember the location and check for collisions
An update() function to handle any movement of the sprite
It's handy to work with the existing PyGame Sprite layout because a lot of functionality is already provided by the library code.
So let's make a BulletSprite:
class BulletSprite( pygame.sprite.Sprite ):
def __init__( self, bitmap, x, y ):
pygame.sprite.Sprite.__init__( self )
self.image = bitmap # How the sprite looks
self.rect = bitmap.get_rect() # Bounding box the size of the image
self.rect.centerx = x # position the bullet
self.rect.centery = y # about its centre
And that's it. This will give you a stationary sprite, painted as the given bitmap at (x,y) on-screen.
So let's now use it:
# Create a group of 100 bullets
sprite_image = pygame.image.load( "bullet.png" )
all_bullets = pygame.sprite.Group() # Group to hold the sprites
for i in range( 100 ):
random_x = 50 + random.randrange( 950 ) # TODO: use proper screen size
random_y = 50 + random.randrange( 950 )
new_sprite = BulletSprite( sprite_image, random_x, random_y )
all_bullets.add( new_sprite )
# Main Window loop
while True:
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
break
# Paint the window
window.fill( ( 0, 0, 0 ) ) # Paint it Black
all_bullets.update() # move all the bullets
all_bullets.draw( window ) # Paint all the bullets
pygame.display.flip()
pygame.quit()
And that's it. After the sprites are created, it takes two lines of code to move them all, and draw them all.
But they wont move yet. For them to move, the BulletSprite needs to have an update() function. This function does whatever is necessary to move the sprite - apply velocity, gravity, whatever. The net effect is the sprite's rectangle's (x,y) is changed.
So adding some changes to the sprite - first an x and y velocity, and then the movement code.
class BulletSprite( pygame.sprite.Sprite ):
def __init__( self, bitmap, x, y ):
pygame.sprite.Sprite.__init__( self )
self.image = bitmap # How the sprite looks
self.rect = bitmap.get_rect() # Bounding box the size of the image
self.rect.centerx = x # position the bullet
self.rect.centery = y # about its centre
self.x_move = random.randrange( -3, 3 ) # simple random velocity
self.y_move = random.randrange( -3, 3 )
def update( self ):
x = self.rect.centerx + self.x_move # calculate the new position
y = self.rect.centerx + self.y_move # by applying an offset
# has the sprite gone off-screen
if ( x < 0 or x > 1000 or y < 0 or y > 1000 )
self.kill() # remove from the group
else:
self.rect.centerx = x # RE-position the bullet
self.rect.centery = y
Every time the update() is called on your sprite group (all_bullets), the sprite library will go and call the update() function on every sprite in the group. The code checks to see if the bullet is off-screen (I lazily used 1000 as a placeholder for screen width & height), and if-so, the sprite.kill() function handles the removing from the group and cleaning up. It's very easy.

How can I change this surface image to the image I want in pygame?

# Memory V2
# The second version contains the complete tile grid and the black panel on the right, there is score in the black panel. All 8 pairs of two tiles are covered by question mark when the game starts . Each time the game is played, the tiles spawn in random locations in the grid. Player can click tiles to reveal images. Game ends upon clicking close screen or all 16 tiles being exposed. Game occurs on a 4x4 grid.
import pygame,random, time
# User-defined functions
def main():
# initialize all pygame modules (some need initialization)
pygame.init()
# create a pygame display window
pygame.display.set_mode((500, 400))
# set the title of the display window
pygame.display.set_caption('Memory v1')
# get the display surface
w_surface = pygame.display.get_surface()
# create a game object
game = Game(w_surface)
# start the main game loop by calling the play method on the game
#object
game.play()
# quit pygame and clean up the pygame window
pygame.quit()
# User-defined classes
class Game:
# An object in this class represents a complete game.
def __init__(self, surface):
# Initialize a Game.
# - self is the Game to initialize
# - surface is the display window surface object
# === objects that are part of every game that we will discuss
self.surface = surface
self.bg_color = pygame.Color('black')
self.FPS = 60
self.game_Clock = pygame.time.Clock()
self.close_clicked = False
self.continue_game = True
Tile.set_surface(self.surface)
# tell grid to be size 4 meaning 4x4 or 16 squares total
grid_size = 4
self.create_grid(grid_size)
self.score=0
def draw_score(self):
# this method draws the player's score in the top-right corner of the
# game window.
# - self : the game the score is being drawn for
font_color = pygame.Color("white")
font_bg = pygame.Color("black")
font = pygame.font.SysFont("arial", 32)
text_img = font.render(str(self.score), True, font_color, font_bg)
text_pos = (460,0)
self.surface.blit(text_img, text_pos)
def create_grid(self, grid_size):
# Creates a grid of tiles that is grid_size x grid_size in size.
self.grid = [ ]
# Create list of image names to be used on the squares (we just append image(1-9) and the file type bmp
# Then we create image surfaces of each image name and add image surfaces to itself which provides us with two of each image
img_names = ['image' + str(i) + '.bmp' for i in range(1,9)]
image_surfaces = [pygame.image.load(name) for name in img_names]
image_surfaces = image_surfaces + image_surfaces
random.shuffle(image_surfaces)
# this for loop creates each row in our grid
for row_num in range(grid_size):
new_row = self.create_row(row_num, grid_size,image_surfaces)
self.grid.append(new_row)
def create_row(self, row_num, size,images):
# Create one row in a grid. Each row contains size Tiles
# required for calculating the tile's x,y coordinates on screen
# - row_num: the nth row of the grid being created
# - size : the number of tiles in the row
# returns the newly created row'
tile_height = self.surface.get_height() // size
# 3/4 to leave space for black column on side
tile_width = 3/4*self.surface.get_width() // size
new_row = [ ]
for col_num in range(size):
# number of row x tile height produces y position
# number of col x tile_width produces x position
# + 10 so it fits
pos = (row_num*tile_height+10,col_num*tile_width+10)
# assigns one of the images to each tile by pairing it with a unique coordinate
#content=pygame.image.load('image0.bmp')
#content = images[row_num*size+col_num]
one_tile = Tile(pos, tile_width, tile_height)
content=pygame.image.load('image0.bmp')
#content=images[row_num*size+col_num]
if self.handle_mouse_click==True:
content=images[row_num*size+col_num]
one_tile.set_content(content)
#one_tile = Tile(pos, tile_width, tile_height)
#one_tile.set_content(content)
new_row.append(one_tile)
return new_row
def play(self):
# Play the game until the player presses the close box.
# - self is the Game that should be continued or not.
while not self.close_clicked: # until player clicks close box
# play frame
self.handle_events()
self.draw()
if self.continue_game:
self.update()
self.decide_continue()
self.game_Clock.tick(self.FPS) # run at most with FPS listed
def handle_events(self):
# Handle each user event by changing the game state
# - self is the Game whose events will be handled
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
self.close_clicked = True
if event.type == pygame.MOUSEBUTTONDOWN:
self.handle_mouse_click(event)
def handle_mouse_click(self, event):
# responds to one mouse click on screen; that means flipping the
# tile
#print("Screen was clicked at " + str(event.pos))
for row in self.grid:
for tile in row:
if tile.select(event.pos)==True:
print('hi')
#def change_content(self,content):
#return False
#return content_switch
def draw(self):
# Draw all game objects.
# - self is the Game to draw
self.surface.fill(self.bg_color) # clear the display surface
# draws the grid
for row in self.grid:
for tile in row:
tile.draw()
self.draw_score()
pygame.display.update() # updates the display
def update(self):
# Update the game objects for the next frame.
# - self is the Game to update
self.score= pygame.time.get_ticks()//1000
pass
def decide_continue(self):
# Check and remember if the game should continue
filled_tiles = [ ]
for row in self.grid:
for tile in row:
if tile.tile_content==True:
filled_tiles.append(tile)
if len(filled_tiles) == self.grid_size ** 2:
return False
else:
return True
class Tile:
# A tile represents one location on a grid. Tiles hold content
# (in this case, an X or an O).
# class attributes that are common to all tiles
# setting surface to none isnt exactly necessary, however we set class wide attributes here before putting it in a method
surface = None
fg_color = pygame.Color("white")
bg_color = pygame.Color("black")
# set border width for each tile in grid
border_width = 5
#classmethod
def set_surface(cls, surface):
# sets the class attribute, surface
cls.surface = surface
def __init__(self, screen_position, width, height):
# initialize one instance of our Tile class. Tiles represent
# one 'position' in our tic-tac-toe board.
# - self: the tile being initialized
# - screen_position: the [x, y] coordinates to draw the tile at
# - width: the width of the tile
# - height: the height of the tile
self.screen_position = screen_position
#self.content = ''
# create a rectangle defining our boundaries
x, y = screen_position
self.rect = pygame.Rect(x, y, width, height)
def draw_content(self):
# create an rect object of image so we can blit images to surface of grid tiles
image_rect=self.content.get_rect(center=self.rect.center)
Tile.surface.blit(self.content,image_rect)
def draw(self):
# draw the contents of a tile to its surface.
# - self: the tile being drawn
self.draw_content()
pygame.draw.rect(Tile.surface, Tile.bg_color, self.rect,
Tile.border_width)
def set_content(self, new_content):
# change our tile content.
self.content = new_content
def select(self, pos):
selected=False
if self.rect.collidepoint(pos):
if self.content==pygame.image.load('image0.bmp'):
selected=True
return selected
def __eq__(self):
if self.content==pygame.image.load('image0.bmp'):
return True
def tile_content(self):
if self.content == pygame.image.load('image0.bmp'):
return False
else:
return True
main()
So I'm trying to build another version of the game memory where upon clicking one of the tiles the question mark image 'flips' and becomes the actual image I assigned in my code and then the game ends after I flip all of the tiles. However, when I use self.content==pygame.image.load('image0.bmp') it doesn't seem to work or return true or anything even though I set it to do so. My professor said I need to overload the == operator which I think I did but for some reason it still refuses to flip upon me clicking it and I have no idea why? Can someone explain?
This is because pygame.image.load() returns a pygame.Surface(). Any object (instance of a class) is never exactly the same.
So:
pygame.image.load("image.png") == pygame.image.load("image.png") will always be False.
Also, when asking a question, try to put small snippets of code instead of the whole thing.

builtins.AttributeError: 'module' object has no attribute 'one_tile'

HI I keep getting that error message from the line below
pygame.one_tile.blit(self.i_list[img_index], pos)
and this is from the function below
def create_grid(self, grid_size):
self.grid = [ ]
tile_width = self.surface.get_width() / grid_size
tile_height = self.surface.get_height() / grid_size
# this for loop creates each row in our grid
for i in range(grid_size):
one_row = [ ]
img_index = 0
for j in range(grid_size):
y = i * tile_height
x = j * tile_width
pos = (x,y)
one_tile = Tile(pos, tile_width, tile_height, self.surface)
pygame.one_tile.blit(self.i_list[img_index], pos)
img_index += 1
one_row.append(one_tile)
self.grid.append(one_row)
I'm writing a code for the memory game version 1 (the game that has 8 pairs of images and you need to memorize which image was on which card and match a pair) and I keep get that error message but I don;t really know what I should do to solve it. Any help would be appreciated very much. Thank you!!
and my full code is
import pygame, random
# User-defined functions
def main():
# initialize all pygame modules (some need initialization)
pygame.init()
# create a pygame display window
pygame.display.set_mode((500, 400))
# set the title of the display window
pygame.display.set_caption('A template for graphical games with two moving dots')
# get the display surface
w_surface = pygame.display.get_surface()
# create a game object
game = Game(w_surface)
# start the main game loop by calling the play method on the game object
game.play()
# quit pygame and clean up the pygame window
pygame.quit()
# User-defined classes
class Game:
# An object in this class represents a complete game.
def __init__(self, surface):
# Initialize a Game.
# - self is the Game to initialize
# - surface is the display window surface object
# === objects that are part of every game that we will discuss
self.surface = surface
self.bg_color = pygame.Color('black')
self.FPS = 60
self.game_Clock = pygame.time.Clock()
self.close_clicked = False
self.continue_game = True
# === game specific objects
self.max_frames = 150
self.frame_counter = 0
self.i_list = []
self.images = ["image1.bmp", "image2.bmp", "image3.bmp", "image4.bmp", "image5.bmp", "image6.bmp", "image7.bmp", "image8.bmp"]
for i in self.images:
pygame.image.load(i)
self.i_list.append(i)
self.i_list.append(i)
random.shuffle(self.i_list)
self.create_grid(4)
def create_grid(self, grid_size):
self.grid = [ ]
tile_width = self.surface.get_width() / grid_size
tile_height = self.surface.get_height() / grid_size
# this for loop creates each row in our grid
for i in range(grid_size):
one_row = [ ]
img_index = 0
for j in range(grid_size):
y = i * tile_height
x = j * tile_width
pos = (x,y)
one_tile = Tile(pos, tile_width, tile_height, self.surface)
pygame.one_tile.blit(self.i_list[img_index], pos)
img_index += 1
one_row.append(one_tile)
self.grid.append(one_row)
def play(self):
# Play the game until the player presses the close box.
# - self is the Game that should be continued or not.
while not self.close_clicked: # until player clicks close box
# play frame
self.handle_events()
self.draw()
if self.continue_game:
self.update()
self.decide_continue()
self.game_Clock.tick(self.FPS) # run at most with FPS Frames Per Second
def handle_events(self):
# Handle each user event by changing the game state appropriately.
# - self is the Game whose events will be handled
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
self.close_clicked = True
def draw(self):
# Draw all game objects.
# - self is the Game to draw
self.surface.fill(self.bg_color) # clear the display surface first
for row in self.grid:
for tile in row:
tile.draw()
pygame.display.update()
def update(self):
# Update the game objects for the next frame.
# - self is the Game to update
pass
def decide_continue(self):
# Check and remember if the game should continue
# - self is the Game to check
return True
class Tile:
# A tile represents one location on a grid. Tiles hold content
# (in this case, an X or an O).
def __init__(self, screen_position, width, height, surface):
# initialize one instance of our Tile class. Tiles represent
# one 'position' in our tic-tac-toe board.
# - self: the tile being initialized
# - screen_position: the [x, y] coordinates to draw the tile at
# - surface: the surface on which to draw the tile
# - height: the height of the tile when it is drawn
# - width: the width of the tile when it is drawn
self.screen_position = screen_position
self.surface = surface
self.content = ''
self.height = height
self.width = width
def draw(self):
# draw the contents of a tile to its surface.
tile_boundary = pygame.Rect(self.screen_position[0],
self.screen_position[1],
self.width,
self.height)
pygame.draw.rect(self.surface, pygame.Color("white"), tile_boundary, 2)
main()
I recommend to add an .image attribute and .visible attribute to the class Tile. Each tile knows the associated image and has a state if the image in on the tile is visible:
class Tile:
# A tile represents one location on a grid. Tiles hold content
# (in this case, an X or an O).
def __init__(self, screen_position, width, height, surface, image):
# initialize one instance of our Tile class. Tiles represent
# one 'position' in our tic-tac-toe board.
# - self: the tile being initialized
# - screen_position: the [x, y] coordinates to draw the tile at
# - surface: the surface on which to draw the tile
# - height: the height of the tile when it is drawn
# - width: the width of the tile when it is drawn
self.screen_position = screen_position
self.surface = surface
self.content = ''
self.height = height
self.width = width
self.image = image
self.visible = False
def show(self, visible):
self.visible = visible
def draw(self):
# draw the contents of a tile to its surface.
tile_boundary = pygame.Rect(self.screen_position[0],
self.screen_position[1],
self.width,
self.height)
pygame.draw.rect(self.surface, pygame.Color("white"), tile_boundary, 2)
if self.visible:
img_rect = self.image.get_rect(center = tile_boundary.center)
self.surface.blit(self.image, img_rect.topleft)
To create an image list, you've to load the image. The return value of pygame.image.load is a pygame.Surface object which can be append to i_list:
self.i_list = []
self.images = ["image1.bmp", "image2.bmp", "image3.bmp", "image4.bmp", "image5.bmp", "image6.bmp", "image7.bmp", "image8.bmp"]
for imgname in self.images:
img = pygame.image.load(imgname)
self.i_list.append(img)
random.shuffle(self.i_list)
Pass the image to the constructor of Tile:
for i in range(grid_size):
one_row = [ ]
img_index = 0
for j in range(grid_size):
pos = (j * tile_width, i * tile_height)
one_tile = Tile(pos, tile_width, tile_height, self.surface, self.i_list[img_index])
img_index += 1
one_row.append(one_tile)
self.grid.append(one_row)
Note, for debug reasons you can all the state of all the images "visible" (self.visible = True in the constructor of Tiles).

Software Design and Development Major: Pygame Smudge Trails

First off, i have searched online and this website for solutions and the ones i have tried are not working so i decided to post my individual question and code. This program was created using Python 3.2.2 and the corresponding compatible version of pygame. I also realize a more efficient method would be to use sprites, sprite groups and 'dirty rect' updating but i unable to convert the program and so i will continue without the added benefits of such functions.
Problem: Smudge trails where the 'asteroids' are moving are left behind.
Hypothesis: Background is blitted onto the screen however the asteroids are blitted onto the Background.
Please Reply - btw i'm a highschooler from AUS :D
import pygame
import random
import math
pygame.init()
height = 550
width = 750
screen = pygame.display.set_mode((width, height))
background = pygame.image.load("Planet.jpg")
Clock = pygame.time.Clock()
class asteroid(pygame.sprite.Sprite):
def __init__(self, x, y, size):
pygame.sprite.Sprite.__init__(self)
self.x = x
self.y = y
self.size = 15
self.speed = 0.0
self.angle = 0
self.colour = (171, 130, 255)
self.thickness = 0
def display(self):
pygame.draw.circle(background, self.colour, (int(self.x),int(self.y)), self.size, self.thickness)
pygame.draw.circle(background, (255, 255, 255), (int(self.x),int(self.y)), self.size, 1)
def move(self):
self.x += math.sin(self.angle) * self.speed
self.y -= math.cos(self.angle) * self.speed
def boundaries(self):
if self.x > width - self.size:
self.x = 0 + self.size
elif self.x < self.size:
self.x = width - self.size
if self.y > height - self.size:
self.y = 0 + self.size
elif self.y <self.size:
self.y = height - self.size
num_target = 5
my_particles = []
num_particles = len(my_particles)
while num_particles < 5:
for n in range(num_target):
size = 20
x = random.randint(size, height - size)
y = random.randint(size, width - size)
target = asteroid(x, y, size)
target.speed = random.uniform(1.0, 1.0)
target.angle = random.uniform(0, math.pi*2)
my_particles.append(target)
num_particles = num_particles + 1
def main():
pygame.display.set_caption("Anyu's Game")
screen.blit(background, (0,0))
pygame.display.update()
score = (pygame.time.get_ticks()/1000)
print (score)
while True:
pygame.display.update()
screen.blit(background, (0,0))
MouseP = pygame.mouse.get_pos()
frames = Clock.get_fps
pygame.mouse.set_visible
score = (pygame.time.get_ticks()/1000)
print (score)
print (MouseP)
for target in my_particles:
target.move()
target.boundaries()
target.display()
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit();
if __name__=='__main__':
main()
Basically, you are right! The circles are drawn directly onto the background, and everytime new circles are drawn, the old circles remain. Resulting in the smudges/trails.
You can just change background to screen in your draw method. This will fix it.
But it is really worth using the Sprite classes as intended. I've made a few changes to your code to switch it over for you. With these changes it runs without trails :)
Here are the changes and explainations:
Add this near the top:
#Create a new `pygame.Surface`, and draw a circle on it, then set transparency:
circle = pygame.Surface((30,30))
circle = circle.convert()
pygame.draw.circle(circle, (171, 130, 255), (int(15),int(15)), 15, 0)
circle.set_colorkey(circle.get_at((0, 0)), pygame.RLEACCEL)
Add this to the asteroid, __init__ method:
#Sets the asteroid image, and then the asteroids co-ords (these are in `rect`)
self.image = circle
self.rect = self.image.get_rect()
Add this to the end of def move(self):
self.rect[0] = self.x
self.rect[1] = self.y
change:
my_particles = []
to:
#This is a special pygame container class, it has a draw() method that tracks changed areas of the screen.
my_particles = pygame.sprite.RenderUpdates()
change:
my_particles.append(target)
to:
my_particles.add(target)
change:
while True:
pygame.display.update()
screen.blit(background, (0,0))
MouseP = pygame.mouse.get_pos()
frames = Clock.get_fps
pygame.mouse.set_visible
score = (pygame.time.get_ticks()/1000)
print (score)
print (MouseP)
for target in my_particles:
target.move()
target.boundaries()
target.display()
pygame.display.update()
to:
#initial screen draw:
screen.blit(background, (0,0))
pygame.display.update()
while True:
#remove previous drawn sprites and replaces with background:
my_particles.clear(screen, background)
MouseP = pygame.mouse.get_pos()
frames = Clock.get_fps
pygame.mouse.set_visible
score = (pygame.time.get_ticks()/1000)
print (score)
print (MouseP)
for target in my_particles:
target.move()
target.boundaries()
#draws changed sprites to the screen:
pygame.display.update(my_particles.draw(screen))
Remove the display method as it is no longer needed.
This will also run a lot faster than the your earlier code, as the time taken to draw something is proportional to the size of the drawing area, and previously it was drawing the whole background everytime - now it only draws the sprites and changes to the background!
Hope this helps :)
This already has an answer but this can be useful instead of other methods.
Make sure when you blit the images onto the screen, flip the display after blitting everything.
I would consider making a draw() function
Like this:
def draw(self):
# Blit images
self.screen.blit(image)
# Flip display
pygame.display.flip()
This will flip the display every frame and then draw the next frame without a trail.
Also quick notes, remember to do image = pygame.image.load(image).convert or .convert_alpha() else after adding more images the game will slow down.
Also, if you do import pygame as pg you don't have to type out pygame each time, instead you can just type pg.

Categories