Trying to display an image and move it on a timer, but only while the camera the image is on [duplicate] - pygame

I've been searching for some good tutorial about making simple sprite animation from few images in Python using Pygame. I still haven't found what I'm looking for.
My question is simple: how to make an animated sprite from few images (for an example: making few images of explosion with dimensions 20x20px to be as one but animated)
Any good ideas?

There are two types of animation: frame-dependent and time-dependent. Both work in similar fashion.
Before the main loop
Load all images into a list.
Create three variable:
index, that keeps track on the current index of the image list.
current_time or current_frame that keeps track on the current time or current frame since last the index switched.
animation_time or animation_frames that define how many seconds or frames should pass before switching image.
During the main loop
Increment current_time by the amount of seconds that has passed since we last incremented it, or increment current_frame by 1.
Check if current_time >= animation_time or current_frame >= animation_frame. If true continue with 3-5.
Reset the current_time = 0 or current_frame = 0.
Increment the index, unless if it'll be equal or greater than the amount of images. In that case, reset index = 0.
Change the sprite's image accordingly.
A full working example
import os
import pygame
pygame.init()
SIZE = WIDTH, HEIGHT = 720, 480
BACKGROUND_COLOR = pygame.Color('black')
FPS = 60
screen = pygame.display.set_mode(SIZE)
clock = pygame.time.Clock()
def load_images(path):
"""
Loads all images in directory. The directory must only contain images.
Args:
path: The relative or absolute path to the directory to load images from.
Returns:
List of images.
"""
images = []
for file_name in os.listdir(path):
image = pygame.image.load(path + os.sep + file_name).convert()
images.append(image)
return images
class AnimatedSprite(pygame.sprite.Sprite):
def __init__(self, position, images):
"""
Animated sprite object.
Args:
position: x, y coordinate on the screen to place the AnimatedSprite.
images: Images to use in the animation.
"""
super(AnimatedSprite, self).__init__()
size = (32, 32) # This should match the size of the images.
self.rect = pygame.Rect(position, size)
self.images = images
self.images_right = images
self.images_left = [pygame.transform.flip(image, True, False) for image in images] # Flipping every image.
self.index = 0
self.image = images[self.index] # 'image' is the current image of the animation.
self.velocity = pygame.math.Vector2(0, 0)
self.animation_time = 0.1
self.current_time = 0
self.animation_frames = 6
self.current_frame = 0
def update_time_dependent(self, dt):
"""
Updates the image of Sprite approximately every 0.1 second.
Args:
dt: Time elapsed between each frame.
"""
if self.velocity.x > 0: # Use the right images if sprite is moving right.
self.images = self.images_right
elif self.velocity.x < 0:
self.images = self.images_left
self.current_time += dt
if self.current_time >= self.animation_time:
self.current_time = 0
self.index = (self.index + 1) % len(self.images)
self.image = self.images[self.index]
self.rect.move_ip(*self.velocity)
def update_frame_dependent(self):
"""
Updates the image of Sprite every 6 frame (approximately every 0.1 second if frame rate is 60).
"""
if self.velocity.x > 0: # Use the right images if sprite is moving right.
self.images = self.images_right
elif self.velocity.x < 0:
self.images = self.images_left
self.current_frame += 1
if self.current_frame >= self.animation_frames:
self.current_frame = 0
self.index = (self.index + 1) % len(self.images)
self.image = self.images[self.index]
self.rect.move_ip(*self.velocity)
def update(self, dt):
"""This is the method that's being called when 'all_sprites.update(dt)' is called."""
# Switch between the two update methods by commenting/uncommenting.
self.update_time_dependent(dt)
# self.update_frame_dependent()
def main():
images = load_images(path='temp') # Make sure to provide the relative or full path to the images directory.
player = AnimatedSprite(position=(100, 100), images=images)
all_sprites = pygame.sprite.Group(player) # Creates a sprite group and adds 'player' to it.
running = True
while running:
dt = clock.tick(FPS) / 1000 # Amount of seconds between each loop.
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
player.velocity.x = 4
elif event.key == pygame.K_LEFT:
player.velocity.x = -4
elif event.key == pygame.K_DOWN:
player.velocity.y = 4
elif event.key == pygame.K_UP:
player.velocity.y = -4
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT or event.key == pygame.K_LEFT:
player.velocity.x = 0
elif event.key == pygame.K_DOWN or event.key == pygame.K_UP:
player.velocity.y = 0
all_sprites.update(dt) # Calls the 'update' method on all sprites in the list (currently just the player).
screen.fill(BACKGROUND_COLOR)
all_sprites.draw(screen)
pygame.display.update()
if __name__ == '__main__':
main()
When to chose which
Time-dependent animation allows you to play the animation at the same speed, no matter how slow/fast the frame-rate is or slow/fast your computer is. This allows your program to freely change the framerate without affecting the animation and it'll also be consistent even if the computer cannot keep up with the framerate. If the program lags the animation will catch up to the state it should've been as if no lag had happened.
Although, it might happen that the animation cycle don't synch up with the framerate, making the animation cycle seem irregular. For example, say that we have the frames updating every 0.05 second and the animation switch image every 0.075 second, then the cycle would be:
Frame 1; 0.00 seconds; image 1
Frame 2; 0.05 seconds; image 1
Frame 3; 0.10 seconds; image 2
Frame 4; 0.15 seconds; image 1
Frame 5; 0.20 seconds; image 1
Frame 6; 0.25 seconds; image 2
And so on...
Frame-dependent can look smoother if your computer can handle the framerate consistently. If lag happens it'll pause in its current state and restart when the lag stops, which makes the lag more noticeable. This alternative is slightly easier to implement since you just need to increment current_frame with 1 on each call, instead of dealing with the delta time (dt) and passing it to every object.
Sprites
Result

You could try modifying your sprite so that it swaps out its image for a different one inside update. That way, when the sprite is rendered, it'll look animated.
Edit:
Here's a quick example I drew up:
import pygame
import sys
def load_image(name):
image = pygame.image.load(name)
return image
class TestSprite(pygame.sprite.Sprite):
def __init__(self):
super(TestSprite, self).__init__()
self.images = []
self.images.append(load_image('image1.png'))
self.images.append(load_image('image2.png'))
# assuming both images are 64x64 pixels
self.index = 0
self.image = self.images[self.index]
self.rect = pygame.Rect(5, 5, 64, 64)
def update(self):
'''This method iterates through the elements inside self.images and
displays the next one each tick. For a slower animation, you may want to
consider using a timer of some sort so it updates slower.'''
self.index += 1
if self.index >= len(self.images):
self.index = 0
self.image = self.images[self.index]
def main():
pygame.init()
screen = pygame.display.set_mode((250, 250))
my_sprite = TestSprite()
my_group = pygame.sprite.Group(my_sprite)
while True:
event = pygame.event.poll()
if event.type == pygame.QUIT:
pygame.quit()
sys.exit(0)
# Calling the 'my_group.update' function calls the 'update' function of all
# its member sprites. Calling the 'my_group.draw' function uses the 'image'
# and 'rect' attributes of its member sprites to draw the sprite.
my_group.update()
my_group.draw(screen)
pygame.display.flip()
if __name__ == '__main__':
main()
It assumes that you have two images called image1.png and image2.png inside the same folder the code is in.

You should have all your sprite animations on one big "canvas", so for 3 20x20 explosion sprite frames you will have 60x20 image. Now you can get right frames by loading an area of the image.
Inside your sprite class, most likely in update method you should have something like this (hardcoded for simplicity, I prefer to have separate class to be responsible for picking the right animation frame). self.f = 0 on __init__.
def update(self):
images = [[0, 0], [20, 0], [40, 0]]
self.f += 1 if self.f < len(images) else 0
self.image = your_function_to_get_image_by_coordinates(images[i])

For an animated Sprite a list of images (pygame.Surface objects) must be generated. A different picture of the list is displayed in each frame, just like in the pictures of a movie. This gives the appearance of an animated object.
One way to get a list of images is to load an animated GIF (Graphics Interchange Format). Unfortunately, PyGame doesn't offer a function to load the frames of an animated GIF. However, there are several Stack Overflow answers that address this issue:
How can I load an animated GIF and get all of the individual frames in PyGame?
How do I make a sprite as a gif in pygame?
Pygame and Numpy Animations
One way is to use the popular Pillow library (pip install Pillow). The following function loads the frames of an animated GIF and generates a list of pygame.Surface objects:
from PIL import Image, ImageSequence
def loadGIF(filename):
pilImage = Image.open(filename)
frames = []
for frame in ImageSequence.Iterator(pilImage):
frame = frame.convert('RGBA')
pygameImage = pygame.image.fromstring(
frame.tobytes(), frame.size, frame.mode).convert_alpha()
frames.append(pygameImage)
return frames
Create a pygame.sprite.Sprite class that maintains a list of images. Implement an update method that selects a different image in each frame.
Pass the list of images to the class constructor. Add an index attribute that indicates the index of the current image in the list. Increase the index in the Update method. Reset the index if it is greater than or equal to the length of the image list (or use the modulo (%) operator). Get the current image from the list by subscription:
class AnimatedSpriteObject(pygame.sprite.Sprite):
def __init__(self, x, bottom, images):
pygame.sprite.Sprite.__init__(self)
self.images = images
self.image = self.images[0]
self.rect = self.image.get_rect(midbottom = (x, bottom))
self.image_index = 0
def update(self):
self.image_index += 1
if self.image_index >= len(self.images):
self.image_index = 0
self.image = self.images[self.image_index]
See also Load animated GIF and Sprite
Example GIF (from Animated Gifs, Animated Image):
Minimal example: repl.it/#Rabbid76/PyGame-SpriteAnimation
import pygame
from PIL import Image, ImageSequence
def loadGIF(filename):
pilImage = Image.open(filename)
frames = []
for frame in ImageSequence.Iterator(pilImage):
frame = frame.convert('RGBA')
pygameImage = pygame.image.fromstring(
frame.tobytes(), frame.size, frame.mode).convert_alpha()
frames.append(pygameImage)
return frames
class AnimatedSpriteObject(pygame.sprite.Sprite):
def __init__(self, x, bottom, images):
pygame.sprite.Sprite.__init__(self)
self.images = images
self.image = self.images[0]
self.rect = self.image.get_rect(midbottom = (x, bottom))
self.image_index = 0
def update(self):
self.image_index += 1
self.image = self.images[self.image_index % len(self.images)]
self.rect.x -= 5
if self.rect.right < 0:
self.rect.left = pygame.display.get_surface().get_width()
pygame.init()
window = pygame.display.set_mode((300, 200))
clock = pygame.time.Clock()
ground = window.get_height() * 3 // 4
gifFrameList = loadGIF('stone_age.gif')
animated_sprite = AnimatedSpriteObject(window.get_width() // 2, ground, gifFrameList)
all_sprites = pygame.sprite.Group(animated_sprite)
run = True
while run:
clock.tick(20)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
all_sprites.update()
window.fill((127, 192, 255), (0, 0, window.get_width(), ground))
window.fill((255, 127, 64), (0, ground, window.get_width(), window.get_height() - ground))
all_sprites.draw(window)
pygame.display.flip()
pygame.quit()
exit()

Related

Pygame keeps flickering even though display.update is only called once. Couldn't find an applicable answer from previous posts

I can't seem to figure out why my groups Pygame code keeps flickering when we run it. pygame.display.update is only called once, but our background seems to be potentially causing the issue.
Any an all help would be greatly appreciated! :)
Here's a link to my Replit with the code:
https://replit.com/#LukaLUSV6912/Santa-vs-Evil-Elves-Game?v=1
We are trying to get rid of the flickering from the Pygame animation and implement a winning and loosing screen based off the visibility of the elves(enemies) when the timer runs down to 0
Instructions: move with right + left arrow keys and fire snowballs with spacebar
'''"""
import pygame, random
pygame.init() # initialize pygame managers
# create a window
w = 600
h = 600
win = pygame.display.set_mode((w,h)) # define window variable
# pygame.display.set_caption("Read carefully.") # uncomment & edit to caption the window
#======================== Variables & functions ===================================================
img_size = (50,100)
img_size2 = (30,30)
# class that creates Santa
class Santa:
def __init__(self,color,x,y):
self.color = color
self.x = x
self.y = y
self.img = pygame.image.load("santaimg.png").convert_alpha()
self.img = pygame.transform.scale(self.img, img_size)
self.center=(x,y)
# The methods Right and Left allow Santa to move right and left and have boundries
def Right(self):
if self.x <551:
self.x +=20
self.center=(self.x,self.y)
# self.rect=self.img.get_rect(center=self.center)
def Left(self):
if self.x >0:
self.x -=20
self.center=(self.x,self.y)
# self.rect=self.img.get_rect(center=self.center)
# method that allows the bubbles to be drawn on the screen
def show(self):
# self.img = pygame.image.load("santaimg.png").convert()
# self.rect=self.img.get_rect(center=self.center)
win.blit(self.img, self.center)
#pygame.draw.ellipse(win,self.color,self.img)
#Class that creates the elves
class Elves():
def __init__(self, color, pos_x, pos_y):
self.color = color
self.pos_x= pos_x
self.pos_y = pos_y
self.square = 20
self.velocity = 0 # elves start stationary
self.box = pygame.Rect(self.pos_x,self.pos_y,self.square,self.square)
self.img2 = pygame.image.load("elfimg.png").convert_alpha()
self.img2 = pygame.transform.scale(self.img2,img_size2)
#self.area = pygame.Surface((self.square,self.square))
self.draw = True
# method that displays the elves
def Show(self):
if (self.draw == True):
win.blit(self.img2, (self.pos_x, self.pos_y))
# method that makes elves dissapear if snowball hits them
def dissapear(self, box):
global Dis
if (self.draw):
if self.box.colliderect(box): # box is the rect for elf
Dis += 1 # Each time user hits elf with snowball, value of Dis goes up one
self.draw = False
if (Dis == 20): # New victory image shows once 20 collisions occur
print("Victory! You Saved Christmas!")
global BG
BG = False # Makes it so background image stops refreshing
global SUp
SUp = False #Snowballs stop moving
gameDisplay = pygame.display.set_mode((w,h)) # Background Code - https://stackoverflow.com/questions/65009141/in-pygame-how-do-i-make-an-image-the-background#:~:text=Load%20a%20back%20ground%20image%20%28e.g.%20%27background.jpg%27%29%2C%20scale,pygame.transform.smoothscale%20%28background%2C%20gameDisplay.get_size%20%28%29%29%20gameDisplay.blit%20%28background%2C%20%280%2C%200%29%29
background = pygame.image.load('Victory.png').convert() #Retrieve the image
background = pygame.transform.smoothscale(background, gameDisplay.get_size()) # Sets the image size to the window size
gameDisplay.blit(background, (0, 0)) # Draws the image onto the screen
global running
running = False
# allows the elves to move up and down continuously
def Moving (self):
self.pos_y += self.velocity # Elves move based on velocity value
if self.pos_y >= 0 and self.pos_y < h/2.4: # Elves accelerate downwards when in top half of screen
self.velocity +=.3 # Velocity goes up, pos_y goes down
if self.pos_y > h/2.4: # Elves accelerate upwards when in bottom half of screen
self.velocity -= .3 # Velocity goes down, pos_y goes up
self.box = pygame.Rect(self.pos_x,self.pos_y,self.square,self.square)
#Class that creates snowballs
class Snow:
def __init__(self,color,x,y):
self.x = x
self.y = y
self.color = color
self.height = 15
self.length = 15
self.draw = True
self.snowy = pygame.draw.ellipse(win,self.color,(self.x,self.y,self.height,self.length))
# Show snowballs on screen
def show(self):
if (self.draw == True):
self.snowy = pygame.draw.ellipse(win,self.color,self.snowy)
#Snowballs go up
def upward(self):
global SUp
if SUp == True:
self.snowy.y -= 5
self.snowy = pygame.draw.ellipse(win,self.color,self.snowy)
# class that creates a timer
class Timer:
def __init__(self,limit):
self.limit = limit
self.clock = pygame.time.Clock()
self.start=pygame.time.get_ticks() # get starter tick at Timer instantiation
self.timeLeft = self.countDown()
def countDown(self):
'''Note: Will count down based on time passed, not frame rate, or call frequency.
Retuurns whole value time in seconds (int)'''
currTime=(self.start + pygame.time.get_ticks())/1500 #calculate how many seconds
# if self.timeLeft > 0:
return int(self.limit - currTime) # returns whole second numbers
#================================ Animation loop ===================================================
class Manager:
def __init__(self):
self.colors = ((255,255,255),(0,0,0),(173,216,230),(252,36,2),(0,0,255))
self.ElvesList = []
self.snowballs=[]
global SUp
SUp = True
global BG
BG = True
global Dis
Dis = 0
def main(self):
global running
running = True
clock = pygame.time.Clock()
# code that allows elves to show, everytime you play the game the elves have a different x and y
for i in range(20):
x = random.randint(0,w-20)
y= random.randint(0,h/3)
self.ElvesList.append(Elves(self.colors[2],x,y)) # instantiates the elves class
# calls Santa class in a variable called "SantaC"
SantaC= Santa(self.colors[3],w/2,h-50)
while running:
# clear window (comment out to have trace behind animation)
if (BG == True): # Refresh of image can be turned on/off
gameDisplay = pygame.display.set_mode((w,h)) # Background Code - https://stackoverflow.com/questions/65009141/in-pygame-how-do-i-make-an-image-the-background#:~:text=Load%20a%20back%20ground%20image%20%28e.g.%20%27background.jpg%27%29%2C%20scale,pygame.transform.smoothscale%20%28background%2C%20gameDisplay.get_size%20%28%29%29%20gameDisplay.blit%20%28background%2C%20%280%2C%200%29%29
background = pygame.image.load('SnowBackground.webp').convert() #Retrieve the image
background = pygame.transform.smoothscale(background, gameDisplay.get_size()) # Sets the image size to the window size
gameDisplay.blit(background, (0, 0)) # Draws the image onto the screen
# for every elf created in line 128 they will all show and move, methods of the elves are being called
for evil in self.ElvesList:
evil.Show()
evil.Moving()
# method that allows Santa to be shown
SantaC.show()
#borrowed code in order to display score in left corner (line 141-152)
font = pygame.font.Font("freesansbold.ttf",32)
textx = 10
texty = 10
timer = Timer(27) # 27 is used because there is a 2 second delay to run the code
timeLeft =timer.countDown()
# function that displays timer
def showtimer(x,y):
timing = font.render("Timer :" +str(timeLeft),True,(self.colors[4]))
win.blit(timing,(x,y))
showtimer(textx,texty) # determines where the timer will be displayed
# when the game reaches 0 seconds on the timer, "gameover" is displayed to the terminal
if timeLeft<=0:
gameDisplay = pygame.display.set_mode((w,h)) # Background Code - https://stackoverflow.com/questions/65009141/in-pygame-how-do-i-make-an-image-the-background#:~:text=Load%20a%20back%20ground%20image%20%28e.g.%20%27background.jpg%27%29%2C%20scale,pygame.transform.smoothscale%20%28background%2C%20gameDisplay.get_size%20%28%29%29%20gameDisplay.blit%20%28background%2C%20%280%2C%200%29%29
background = pygame.image.load('Defeat.png').convert() #Retrieve the image
background = pygame.transform.smoothscale(background, gameDisplay.get_size()) # Sets the image size to the window size
gameDisplay.blit(background, (0, 0)) # Draws the image onto the screen
for s in self.snowballs:
s.show()
s.upward()
# Sees if the rect of snowball and rect of elves has collided, if so the elf dissapears
for evil in self.ElvesList:
for s in self.snowballs:
evil.dissapear(s.snowy)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False # stops animation
if event.type == pygame.KEYDOWN:
# handles key press event
if event.key == pygame.K_SPACE:
# When the spacebar is pressed, a snowball will be shot out of Santas location
self.snowballs.append(Snow(self.colors[0],17.5+ SantaC.x,SantaC.y))
if event.key == pygame.K_RIGHT or event.key == pygame.K_d: # santa moves right if right key is clicked. Calls method from Santa's class.
SantaC.Right()
if event.key == pygame.K_LEFT or event.key == pygame.K_a: # santa moves left if left key is clicked. Calls method from Santa's class.
SantaC.Left()
# snowballs will be shown and move upward
#================== Animation control ===================
pygame.display.update()
clock.tick(30) # framerate in fps (30-60 is typical)
Manager().main()
The code is doing a lot of image and font loading repeatedly in the main loop. It's just re-loading the same thing, so this should be moved to before the loop. But I suspect the flickering is caused by the repeated calls to pygame.display.set_mode((w,h)) which is typically only called once, or when handling a window-resize event.
So maybe you want something like this for your main loop:
def showtimer(x,y,font,timeLeft,win):
timing = font.render("Timer :" +str(timeLeft),True,(self.colors[4]))
win.blit(timing,(x,y))
# Set the window size, and setup the timer
gameDisplay = pygame.display.set_mode((w,h))
timer = Timer(27) # 27 because there is a 2 second delay to run the code
timer_x = 10
timer_y = 10
# Load & scale any images, fonts, etc.
font = pygame.font.Font("freesansbold.ttf",32)
snowBackground = pygame.image.load('SnowBackground.webp').convert()
snowBackground = pygame.transform.smoothscale( snowBackground, gameDisplay.get_size())
defBackground = pygame.image.load('Defeat.png').convert()
defBackground = pygame.transform.smoothscale( defBackground, gameDisplay.get_size())
victBackground = pygame.image.load('Victory.png').convert()
victBackground = pygame.transform.smoothscale( victBackground, gameDisplay.get_size())
while running:
timeLeft = timer.countDown()
# clear window (comment out to have trace behind animation)
if (BG == True): # Refresh of image can be turned on/off
# when the game reaches 0 seconds on the timer, "gameover" is displayed to the terminal. Otherwise it's a snowy background
if Dis >= 20:
background = victBackground # player won!
elif timeLeft<=0:
background = defBackground # player defeated
else:
background = snowBackground # still playing
gameDisplay.blit( background, (0, 0) )
# paint & update Evil Elves
for evil in self.ElvesList:
evil.Show()
evil.Moving()
# method that allows Santa to be shown
SantaC.show()
# paint the timer
showtimer( timer_x, timer_y, font, timeLeft, win )
# flush the updates to the window
pygame.display.update()
# handle events & user input
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False # stops animation
# ... etc.
clock.tick( 60 )
I'm not 100% sure how the timer system works, so I just re-arranged it to how I guess it functions. Errors and Omissions should be expected.
The upshot of the changes above is that anything that can be pre-calculated is done outside of the loop. There absolutely no need to re-load and re-scale a background image to the window size every update loop. If you do need to handle window re-size events, do that in an event handler.

how can i add an image background? [duplicate]

new to pygame just wondering how i would go about adding a background image into the game itself? this is my code so far, i've been using the bg as a way to import my image but the py file itself refuses to load up.
import pygame
import sys
from pygame.locals import *
clock = pygame.time.Clock()
screen = pygame.display.set_mode((600,500))
bg = pygame.image.load("images\space.png")
pygame.mouse.set_visible(0)
ship = pygame.image.load("images\ship.png")
ship_top = screen.get_height() - ship.get_height()
ship_left = screen.get_width()/2 - ship.get_width()/2
screen.blit(ship, (ship_left,ship_top))
shot = pygame.image.load("images\shot.png")
shoot_y = 0
pygame.display.set_caption('galaxy invaders')
while True:
clock.tick(60)
screen.fill((r,0,0))
screen.blit(bg.(0,0))
x,y = pygame.mouse.get_pos()
screen.blit(ship, (x-ship.get_width()/2, ship_top))
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == MOUSEBUTTONDOWN:
shoot_y = 500
shoot_x = x
if shoot_y > 0:
screen.blit(shot, (shoot_x, shoot_y))
shoot_y -= 10
pygame.display.update()
For background I always make an image the size of my game window or smaller then before all of the images are displayed, I blit that image to 0,0.
bg = pygame.image.load("bg.png")
#INSIDE OF THE GAME LOOP
gameDisplay.blit(bg, (0, 0))
#REST OF ITEMS ARE BLIT'D TO SCREEN.
Hope this helps.
This problem can be easily solved. You will need an image the size of your screen for your background. Please remember to add pygame.init() at the beginning of your game to be able to start it and its abilities. A function for this picture can be used like this:
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) #call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
This will allow the program to load your image through this function when you call it like this:
BackGround = Background('background_image.png', [0,0])
And you will also need these two lines in your while loop:
screen.fill([255, 255, 255])
screen.blit(BackGround.image, BackGround.rect)
This will fill your screen white and put the background image over it but under your other sprites and objects.
Suggestions:
You should make another class for your other sprite (maybe the reason why the image is not appearing). An example could be like:
class Ship(pygame.sprite.Sprite):
def __init__(self, image_file, speed, 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
You could then "activate" it like this:
ship = Ship("images\ship.png", [a, b])
Select the coordinates for a and b. You can then blit the image on to the screen like this but after your background blit statement:
screen.blit(ship.image, ship.rect)
I hope this helps you!
First of all, none of this will work because you did not initialize Pygame after importing it. Also, the pictures won't be loaded because the backslash indicates an escape seqeunce. Lastly, you should fix your indentation.
import pygame
import sys
from pygame.locals import *
pygame.init() # initialize pygame
clock = pygame.time.Clock()
screen = pygame.display.set_mode((600,500))
# os.path.join properly forms a cross-platform relative path
# by joining directory names
bg = pygame.image.load(os.path.join("images", "space.png"))
pygame.mouse.set_visible(0)
ship = pygame.image.load(os.path.join("images", "ship.png"))
ship_top = screen.get_height() - ship.get_height()
ship_left = screen.get_width()/2 - ship.get_width()/2
screen.blit(ship, (ship_left,ship_top))
shot = pygame.image.load(os.path.join("images", "space.png"))
shoot_y = 0
pygame.display.set_caption('galaxy invaders')
# fix indentation
while True:
clock.tick(60)
screen.blit(bg, (0,0))
x,y = pygame.mouse.get_pos()
screen.blit(ship, (x-ship.get_width()/2, ship_top))
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == MOUSEBUTTONDOWN:
shoot_y = 500
shoot_x = x
if shoot_y > 0:
screen.blit(shot, (shoot_x, shoot_y))
shoot_y -= 10
pygame.display.update()

How to blit numbers in pygame?

Currently, I am working on a game in which you are asked a mathematical question such as 2 + 1 and numbers will fall from the top of the screen. The player has to click the right answer before it falls below the screen. However, I don't want to download images of the numbers because it would waste lots of time. Is there a way to draw or blit numbers instead? If there is, can the numbers be in a circle shape instead of a rectangle?
EDIT: I could create the numbers by displaying them as text. However, that would mean I have to create hundreds of different rectangles for each possible answer. My question then is, is there a way for me to tell Python to generate numbers and place them on a rectangle without me having to do it manually?
One solution would be to create a class in which you store the actual answer, the text surface and the rect for the collision detection. I use a pygame.sprite.Sprite subclass here, but you can do the same with a normal class. When a new question gets asked, you can just create a bunch of instances and add them to a sprite group, so that you can update them altogether in the main loop. You can use the pygame.Rect.collidepoint method for the collision detection.
import random
import pygame as pg
from pygame.math import Vector2
pg.init()
BLUE = pg.Color('dodgerblue1')
FONT = pg.font.Font(None, 42)
class Answer(pg.sprite.Sprite):
def __init__(self, pos, number):
super().__init__()
# Store the actual number, so that we can compare it
# when the user clicks on this object.
self.number = number
# Render the new text image/surface.
self.image = FONT.render(str(number), True, BLUE)
# A rect with the size of the surface, used for collision
# detection and rendering.
self.rect = self.image.get_rect(topleft=pos)
self.vel = Vector2(0, random.uniform(1, 4))
self.pos = Vector2(pos)
def update(self):
self.pos += self.vel
self.rect.center = self.pos
if self.rect.top > 480: # Screen bottom.
self.kill() # Remove the sprite from all groups.
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEBUTTONDOWN:
if event.button == 3: # Right mouse button.
# Add 20 numbers (Answer objects).
for _ in range(20):
number = random.randrange(100)
all_sprites.add(Answer((random.randrange(620), -20), number))
elif event.button == 1: # Left mouse button.
# See if the user clicked on a number.
for answer in all_sprites:
# event.pos is the mouse position.
if answer.rect.collidepoint(event.pos):
print(answer.number)
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
(Right click to spawn the numbers, left click to touch single numbers.)

How to cycle 3 images on a rect button?

Just started learning Python/Pygame watching videos and reading to learn . I would like to see a example code to cycle 3 images on a rect button from a mouse press and return to first image. Really I want the pictures to be three options and return different results. So be able to cycle image be able to select option and option when triggered execute choice.
Example
import pygame
pygame.init()
screen = pygame.display.set_mode((300,200))
# three images
images = [
pygame.Surface((100,100)),
pygame.Surface((100,100)),
pygame.Surface((100,100)),
]
images[0].fill((255,0,0))
images[1].fill((0,255,0))
images[2].fill((0,0,255))
# image size and position
images_rect = images[0].get_rect()
# starting index
index = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
# check mouse position and pressed button
if event.button == 1 and images_rect.collidepoint(event.pos):
# cycle index
index = (index+1) % 3
screen.blit(images[index], images_rect)
pygame.display.flip()
pygame.quit()
Example using class - to create many buttons
import pygame
class Button(object):
def __init__(self, position, size):
self._images = [
pygame.Surface(size),
pygame.Surface(size),
pygame.Surface(size),
]
self._images[0].fill((255,0,0))
self._images[1].fill((0,255,0))
self._images[2].fill((0,0,255))
self._rect = pygame.Rect(position, size)
self._index = 0
def draw(self, screen):
screen.blit(self._images[self._index], self._rect)
def event_handler(self, event):
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1 and self._rect.collidepoint(event.pos):
self._index = (self._index+1) % 3
pygame.init()
screen = pygame.display.set_mode((320,110))
button1 = Button((5, 5), (100, 100))
button2 = Button((110, 5), (100, 100))
button3 = Button((215, 5), (100, 100))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
button1.event_handler(event)
button2.event_handler(event)
button3.event_handler(event)
button1.draw(screen)
button2.draw(screen)
button3.draw(screen)
pygame.display.flip()
pygame.quit()
If I understood the question correctly, you need a single button that changes the look every time you click on it, and changes its relative function accordingly.
You should be able to solve your problem by creating a class that takes two list and a counter
1) list of images
2) list of functions
3) the counter tells you which image/function is selected.
The functions needs to be built in the class, but you can provide the image that you want in the class argument (actually, you could pass them as arguments, but I don't think is worth it).
Here is the code, I commented some lines with their intended meaning (in line comments)
import pygame
import sys
pygame.init()
width = 600
height = 400
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Magic Buttons")
background = pygame.Surface(screen.get_size())
clock = pygame.time.Clock()
class Buttons:
def __init__(self, posX, posY, image1, image2, image3):
self.image_list = [image1, image2, image3] # static list of images
self.function_list = [self.button_function_1,self.button_function_2,self.button_function_3 ]
self.rect_position = (posX, posY) # this is a tuple to identify the upper left corner of the rectangle of the image
self.button_type = 0 # initial value of the button, both for the function and the image
self.image = pygame.image.load(self.image_list[0]) #default image, index number 0 of image_list
self.rect = pygame.Rect(posX, posY, self.image.get_width(), self.image.get_height()) # create a rectangle object same size of the images
def check(self, pos):
if self.rect.collidepoint(pos) ==True:
self.change_button()
else:
pass
def change_button(self):
self.button_type = (self.button_type +1)%3
self.image = pygame.image.load(self.image_list[self.button_type ]) # load the image relative to button_type
self.function_list[self.button_type -1]() # execute the function relative to the new value of button_type
self.draw_button()
def draw_button(self):
screen.blit(self.image, self.rect_position) # blit the new button image
def button_function_1(self):
print ("function 1 in action")
def button_function_2(self):
print ("function 2 in action")
def button_function_3(self):
print ("function 3 in action")
multibutton = Buttons(100,100,"button1.png","button2.png","button3.png") # create an istance of the button in the x=100, y = 100, with the three image button
while True:
background.fill((0,0,0))
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONUP:
pos = pygame.mouse.get_pos() # fetch the position of the mouse
multibutton.check(pos) # check if the mouse is on the button
multibutton.draw_button()
pygame.display.flip()

Spawning Multiple of the same sprite

I am currently working on a game called Table Wars, a turn based strategy game for two players. Progress has been going smoothly, until I ran into a problem with spawning units.
The program won't spawn multiple of the same unit, or respawn new ones after the old ones die.
Here is some information that may help:
Each class is stored in a variable: (redI = Red_Infantry())
All functions are stored in the main loop.
The sprite classes have hard-coded X and Y values, used when spawning units and moving units.
What should I do?
As requested, here is the class for the Red Infantry:
class Red_Infantry(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_image('Soldier_red.png', -1)
self.selected = 0
self.area = screen.get_rect()
self.rect.topleft = (100, 300)
self.health = 100 #Soldiers are have mediocre toughness.
self.attack_damage = 25
self.range_maximum = 20 #in pixels, this is melee range
self.range_minimum = 0
self.update()
def update(self):
if self.health <= 0:
self.kill()
and the code to spawn this unit:
if spawned_units < 3 and PHASE == 1 and TURN == 'Red':
if REDGOLD < 10:
print "Out of money! Moving to Phase 2!"
PHASE = 2
spawned_units = 0
elif event.type == KEYDOWN and event.key == K_1:
if REDGOLD >= 10 and REDCOMMAND >= 5:
Sprites.append(redI)
REDGOLD -= 10
REDCOMMAND -= 5
spawned_units = spawned_units + 1
else:
print "Not enough gold!"
This is similar style with all units. It performs correctly the first time, but not in the second, third, and so on, meaning I can only have one Soldier. Also, when that soldier dies via self.kill, it won't come back if I try to spawn it.
The part of the spawn procedure you posted doesn't create any new instances. Unless redI is declared as a new Red_Infantry somewhere else, you need to modify the code to create a new instance of Red_Infantry every time you want to spawn a soldier.
sprites.append(Red_Infantry())
To update the sprites:
for sprite in sprites:
sprite.update()
Put movement and other state changes in the update method. This is what pygame expects, but you can use a different style if you want. The main point is you must have multiple instances of Red_Infantry in order to see multiple sprites.
You could also use pygame's Group class instead of a simple list to hold the sprites.
Here's a full example that uses Group instead of list. In the example, an Enemy is spawned each time a key is pressed. Each Enemy prints its unique ID to stdout.
import sys
import random
import pygame
from pygame.locals import *
def main():
pygame.init()
screen = pygame.display.set_mode((480, 320))
enemies = pygame.sprite.Group()
while True:
for event in pygame.event.get():
if event.type == KEYDOWN:
enemies.add(Enemy(screen))
elif event.type == QUIT:
sys.exit()
enemies.update()
screen.fill(pygame.Color("black"))
enemies.draw(screen)
pygame.display.update()
class Enemy(pygame.sprite.Sprite):
def __init__(self, screen):
pygame.sprite.Sprite.__init__(self)
print "created a new sprite:", id(self)
self.image = pygame.image.load("sprite.png")
self.rect = self.image.get_rect()
self.rect.move_ip(random.randint(0, screen.get_width()),
random.randint(0, screen.get_height()))
def update(self):
self.rect.move_ip(random.randint(-3, 3), 0)
if __name__ == "__main__":
main()