Make stars blink in Pygame - python

The following code generates random x,y coordinates, appending the coordinates to a list, then runs a for-loop through the list to blit star images to the screen. The same stars are constantly being redrawn while the code is running, but they're in the same location, so the screen looks static. Here is the code.
import time
import pygame
from random import randint
pygame.init()
display_width = 800
display_height = 600
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption("My God, it's full of stars!")
med_star_img = pygame.image.load('images/medium_star.png')
tiny_star_img = pygame.image.load('images/tiny_star.png')
black = (0,0,0)
white = (255,255,255)
gray = (50,50,50)
tiny_stars_width = tiny_star_img.get_width()
tiny_stars_height = tiny_star_img.get_height()
med_stars_width = med_star_img.get_width()
med_stars_height = med_star_img.get_height()
tiny_stars_location = []
med_stars_location = []
clock = pygame.time.Clock()
running = True
gameDisplay.fill(gray)
# create random coordinates for stars
for i in range(25):
tiny_stars_location.append(pygame.Rect(randint(1,800),randint(1,600),tiny_stars_width,tiny_stars_height))
for i in range(10):
med_stars_location.append(pygame.Rect(randint(1,800),randint(1,600),med_stars_width,med_stars_height))
def make_med_star(x,y):
gameDisplay.blit(med_star_img, (x,y))
def make_tiny_star(x,y):
gameDisplay.blit(tiny_star_img, (x,y))
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
for star in med_stars_location:
make_med_star(star.x,star.y)
for star in tiny_stars_location:
make_tiny_star(star.x,star.y)
pygame.display.flip()
time.sleep(1)
clock.tick(60)
pygame.quit()
quit()
I'd like the stars to occasionally blink, which I think would be done by randomly removing one or two stars from the stars_location list during the main loop before adding them back when the loop comes back around. The loop probably executes very fast, but I think I can add a delay. Any help here would be greatly appreciated.

You can save a boolean for each star. If the value is True the star is visible.
Example for saving star data: (I wouldn't use the pygame.Rect and instead a simple list)
for i in range(25):
tiny_stars_location.append([randint(1,800),randint(1,600),tiny_stars_width,tiny_stars_height, True])
Also you should not use time.sleep in a pygame program. It does not delay a task. I delays the program. So you have to wait a whole second to close the program or interact with it in any way.
A lazy but working approach would be just to use randomness. You could just get a random number with random.randint(0, n), if it is 0 you set the boolean value of the star to True or False, depending on what state it currently is. You can set the variable n to some number like
n = maximum fps * average seconds before the star is hidden/shown
An other thing you probably just forgot is to clear the window in the loop with gameDisplay.fill(gray).
Your finished code could look like this:
import time
import pygame
from random import randint
pygame.init()
display_width = 800
display_height = 600
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption("My God, it's full of stars!")
med_star_img = pygame.image.load('images/medium_star.png')
tiny_star_img = pygame.image.load('images/tiny_star.png')
black = (0,0,0)
white = (255,255,255)
gray = (50,50,50)
tiny_stars_width = tiny_star_img.get_width()
tiny_stars_height = tiny_star_img.get_height()
med_stars_width = med_star_img.get_width()
med_stars_height = med_star_img.get_height()
tiny_stars_location = []
med_stars_location = []
clock = pygame.time.Clock()
running = True
gameDisplay.fill(gray)
# create random coordinates for stars
for i in range(25):
tiny_stars_location.append([randint(1,800),randint(1,600),tiny_stars_width,tiny_stars_height, True])
for i in range(10):
med_stars_location.append([randint(1,800),randint(1,600),med_stars_width,med_stars_height, True])
def make_med_star(x,y):
gameDisplay.blit(med_star_img, (x,y))
def make_tiny_star(x,y):
gameDisplay.blit(tiny_star_img, (x,y))
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
for star in med_stars_location:
if star[2]:
make_med_star(star[0], star[1])
if not randint(0, 300): # 300 / 60(fps) = 5 -> every 5 seconds on average
star[2] = not star[2] # inverse
for star in tiny_stars_location:
if star[2]:
make_tiny_star(star[0], star[1])
if not randint(0, 300): # 300 / 60(fps) = 5 -> every 5 seconds on average
star[2] = not star[2] # inverse
pygame.display.flip()
#time.sleep(1) <- never do this in pygame
clock.tick(60)
gameDisplay.fill(gray) # reset display
pygame.quit()

Related

Having an image appear multiple times Pygame

I have a single battery image appearing once here and it moves from right to left then goes away. I want batteries constantly coming and going until the player hits one of them.
My question is how do I get the batteries to keep on coming until a player hits it? I want them to appear maybe 100 units apart.
from pygame import *
import os
import random
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d, %d" %(0, 0)
init()
#set screen size
size = width, height = 800, 600
screen = display.set_mode(size)
#set fonts
fontGame=font.SysFont("Times New Roman", 30)
fontBack=font.SysFont("Ariel", 30)
fontTitle=font.SysFont("Ariel", 100)
fontResearch=font.SysFont ("Times New Roman", 18)
#set button and page to 0
button = 0
page=0
#setting colours
BLACK = (0, 0, 0)
RED = (255,0,0)
GREEN = (0, 255, 0)
BLUE = (106,186,232)
#loading image
backgroundPic=image.load("Background.jpg")
backgroundGame=image.load("gameBackground.jpg")
backgroundGame=transform.scale(backgroundGame,(800,600))
battery=image.load("Battery.png")
battery=transform.scale(battery,(100,100))
backgroundx=0
playerPic=image.load("player.png")
playerPic=transform.scale(playerPic,(70,70))
batteryx=[]
#defining what is going to be shown on the screen
def drawScene(screen, button,page,locationx,locationy):
global batteryx
mx, my = mouse.get_pos() #will get where the mouse is
#if the user does nothing
if page==0:
draw.rect(screen, BLACK, (0,0, width, height))
screen.fill(BLACK)
rel_backgroundx= backgroundx % backgroundGame.get_rect().width
screen.blit(backgroundGame, (rel_backgroundx - backgroundGame.get_rect().width,0))
if rel_backgroundx < width:
screen.blit (backgroundGame, (rel_backgroundx,0))
screen.blit(playerPic,(locationx,locationy))
screen.blit(battery,(batteryx,420))
batteryx-=1
display.flip()
return page
#def collision (battery, playerPic):
#if battery.colliderect(playerPic):
#return True
#return False
running = True
myClock = time.Clock()
KEY_LEFT= False
KEY_RIGHT= False
KEY_UP= False
KEY_DOWN= False
locationx=0
jumping=False
accel=20
onGround= height-150
locationy=onGround
batteryx=random.randrange(50,width,10)
# Game Loop
while running:
button=0
print (KEY_LEFT, KEY_RIGHT)
for evnt in event.get(): # checks all events that happen
if evnt.type == QUIT:
running=False
if evnt.type == MOUSEBUTTONDOWN:
mx,my=evnt.pos
button = evnt.button
if evnt.type== KEYDOWN:
if evnt.key==K_LEFT:
KEY_LEFT= True
KEY_RIGHT= False
if evnt.key==K_RIGHT:
KEY_RIGHT= True
KEY_LEFT= False
if evnt.key==K_UP and jumping==False:
jumping=True
accel=20
if evnt.key== K_DOWN:
KEY_DOWN= True
KEY_UP= False
if evnt.type==KEYUP:
if evnt.key==K_LEFT:
KEY_LEFT= False
if evnt.key==K_RIGHT:
KEY_RIGHT= False
if evnt.key==K_DOWN:
KEY_DOWN=False
if KEY_LEFT== True:
locationx-=10
backgroundx+=10
if KEY_RIGHT== True:
locationx+=10
backgroundx-=10
if jumping==True:
locationy-=accel
accel-=1
if locationy>=onGround:
jumping=False
locationy=onGround
#player cannot move off screen
if locationx<0:
locationx=0
if locationx>400:
locationx=400
#if collision(battery, playerPic)==True:
#screen.fill(BLACK)
page=drawScene(screen,button,page,locationx,locationy)
myClock.tick(60) # waits long enough to have 60 fps
if page==6: #if last button is clicked program closes
running=False
quit()
Create a list of rects which serve as the positions of the batteries. Use for loops to update the rects and blit the battery image at the rects. Remove rects that have left the screen and append new rects to create new batteries
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
BG_COLOR = pg.Color('gray12')
battery_image = pg.Surface((30, 50))
battery_image.fill(pg.Color('sienna1'))
# Append pygame.Rect objects to this list.
batteries = []
batteries.append(battery_image.get_rect(topleft=(700, 100)))
battery_speed = -5
battery_timer = 40
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
# A simple frame based timer.
battery_timer -= 1
if battery_timer <= 0:
battery_timer = 40
# After 40 frames a new rect gets appended to the list.
batteries.append(battery_image.get_rect(topleft=(700, 100)))
temp_list = []
# Iterate over the rects to update them.
for battery_rect in batteries:
battery_rect.x += battery_speed
# Rects with x <= 50 won't be appended to the temp_list.
if battery_rect.x > 50:
temp_list.append(battery_rect)
# Assign the list with the remaining rects to the batteries variable.
batteries = temp_list
# Blit everything.
screen.fill(BG_COLOR)
for battery_rect in batteries:
screen.blit(battery_image, battery_rect)
pg.display.flip()
clock.tick(30)
pg.quit()

Pygame Rectangle and Mouse pos Collidepoint not working

I'm making a basic game where I have a surface and everytime I click on the surface it moves 5 pixels to the right. The program is working just fine without the checkCollide(event) function, but when I put the that condition it doesn't move. What is wrong?
My code until now is this
import pygame, sys
from pygame.locals import *
pygame.init()
DISPLAYSURF = pygame.display.set_mode((300,300))
def checkCollide(event):
k = 0
a,b = event.pos
x = P1[0].get_rect()
if x.collidepoint(a,b):
return True
return False
CP1 = [(150, 150)
,(155, 150)
,(160, 150)
,(165, 150)
,(170, 150)
,(175, 150)
,(180, 150)
,(185, 150)
,(190, 150)]
statp1_1 = 0
WHITE = (255,255,255)
DISPLAYSURF.fill(WHITE)
while True: # the main game loop
P1 = [pygame.image.load('PAzul.png'),CP1[statp1_1],statp1_1]
DISPLAYSURF.blit(P1[0], P1[1])
e = pygame.event.get()
for event in e:
if event.type == MOUSEBUTTONUP:
a = checkCollide(event)
if a:
DISPLAYSURF.fill(WHITE)
statp1_1 +=1
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
Thank you
Check your logic in these lines of your function:
x = P1[0][0].get_rect()
if x.collidepoint(a,b):
return True
return False
Your code hinges on this bit:
a = checkCollide(event)
if a:
DISPLAYSURF.fill(WHITE)
So you're never evaluating this piece to be true.
I just realized what was wrong. When I do x = P1[0].get_rect() it creates a surface with topleft at (0,0).
What I needed to do was change the position of the rectangle using x.topleft = P1[1]
I've got some tips for you. First store the rect in the P1 list (it contains only the image and the rect in the following example, but maybe you could also add the statp1_1 index to it). Now we can just move this rect, if the user clicks on it (in the example I set the topleft attribute to the next point). Read the comments for some more tips. One thing you need to fix is to prevent the game from crashing when the statp1_1 index gets too big.
import sys
import pygame
pygame.init()
DISPLAYSURF = pygame.display.set_mode((300, 300))
WHITE = (255, 255, 255)
# Don't load images in your while loop, otherwise they have to
# be loaded again and again from your hard drive.
# Also, convert loaded images to improve the performance.
P1_IMAGE = pygame.image.load('PAzul.png').convert() # or .convert_alpha()
# Look up `list comprehension` if you don't know what this is.
CP1 = [(150+x, 150) for x in range(0, 41, 5)]
statp1_1 = 0
# Now P1 just contains the image and the rect which stores the position.
P1 = [P1_IMAGE, P1_IMAGE.get_rect(topleft=CP1[statp1_1])]
clock = pygame.time.Clock() # Use this clock to limit the frame rate.
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONUP:
if P1[1].collidepoint(event.pos):
print('clicked')
statp1_1 += 1
# Set the rect.topleft attribute to CP1[statp1_1].
P1[1].topleft = CP1[statp1_1]
DISPLAYSURF.fill(WHITE)
DISPLAYSURF.blit(P1[0], P1[1]) # Blit image at rect.topleft.
pygame.display.update()
clock.tick(30) # Limit frame rate to 30 fps.

How do I make pygame display the time and change it when the time changes using fonts?

I have got a working digital clock in python but I am stuck trying to make it a visual in pygame.
The code for the clock works but it just doesn't display anything, even though I have used .blit to do so.
The idea is to have the timer show every minute (Second), Hour(every 60 seconds) and Days (Every 12 in game hours). This is then to appear on the top left.
Here is my code:
import sys, pygame, random, time
pygame.init()
#Screen
size = width, height = 1280, 720 #Make sure background image is same size
screen = pygame.display.set_mode(size)
done = False
#Animation
A1=0
A2=0
#Time Info
Time = 0
Minute = 0
Hour = 0
Day = 0
counter=0
#Colour
Black = (0,0,0)
White = (255, 255, 255)
#Fonts
Font = pygame.font.SysFont("Trebuchet MS", 25)
#Day
DayFont = Font.render("Day:"+str(Day),1, Black)
DayFontR=DayFont.get_rect()
DayFontR.center=(985,20)
#Hour
HourFont = Font.render("Hour:"+str(Hour),1, Black)
HourFontR=HourFont.get_rect()
HourFontR.center=(1085,20)
#Minute
MinuteFont = Font.render("Minute:"+str(Minute),1, Black)
MinuteFontR=MinuteFont.get_rect()
MinuteFontR.center=(1200,20)
#Images
Timer=pygame.time.get_ticks
Clock = pygame.time.Clock()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(White)
#Timer
if Time<60:
time.sleep(1)
Minute=Minute+1
if Minute == 60:
Hour=Hour+1
Minute=0
if Hour==12:
Day=Day+1
Hour=0
if A1==0:
A1=A1+1
A2=A2+1
time.sleep(1)
if A1==1 or A2==1:
A2=A2-1
A1=A1-1
if A1==1:
screen.blit(MinuteFont, MinuteFontR)
screen.blit(HourFont, HourFontR)
screen.blit(DayFont, DayFontR)
if A2==0:
screen.fill(pygame.Color("White"), (1240, 0, 40, 40))
pygame.display.flip()
Clock.tick(60)
pygame.quit()
Sorry if this is nooby, but any help is appreciated
Barring all other problems, I'm not sure what your A1 and A2 are supposed to be, but
if A1==0: #true for the first run through
A1=A1+1 #A1 = 1
A2=A2+1
time.sleep(1)
if A1==1 or A2==1: #always true, since A1==1
A2=A2-1
A1=A1-1 #A1 = 0
this will always increase A1 and set it back to zero in the same step, essentially doing nothing, so you never get to the part if A1==1 where you might blit the time.
Apart from that, Font.render() "creates a new Surface with the specified text rendered on it." (cf. the documentation) This means you have to re-render the font every time you want to update the text, otherwise you keep blitting the same (unchanged) surface again and again. You will also need to adjust the rect to account for the text being wider then the time goes up from one digit to two.
The easiest way to keep track of time might be to use a custom user event that's fired every second in the event queue like so:
import pygame
pygame.init()
#Screen
size = width, height = 1280, 720 #Make sure background image is same size
screen = pygame.display.set_mode(size)
done = False
#Time Info
Time = 0
Minute = 0
Hour = 0
Day = 0
counter=0
#Colour
Black = (0,0,0)
White = (255, 255, 255)
#Fonts
Font = pygame.font.SysFont("Trebuchet MS", 25)
#Day
DayFont = Font.render("Day:{0:03}".format(Day),1, Black) #zero-pad day to 3 digits
DayFontR=DayFont.get_rect()
DayFontR.center=(985,20)
#Hour
HourFont = Font.render("Hour:{0:02}".format(Hour),1, Black) #zero-pad hours to 2 digits
HourFontR=HourFont.get_rect()
HourFontR.center=(1085,20)
#Minute
MinuteFont = Font.render("Minute:{0:02}".format(Minute),1, Black) #zero-pad minutes to 2 digits
MinuteFontR=MinuteFont.get_rect()
MinuteFontR.center=(1200,20)
Clock = pygame.time.Clock()
CLOCKTICK = pygame.USEREVENT+1
pygame.time.set_timer(CLOCKTICK, 1000) # fired once every second
screen.fill(White)
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == CLOCKTICK: # count up the clock
#Timer
Minute=Minute+1
if Minute == 60:
Hour=Hour+1
Minute=0
if Hour==12:
Day=Day+1
Hour=0
# redraw time
screen.fill(White)
MinuteFont = Font.render("Minute:{0:02}".format(Minute),1, Black)
screen.blit(MinuteFont, MinuteFontR)
HourFont = Font.render("Hour:{0:02}".format(Hour),1, Black)
screen.blit(HourFont, HourFontR)
DayFont = Font.render("Day:{0:03}".format(Day),1, Black)
screen.blit(DayFont, DayFontR)
pygame.display.flip()
Clock.tick(60) # ensures a maximum of 60 frames per second
pygame.quit()
I've zero-padded the minutes, hours and days so you don't have to recalculate the rectangle every time. You could also optimize the draw code by only drawing hours and days if they have changed (in the respective if statements).
To see other methods of how to handle timed events, check out Do something every x (milli)seconds in pygame.

Python/Pygame - Create "End credits" like the ones at the end of a movie

I'm trying to create "end credits" like the ones at the end of a movie, using pygame. I've googled for other ways to achieve this using python, but I haven't found any yet.
I've almost achieved this with the following code: http://pastebin.com/nyjxeDYQ
#!/usr/bin/python
import time
import threading
import pygame
from pygame.locals import *
# Initialise pygame + other settings
pygame.init()
pygame.fastevent.init()
event_get = pygame.fastevent.get
pygame.display.set_caption('End credits')
screen = pygame.display.set_mode((1920, 1080))
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((255, 255, 255))
fontsize = 40
font = pygame.font.SysFont("Arial", fontsize)
x = 0
def main():
global x
credit_list = ["CREDITS - The Departed"," ","Leonardo DiCaprio - Billy","Matt Damon - Colin Sullivan", "Jack Nicholson - Frank Costello", "Mark Wahlberg - Dignam", "Martin Sheen - Queenan"]
going = True
while going:
events = event_get()
for e in events:
if e.type in [QUIT]:
going = False
if e.type in [KEYDOWN] and e.key == pygame.K_ESCAPE:
going = False
# Loop that creates the end credits
ypos = screen.get_height()
while ypos > (0 - len(credit_list)*50) and x == 0: # Loop through pixel by pixel, screenheight + height of all the textlines combined
drawText(credit_list,ypos)
ypos = ypos - 1
x = 1
pygame.quit()
def drawText(text,y):
for line in text:
text = font.render(line, 1, (10, 10, 10))
textpos = text.get_rect()
textpos.centerx = background.get_rect().centerx
background.blit(text, (textpos.x,y))
y = y + 45
# Blit all the text
screen.blit(background, (0, 0))
pygame.display.flip()
time.sleep(0.0001) # Sleep function to adjust speed of the end credits
# Blit white background (else all the text will stay visible)
background.fill((255, 255, 255))
screen.blit(background, (0, 0))
pygame.display.flip()
if __name__ == '__main__': main()
The problem is that the scrolling text is flickering. This is because I use a time.sleep()-function to control the speed of the scrolling. When I use a value like 0.04 sec, it works pretty well, but the text moves too slow and there is still a bit of flickering. When I use a much lower value, like: 0.001 sec, the text is moving at a speed that I like, but there is a lot more flickering going on.
There is another value I can use to adjust the speed of the scrolling: the number of pixels to move. But when I set this to anything higher than 1, the scrolling isn't smooth anymore.
Does anyone know a solution to this problem? I don't necessarily have to use pygame, I do have to use python though.
Many thanks in advance!
Albrecht
Here are some simpe rule you should follow that will help you with your problem:
Don't call pygame.display.flip() more than once per frame
Don't use time.sleep() to control the speed of something in your application
Use a Clock to control the framerate
Here's a cleaned up, minimal working example:
#!/usr/bin/python
import pygame
from pygame.locals import *
pygame.init()
pygame.display.set_caption('End credits')
screen = pygame.display.set_mode((800, 600))
screen_r = screen.get_rect()
font = pygame.font.SysFont("Arial", 40)
clock = pygame.time.Clock()
def main():
credit_list = ["CREDITS - The Departed"," ","Leonardo DiCaprio - Billy","Matt Damon - Colin Sullivan", "Jack Nicholson - Frank Costello", "Mark Wahlberg - Dignam", "Martin Sheen - Queenan"]
texts = []
# we render the text once, since it's easier to work with surfaces
# also, font rendering is a performance killer
for i, line in enumerate(credit_list):
s = font.render(line, 1, (10, 10, 10))
# we also create a Rect for each Surface.
# whenever you use rects with surfaces, it may be a good idea to use sprites instead
# we give each rect the correct starting position
r = s.get_rect(centerx=screen_r.centerx, y=screen_r.bottom + i * 45)
texts.append((r, s))
while True:
for e in pygame.event.get():
if e.type == QUIT or e.type == KEYDOWN and e.key == pygame.K_ESCAPE:
return
screen.fill((255, 255, 255))
for r, s in texts:
# now we just move each rect by one pixel each frame
r.move_ip(0, -1)
# and drawing is as simple as this
screen.blit(s, r)
# if all rects have left the screen, we exit
if not screen_r.collidelistall([r for (r, _) in texts]):
return
# only call this once so the screen does not flicker
pygame.display.flip()
# cap framerate at 60 FPS
clock.tick(60)
if __name__ == '__main__':
main()

Generating Platforms without Pygame Sprite class

How can I go about generating platforms to corresponding "E"'s of the level without the Sprite class? I tried some different methods but none of them seemed to work. Basically, the idea is that the "E"'s should just make 50x50 blocks in a row.
import pygame
pygame.init()
display_width = 900
display_height = 600
size = (display_width, display_height)
black = (0,0,0)
white = (255,255,255)
clock = pygame.time.Clock()
screen = pygame.display.set_mode(size)
platx = 350
platy = 150
platw = 50
plath = 50
clock = pygame.time.Clock()
platx = 600
platy = 350
platw = 50
plath = 50
level = ["E","E","E","E","E","E","E","E","E","E"]
class Platform:
def __init__(self, platx, platy, platw, plath):
self.platx = platx
self.platy = platy
self.platw = platw
self.plath = plath
def draw(self):
pygame.draw.rect(screen, black, [self.platx, self.platy, self.platw, self.plath])
platforms = Platform(platx, platy, platw, plath)
game_exit = False
while not game_exit:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_exit = True
#why doesnt a loop like this work?
#for i in level:
# if i == "E":
# platforms.draw()
# platx += display_width *.1
#shouldn't it be placing platforms 1/10 of the display_width from the starting platx?
screen.fill(white)
pygame.display.update()
If I understand your code correctly, this is the code that needs to be fixed:
#why doesnt a loop like this work?
#for i in level:
# if i == "E":
# platforms.draw()
# platx += display_width *.1
#shouldn't it be placing platforms 1/10 of the display_width from the starting platx?
The problem with this is that platx was already passed as an argument to Platform.__init__. Since the variable platform.platx no longer has a weakref to the global platx (being passed as an argument), they no longer match. Thus, instead of incrementing platx, `platform.platx should be incremented.
Also, you need to reset platform.platx after drawing each row.

Categories

Resources