I got a problem with a python programm, using pygame. I want to make a sun in a solar system spin (rotate). It works partially: the problem about some strange "stuttering" of the sun when running the programm. This stuttering occurs again and again, in a loop. Heres the code:"
# -*- coding: utf-8 -*-
import pygame as pg
import pygame.locals as local
import sys
def rot_center(image, angle):
"""rotate an image while keeping its center and size"""
orig_rect = image.get_rect()
rot_image = pg.transform.rotate(image, angle)
rot_rect = orig_rect.copy()
rot_rect.center = rot_image.get_rect().center
rot_image = rot_image.subsurface(rot_rect).copy()
return rot_image
pg.init()
#deklaration
xres=0
yres=0
#auflösungseinstellungen
try:
xres=int(sys.argv[1]) #auflösung von der kommandozeile, parameter 1
yres=int(sys.argv[2]) #auflösung von der kammondozeile, parameter 2
except IndexError:
xres=800
yres=600
screen = pg.display.set_mode((xres,yres)) #coords nicht hart coden, variablen nutzen
pg.display.set_caption("future rpg prepreprepalphawhatever")
pg.mouse.set_visible(1)
pg.key.set_repeat(1,30)
clock = pg.time.Clock()
running = 1
rotation_stat = 0.0
while running:
planet01 = pg.image.load("grafik/menu/planet02.png")
planet01.set_colorkey((251,0,250), local.RLEACCEL) #load planet01
sun = pg.image.load("grafik/menu/sun.png") #load sun
bg = pg.image.load("grafik/menu/bg.png") #load background
#den hintergrund skalieren, falls auflösung zu hoch
sizedbg = pg.transform.smoothscale(bg, (xres, yres))
rotation_stat += 1
clock.tick(30)
screen.fill((0,0,0))
screen.blit(sizedbg, (0,0))
screen.blit(planet01, (xres/5-planet01.get_width()/2,yres/2-planet01.get_height()/2))
orig_rect = sun.get_rect()
sun = pg.transform.rotate(sun, rotation_stat)
screen.blit(sun, (xres/2-sun.get_width()/2,yres/2-sun.get_height()/2))
rot_rect = orig_rect.copy()
rot_rect.center = sun.get_rect().center
sun = sun.subsurface(rot_rect).copy()
for event in pg.event.get():
if event.type == local.QUIT:
running = 0
if event.type == local.KEYDOWN:
if event.key == local.K_ESCAPE:
pg.event.post(pg.event.Event(local.QUIT))
pg.display.flip()
Put the code for loading the images outside the loop
I'm not entirely sure but it seems like you are loading the images every time your loop runs which is heavily intensive on your CPU
I have rewritten my answer based on your comments and after having glanced over the pygame docs
You really do only need to load your images once. What I am doing here is always rotating the originally loaded sun to a new surface, using the incrementing rotation. The rotation always wraps back to 0 degrees. As for the jitter, I am sure that related to an aliasing artifact and maybe even your centering math. But my goal was to address the biggest performance hit which was reading your sun image from disk every frame (30 times a second)
import pygame as pg
import pygame.locals as local
import sys
def main():
pg.init()
xres=800
yres=600
try:
xres=int(sys.argv[1])
yres=int(sys.argv[2])
except IndexError:
pass
screen = pg.display.set_mode((xres,yres))
pg.display.set_caption("future rpg prepreprepalphawhatever")
pg.mouse.set_visible(1)
pg.key.set_repeat(1,30)
clock = pg.time.Clock()
rotation_stat = 0.0
planet01 = pg.image.load("earth.png")
planet01.set_colorkey((251,0,250), local.RLEACCEL) #load planet01
sun = pg.image.load("sun.png") #load sun
bg = pg.image.load("bg.png") #load background
sizedbg = pg.transform.smoothscale(bg, (xres, yres))
planet_pos = (xres/5-planet01.get_width()/2,yres/2-planet01.get_height()/2)
running = 1
xres_cent, yres_cent = xres/2, yres/2
while running:
clock.tick(30)
for event in pg.event.get():
if event.type == local.QUIT:
running = 0
if event.type == local.KEYDOWN:
if event.key == local.K_ESCAPE:
pg.event.post(pg.event.Event(local.QUIT))
rotation_stat += 1
if rotation_stat % 360 == 0:
rotation_stat = 0.0
screen.blit(sizedbg, (0,0))
screen.blit(planet01, planet_pos)
sun_rect = sun.get_rect()
sun_rotated = pg.transform.rotate(sun, rotation_stat)
center = sun_rotated.get_rect().center
sun_pos = (xres_cent-center[0], yres_cent-center[1])
screen.blit(sun_rotated, sun_pos)
pg.display.flip()
pg.quit()
if __name__ == "__main__":
main()
Related
For some reason, this program crashes after only a second or so of running this. Funnily enough, changing the turnAngle or fps affects how long it lasts before crashing. The error it throws is a
ValueError: subsurface rectangle outside of surface area on line 29 / line 9. Any ideas?
import pygame as pg
def rot_center(image, angle):
orig_rect = image.get_rect()
rot_image = pg.transform.rotate(image, angle)
rot_rect = orig_rect.copy()
rot_rect.center = rot_image.get_rect().center
rot_image = rot_image.subsurface(rot_rect).copy()
return rot_image
pg.init()
mainDisplay = pg.display.set_mode([600, 376])
imgSplashBG = pg.image.load('guiAssets/splash/BG.png')
imgSplashFG = pg.image.load('guiAssets/splash/FG.png')
imgSplashRedCircle = pg.image.load('guiAssets/splash/redCircle.png')
imgSplashGreenCircle = pg.image.load('guiAssets/splash/greenCircle.png')
turnAngle = 10
frame = 0
clock = pg.time.Clock()
crashed = False
while not crashed:
frame += 1
mainDisplay.blit(imgSplashBG, (0, 0))
mainDisplay.blit(rot_center(imgSplashGreenCircle, ((frame % 360) * 10)), (-189, -189))
mainDisplay.blit(rot_center(imgSplashRedCircle, ((frame % 360) * 10)), (453, 230))
mainDisplay.blit(imgSplashFG, (0, 0))
pg.display.flip()
clock.tick(30)
See How do I rotate an image around its center using PyGame?
Furthermore you have to handle the events in the application loop. See pygame.event.get() respectively pygame.event.pump():
For each frame of your game, you will need to make some sort of call to the event queue. This ensures your program can internally interact with the rest of the operating system.
For instance:
import pygame as pg
def rot_center(image, angle, center):
rotated_image = pg.transform.rotate(image, angle)
new_rect = rotated_image.get_rect(center = center)
return rotated_image, new_rect
pg.init()
mainDisplay = pg.display.set_mode([600, 376])
imgSplashBG = pg.image.load('guiAssets/splash/BG.png')
imgSplashFG = pg.image.load('guiAssets/splash/FG.png')
imgSplashRedCircle = pg.image.load('guiAssets/splash/redCircle.png')
imgSplashGreenCircle = pg.image.load('guiAssets/splash/greenCircle.png')
turnAngle = 10
frame = 0
clock = pg.time.Clock()
run = True
crashed = False
while not crashed and run:
for event in pg.event.get():
if event.type == pg.QUIT:
run = False
frame += 1
angle = (frame*turnAngle) % 360
centerGreen = (189, 189)
centerRed = (453, 230)
mainDisplay.blit(imgSplashBG, (0, 0))
mainDisplay.blit( *rot_center(imgSplashGreenCircle, angle, centerGreen) )
mainDisplay.blit( *rot_center(imgSplashRedCircle, angle,centerRed) )
mainDisplay.blit(imgSplashFG, (0, 0))
pg.display.flip()
clock.tick(30)
pygame.quit()
Can you try to add a time variable before your loop 'while not'
Like this :
.....
frame = 0
clock = pg.time.Clock()
time = pg.time.get_ticks() #here
crashed = False
while not .....
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.
couple of days ago, I made a code that plays a video in pygame window.
Code works just fine, just as I originally intended. However, when I print debug statement to see its fps, it's somewhere around 30fps. If I were to increase fps, what should I do?
Here is the code I used.
import sys
from color import *
import pyglet
pygame.init()
running = True
gameDisplay= pygame.display.set_mode((800,600))
window = pyglet.window.Window(visible=False)
background_vid = pyglet.media.Player()
background_vid.queue(pyglet.media.load(".\\music_folder\\music_vid/servant_of_evil_converted.mp4"))
background_vid.play()
def hellow():
print "hellow bloody world"
def on_draw():
#We have to convert the Pyglet media player's image to a Pygame surface
rawimage = background_vid.get_texture().get_image_data()
print "rawimage "+str(rawimage)
pixels = rawimage.get_data('RGBA', rawimage.width *8)
video = pygame.image.frombuffer(pixels, (rawimage.width*2,rawimage.height), 'RGBA')
#Blit the image to the screen
gameDisplay.blit(video, (0, 0))
circle_x=300
while True:
pyglet.clock.tick()
on_draw()
print "fps: "+str(pyglet.clock.get_fps())
for event in pygame.event.get():
if(event.type == pygame.QUIT):
sys.exit()
pygame.quit()
pygame.draw.rect(gameDisplay, red, (circle_x, 300, 300, 300), 5)
circle_x+=1
pygame.display.update()
So what #pydude said is not completely wrong.
However, in order to actually messure FPS I'd put a custom counter in the on_draw function, that will give better accuracy.
Further more, the only real problem with your code is that you don't insert vsync=False into your Window() decorator.
I've reworked your code to make it a little bit more modular, I've also removed potential bottle-necks and added my own custom FPS counter (via GL and not console), here - have a go and see if it works better for you.
(Note: Pressing Escape will exit the application)
import sys
from color import *
import pyglet
from pyglet.gl import *
from time import time # Used for FPS calc
key = pyglet.window.key
class main(pyglet.window.Window):
def __init__ (self):
super(main, self).__init__(800, 800, fullscreen = False, vsync = True)
self.running = True
self.background_vid = pyglet.media.Player()
self.background_vid.queue(pyglet.media.load(".\\music_folder\\music_vid/servant_of_evil_converted.mp4"))
self.background_vid.play()
self.fps_counter = 0
self.last_fps = time()
self.fps_text = pyglet.text.Label(str(self.fps_counter), font_size=12, x=10, y=10)
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.running = False
def on_draw(self):
self.render()
#We have to convert the Pyglet media player's image to a Pygame surface
def render(self):
self.clear()
rawimage = background_vid.get_texture().get_image_data()
pixels = rawimage.get_data('RGBA', rawimage.width *8)
video = pygame.image.frombuffer(pixels, (rawimage.width*2,rawimage.height), 'RGBA')
#Blit the image to the screen
self.blit(video, (0, 0))
# And flip the GL buffer
self.fps_counter += 1
if time() - self.last_fps > 1:
self.fps_text.text = str(self.fps_counter)
self.fps_counter = 0
self.last_fps = time()
self.fps_text.draw()
self.flip()
def run(self):
while self.running is True:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
if event and event.type == pygame.QUIT:
self.running = False
x = main()
x.run()
Try toggling vsync = True to vsync = False and watch the difference in the FPS counter.
With python, printing is very slow. Try just printing every once and a while.
Example:(requires import random):
if random.random()>0.09:print "fps: "+str(pyglet.clock.get_fps())
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()
I'm new to Pygame and I'm trying to move my sprite on my background image.
My sprite is not re appearing after it moves? Any ideas?
This is most of the program without some screens.
I have been trying to get this to work for many hours,
#dependencies
import pygame as P
import random as R
def welcome(screen):
#load background
bg = P.image.load("space-wallpaper.jpg")
screen.blit(bg,[0,0])
#set fonts etc
font = P.font.Font("Space_Age.ttf",60)
width, height = screen.get_size()
#play button
message = "PLAY "
text = font.render(message,1,[255 , 0, 0])
rect = text.get_rect()
x, y = text.get_size()
rect = rect.move((width - x)/2, (height - y)/2)
screen.blit(text,rect)
#high_score button
message = "HIGH SCORE "
text = font.render(message,1,[255 , 0, 0])
rect = text.get_rect()
x, y = text.get_size()
rect = rect.move((width - x)/2, (height - y)/2 +100)
screen.blit(text,rect)
def play_button(screen):
"""launch welcome screen.
"""
#welcome screen play button
font = P.font.Font("Space_Age.ttf",60)
message = "PLAY "
play_x,play_y = font.size(message)
play_text = font.render(message,1,[255 , 0, 0])
width, height = 800,600
screen.blit(play_text,[(width - play_x)/2, (height - play_y)/2])
play_rect = play_text.get_rect().move((width - play_x)/2, (height - play_y)/2)
P.display.flip()
return(play_rect)
def welcome_background(screen):
# Welcome screen background
bg = P.image.load("space-wallpaper.jpg")
screen.blit(bg,[0,0])
P.display.update()
def high_score_screen(screen):
"""opens the highscore screen"""
high_score_bg = P.image.load("bg_game.jpg")
screen.blit(high_score_bg,[0,0])
P.display.update()
def splash_screen(screen):
"""loads the first screen in the game with a 3 sec wait"""
splash_image = P.image.load('splash.jpg')
screen.blit(splash_image,[0,0])
P.display.update()
P.time.wait(2001)
def play_game(screen):
"""loads the play game screen"""
game_bg = P.image.load("bg_game.jpg")
screen.blit(game_bg,[0,0])
P.display.update()
def move_right(screen,x_cord,flagship):
dist = 20
play_game(screen)
x_cord = x_cord + dist
print(x_cord)
screen.blit(flagship,[x_cord])
P.display.update()
def key_detection(screen,flagship,x_cord):
key = P.key.get_pressed()
if key[P.K_RIGHT]:
move_right(screen,x_cord,flagship)
#move_right()
elif key[P.K_LEFT]:
print("left")
class Sprite():
def __init__(self,screen):
""" The constructor of the class """
self.flagship = P.image.load("sprite2.png")
self.x = 0
self.y = 0
def display(self,screen):
#screen.blit(self.sprite,[self.x,self.y]) changed by geoff
screen.blit(self.flagship,[self.x,self.y])
P.display.update()
_init_
# dependencies
from mods import *
import pygame as P
#initialise pygame
P.init()
def main():
# parameters to control pygame basics
screen_size = width, height = 800,600 #sixe of playing screen
P.display.set_caption('Space Smasher!')
screen = P.display.set_mode(screen_size)
clock = P.time.Clock() # timer used to control rate of looping
loop_rate = 20 #number of times per second does loop
play = True #control the playing of the actual game
splash_screen(screen)
welcome(screen)
P.display.flip()
rect_play = play_button(screen)
flagship = Sprite(screen)
while play:
key_detection(screen,flagship.image,flagship.x)
# for event in P.event.poll(): changed by geoff
event = P.event.poll() #did the player do something?
if event.type == P.QUIT:
play = False
if event.type == P.MOUSEBUTTONDOWN:
player_position = P.mouse.get_pos()
if rect_play.collidepoint(player_position):
play_game(screen)
flagship.display(screen)
P.quit()
if __name__ == '__main__':
main()
You are not calling either of your functions or your classes anywhere. You need a while loop that is similarly structured to this:
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
#Do your movements
With the following imports at the top:
import pygame, sys
from pygame.locals import *
Here is an example of moving an object with the keys:
import pygame, sys
from pygame.locals import *
pygame.init()
WIDTH=1439
HEIGHT=791
DISPLAYSURF = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Hello Pygame World!')
pygame.key.set_repeat(1, 10)
circx, circy = 200, 150
CIRWIDTH=20
while True: # main game loop
if pygame.key.get_pressed()[pygame.K_UP]:
circy-=5
if pygame.key.get_pressed()[pygame.K_DOWN]:
circy+=5
if pygame.key.get_pressed()[pygame.K_RIGHT]:
circx+=5
if pygame.key.get_pressed()[pygame.K_LEFT]:
circx-=5
try:
for event in pygame.event.get():
if event.type == QUIT or event.key == pygame.K_ESCAPE or event.key == pygame.K_q:
pygame.quit()
sys.exit()
except AttributeError:
pass
DISPLAYSURF.fill((0, 0, 0))
pygame.draw.circle(DISPLAYSURF, (158, 219, 222), (circx, circy), CIRWIDTH)
pygame.display.flip()
Main loop should be similar to:
while True:
# events - check keyboad and mouse and change player direction or speed
# move - move sprite with speed and direction
# check collison
# draw background, all objects and all sprites
# clock - to keep constant speed - FPS (Frames Per Seconds)
So you have to move sprites in every loop and draw them.