Posts: 11
Threads: 4
Joined: Aug 2018
I am just starting to learn to program, but I found that if I draw an image on my screen, pygame only runs at 7 fps. I am told that this is normal, and I should optimize, which is what I shall do. Yet it occurs to me, other games update the entire screen every tenth of a second. The entire screen, as the view has to change as the character moves around. How is this done without requiring a supercomputer? Or is python just rather limited in what it can do? I genuinely do not know, any input would be appreciated.
Posts: 591
Threads: 26
Joined: Sep 2016
Quote: I found that if I draw an image on my screen, pygame only runs at 7 fps. I am told that this is normal, and I should optimize, which is what I shall do.
This is absolutely not normal. Pygame and Python are not fast enough for some games, certainly, but you should have no issues maintaining 60 fps until you reach much higher levels of complexity. If possible please share a minimal example that exhibits this behavior (with any needed resources, images, etc) so that we can reproduce it and better assist you.
Posts: 11
Threads: 4
Joined: Aug 2018
ok, so this is the code that is running like a 100 year old stroke victim
import pygame
from pygame.locals import *
import os
import Keyhandler
import Menues
x, y = 0, 0
position = (0, 30)
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (x,y)
os.environ['SDL_VIDEO_WINDOW_POS'] = str(position[0]) + "," + str(position[1])
infoObject = pygame.display.Info()
screenxsize, screenysize = infoObject.current_w, (infoObject.current_h - 30)
running, time, leftmousepress, mouseclicked, mouselefthold, keypressed, screenresizedyet, keycombopressed, helptext, pressed, clickedon, menuenum = True, 0, False, False, False, False, False, False, " ", False, False, "Start"
mpos, mouseheld, key, time = (0,0), False, [], 0
font = pygame.font.SysFont('Comic Sans MS', 10)
pygame.init()
pygame.font.init()
screen = pygame.display.set_mode([(int(screenxsize)), (int(screenysize))], RESIZABLE)
pygame.display.set_caption('Dnd reimagined')
while running:
time = time + 1
running, key, keypressed, keysheld, keycombo, mouseclicked, mouseunclicked, mouseheld, mpos, screenxsize, screenysize = Keyhandler.keyhandler(screenxsize, screenysize, mouseclicked)
menuenum = Menues.drawmenues(screen, screenxsize, screenysize, mpos, mouseclicked, mouseunclicked, mouseheld, menuenum, time)
Menues.drawmenues(screen, screenxsize, screenysize, mpos, mouseclicked, mouseunclicked, mouseheld, menuenum, time)
if len(key) > 0:
print(key)
if mouseclicked == True:
print("CLICK")
print("loop")
pygame.display.update() import pygame
from pygame.locals import *
import time
mpos, mouseheld = False, False
menuenum = "Start"
pygame.init()
pygame.font.init()
font = pygame.font.SysFont('Comic Sans MS', 10)
def drawbutton(screen, mpos, mouseheld, mouseclicked, clickedon, posx, posy, buttonwidth, buttonheight, text):
buttontext = font.render(text, False, (255, 255, 255))
if clickedon == True:
pygame.draw.rect(screen, (100, 10, 100), (posx, posy, buttonwidth, buttonheight))
pygame.draw.rect(screen, (100, (100), 100), (posx + (buttonwidth * .1), posy + (buttonheight * .1), (buttonwidth * .8), (buttonheight * .8)))
screen.blit(buttontext, (posx, posy))
else:
pygame.draw.rect(screen, (100, 100, 100), (posx, posy, buttonwidth, buttonheight))
pygame.draw.rect(screen, (100, 10, 100), (posx + (buttonwidth * .1), posy + (buttonheight * .1), (buttonwidth * .8), (buttonheight * .8)))
screen.blit(buttontext, (posx, posy))
def drawanddetect(screen, mpos, mouseclicked, mouseunclicked, mouseheld, posx, posy, buttonwidth, buttonheight, text):
clickedon = False
if ((posx < mpos[0] < (posx + buttonwidth)) & (posy < mpos[1] < (posy + buttonheight))):
if mouseclicked == True:
clickedon = True
else:
clickedon = False
else: held = False
drawbutton(screen, mpos, mouseheld, mouseclicked, clickedon, posx, posy, buttonwidth, buttonheight, text)
return clickedon
def startmenue(screen, screenxsize, screenysize, mpos, mouseclicked, mouseunclicked, mouseheld, time):
Startscreen = pygame.image.load('Dnd.png')
screen.blit(pygame.transform.scale(Startscreen, (screenxsize, screenysize)), (0, 0))
print("blit")
menuenum = "Start"
buttonwidth = screenxsize // 20
buttonheight = screenysize // 20
posx = (screenxsize // 2) - buttonwidth
posy = (screenysize // 2) - buttonheight
text = "Start"
drawanddetect(screen, mpos, mouseclicked, mouseunclicked, mouseheld, posx, posy, buttonwidth, buttonheight, text)
clickedon = drawanddetect(screen, mpos, mouseclicked, mouseunclicked, mouseheld, posx, posy, buttonwidth, buttonheight, text)
#if clickedon == True:
# menuenum = "Charactorcreatior"
buttonwidth = screenxsize // 20
buttonheight = screenysize // 20
posx = (screenxsize) - buttonwidth
posy = (screenysize) - buttonheight
text = "Help"
drawanddetect(screen, mpos, mouseclicked, mouseunclicked, mouseheld, posx, posy, buttonwidth, buttonheight, text)
clickedon = drawanddetect(screen, mpos, mouseclicked, mouseunclicked, mouseheld, posx, posy, buttonwidth, buttonheight, text)
#if clickedon == True:
#menuenum = "help"
return menuenum
def helpmenue(screen, screenxsize, screenysize, mpos, mouseclicked, mouseunclicked, mouseheld, time):
time = time + 1
print(time)
menuenum = "help"
buttonwidth = screenxsize // 20
buttonheight = screenysize // 20
posx = time
posy = time
text = "Back"
drawanddetect(screen, mpos, mouseclicked, mouseheld, mouseunclicked, posx, posy, buttonwidth, buttonheight, text)
clickedon = drawanddetect(screen, mpos, mouseclicked, mouseunclicked, mouseheld, posx, posy, buttonwidth, buttonheight, text)
if clickedon == True:
menuenum = "Start"
return menuenum, time
def drawmenues(screen, screenxsize, screenysize, mpos, mouseclicked, mouseunclicked, mouseheld, menuenum, time):
menuenum = "help"
if menuenum == "help":
print("fruiof")
startmenue(screen, screenxsize, screenysize, mpos, mouseclicked, mouseunclicked, mouseheld, time)
menuenum = startmenue(screen, screenxsize, screenysize, mpos, mouseclicked, mouseunclicked, mouseheld, time)
if menuenum == "Start":
menuenum, time = helpmenue(screen, screenxsize, screenysize, mpos, mouseclicked, mouseunclicked, mouseheld, time)
helpmenue(screen, screenxsize, screenysize, mpos, mouseclicked, mouseclicked, mouseheld, time)
return menuenum import pygame
from pygame.locals import *
import os
pygame.init()
def keyhandler(screenxsize, screenysize, mouseclicked):
keycombo = False
running = True
keypressed = False
key = []
keysheld = pygame.key.get_pressed()
mousestate = pygame.mouse.get_pressed()
mouseheld = mousestate[0]
opressed = False
mpos = pygame.mouse.get_pos()
bob = 0
mouseclicked = []
mouseunclicked = []
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
print("Quitting")
if event.type == pygame.KEYDOWN:
keypressed = True
if keysheld[K_LSHIFT]:
caps = True
else:
caps = False
print("keypressed")
if event.key == pygame.K_a:
if caps == True:
key.append("A")
else:
key.append("a")
if event.key == pygame.K_b:
if caps == True:
key.append("B")
else:
key.append("b")
if event.key == pygame.K_c:
if caps == True:
key.append("C")
else:
key.append("c")
if event.key == pygame.K_d:
if caps == True:
key.append("D")
else:
key.append("d")
if event.key == pygame.K_e:
if caps == True:
key.append("E")
else:
key.append("e")
if event.key == pygame.K_f:
if caps == True:
key.append("F")
else:
key.append("f")
if event.key == pygame.K_g:
if caps == True:
key.append("G")
else:
key.append("g")
if event.key == pygame.K_h:
if caps == True:
key.append("H")
else:
key.append("h")
if event.key == pygame.K_i:
if caps == True:
key.append("I")
else:
key.append("i")
if event.key == pygame.K_j:
if caps == True:
key.append("J")
else:
key.append("j")
if event.key == pygame.K_k:
if caps == True:
key.append("K")
else:
key.append("k")
if event.key == pygame.K_l:
if caps == True:
key.append("L")
else:
key.append("l")
if event.key == pygame.K_m:
if caps == True:
key.append("M")
else:
key.append("m")
if event.key == pygame.K_n:
if caps == True:
key.append("N")
else:
key.append("n")
if event.key == pygame.K_o:
if caps == True:
key.append("O")
else:
key.append("o")
opressed = True
if keysheld[K_p]:
keycombo = "op"
else:
keycombo = []
if event.key == pygame.K_p:
if caps == True:
key.append("P")
else:
key.append("p")
if opressed == True:
keycombo = "op"
if keysheld[K_o]:
keycombo = "op"
else:
keycombo = []
if event.key == pygame.K_q:
if caps == True:
key.append("Q")
else:
key.append("q")
if event.key == pygame.K_r:
if caps == True:
key.append("R")
else:
key.append("r")
if event.key == pygame.K_s:
if caps == True:
key.append("S")
else:
key.append("s")
if event.key == pygame.K_t:
if caps == True:
key.append("T")
else:
key.append("t")
if event.key == pygame.K_u:
if caps == True:
key.append("U")
else:
key.append("u")
if event.key == pygame.K_v:
if caps == True:
key.append("V")
else:
key.append("v")
if event.key == pygame.K_w:
if caps == True:
key.append("W")
else:
key.append("w")
if event.key == pygame.K_x:
if caps == True:
key.append("X")
else:
key.append("x")
if event.key == pygame.K_y:
if caps == True:
key.append("Y")
else:
key.append("y")
if event.key == pygame.K_z:
if caps == True:
key.append("Z")
else:
key.append("z")
if event.key == pygame.K_LSHIFT:
key.append("LShift")
if event.type == pygame.KEYUP:
keypressed = False
if event.type == pygame.MOUSEBUTTONDOWN:
mouseclicked.append(True)
else:
mouseclicked.append(False)
if event.type == pygame.MOUSEBUTTONUP:
mouseunclicked.append(True)
else:
mouseunclicked.append(False)
if event.type == pygame.VIDEORESIZE:
print("Resizing")
screenxsize = event.w
screenysize = event.h
if True in mouseclicked:
mouseclicked = True
else:
mouseclicked = False
if True in mouseunclicked:
mouseunclicked = True
else:
mouseunclicked = False
key = ''.join(key)
return running, key, keypressed, keysheld, keycombo, mouseclicked, mouseunclicked, mouseheld, mpos, screenxsize, screenysize and this is the code i tried to replicate the problem with. i get that they are obviously different programs, but they both draw the screen once per tick. I do not know why the first if running so badly. As you said, it would have to be really complex to normally have this problem.
import pygame
from pygame.locals import *
import os
x, y = 0, 0
position = (0, 30)
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (x,y)
os.environ['SDL_VIDEO_WINDOW_POS'] = str(position[0]) + "," + str(position[1])
pygame.init()
infoObject = pygame.display.Info()
screenxsize, screenysize = infoObject.current_w, (infoObject.current_h - 30)
screen = pygame.display.set_mode([(int(screenxsize)), (int(screenysize))], RESIZABLE)
Startscreen = pygame.image.load('Dnd.png')
i = 0
while True:
i = i + 1
screen.blit(pygame.transform.scale(Startscreen, (screenxsize, screenysize)), (0, 0))
pygame.draw.rect(screen, (100,100,100), (i, i, 50, 50))
print("looped")
pygame.display.update() also this is probably in the wrong place now. I do not know how to fix that. Any help would be appreciated.
Posts: 591
Threads: 26
Joined: Sep 2016
Okay, I will try to take a closer look tomorrow but a few things jump out immediately.
Convert all images when you load them.
Startscreen = pygame.image.load('Dnd.png').convert() or convert_alpha() if it has transparency.
Add a clock to your program.
clock = pg.time.Clock() And at the end of your game loop once per frame call:
clock.tick(60) Your primary slowdown is probably reloading resources.
It seems your menu function runs once per frame and reloads the image every time it runs.
Load and convert images and fonts once; not every iteration.
Also, we talked about your event handler in the other thread but another slowdown will be using all if s in that function rather than elif s.
An event can only be one thing.
if event.type == pg.KEYDOWN:
#do something
elif event.type == pg.MOUSEBUTTONDOWN: # Note elif not if as an event can't be both a KEYDOWN and a MOUSEBUTTONDOWN.
#do something else Currently your handler checks every single conditional for every single event rather than stopping once it finds the right one.
Start with some of that stuff and see how it goes.
Here is a repo of some examples I have written for folks like you.
https://github.com/Mekire/pygame-samples
Download the repo and see how that stuff runs for you.
Posts: 544
Threads: 15
Joined: Oct 2016
Aug-23-2018, 01:20 PM
(This post was last modified: Aug-23-2018, 03:27 PM by Windspar.)
Example how you can improve key handler.
Reducing the ifs and adding elif will takes less time to process.
def keyhandler(screenxsize, screenysize):
key = []
running = True
mouseclick = False
mouseunclick = False
for event in pygame.event.get()
if event.type == pygame.QUIT:
running = False
elif event.type == pgyame.KEYDOWN:
# handle space, numbers, a-z, A-Z, .!@#$%^&*() etc
if 32 <= event.key < 123:
# handles cap
key.append(event.unicode)
elif event.type == pygame.MOUSEBUTTONDOWN:
# event.button , 1 = left click, 2 = middle click, 3 = right click
mouseclick = True
elif event.type == pygame.MOUDEBUTTONUP:
mouseunclick = True
elif event.type == pygame.VIDEORESIZE:
print("Resizing")
screenxsize = event.w
screenysize = event.h
key_states = pygame.key.get_pressed()
# combos
keycombos = []
if key_states[pygame.K_o] and key_states[pygame.K_p]:
keycombos.append("op")
return (running,
''.join(key),
len(key) > 0, # if a key has been pressed
key_states,
keycombo,
mouseclicked,
mouseunclicked,
pygame.mouse.get_pressed()[0],
pygame.mouse.get_pos(),
screenxsize,
screenysize) If at all possible. You don't want to use pygame.transform.scale every cycle.
This will slow your program down.
99 percent of computer problems exists between chair and keyboard.
Posts: 544
Threads: 15
Joined: Oct 2016
Aug-23-2018, 03:26 PM
(This post was last modified: Aug-23-2018, 03:28 PM by Windspar.)
If you just want letters. You could do.
if 97 <= event.key < 123 or 65 <= event.key < 90:
key.append(event.unicode)
Small error.
if 32 >= event.key > 123: I should have wrote
if 32 <= event.key < 123:
99 percent of computer problems exists between chair and keyboard.
Posts: 591
Threads: 26
Joined: Sep 2016
(Aug-23-2018, 03:26 PM)Windspar Wrote: If you just want letters. I handle this by creating a list (or set) of acceptable characters. This is more readable and more maintainable should the set change:
import string
ACCEPTED = string.ascii_letters + string.digits + string.punctuation + " " And then in your event handler:
if event.unicode in ACCEPTED:
self.buffer.append(event.unicode)
Posts: 544
Threads: 15
Joined: Oct 2016
Aug-29-2018, 11:11 PM
(This post was last modified: Aug-29-2018, 11:16 PM by Windspar.)
@ Mekire
I tested it. It eats home keys and arrows keys.
Do you do something different ?
from types import SimpleNamespace
import string
import pygame
ACCEPTED = string.ascii_letters + string.digits + string.punctuation + " "
def main():
pygame.init()
pygame.display.set_caption('Testing string import')
rect = pygame.Rect(0, 0, 400, 300)
surface = pygame.display.set_mode(rect.size)
clock = pygame.time.Clock()
running = True
font = pygame.font.Font(None, 24)
text = SimpleNamespace(**{'image':None,
'repr_image': None,
'key_image': None,
'buffer':[],
'key_buffer':[]})
while running:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.unicode in ACCEPTED:
text.buffer.append(event.unicode)
text.key_buffer.append(event.key)
elif event.key == pygame.K_BACKSPACE:
if len(text.buffer) > 1:
text.buffer = text.buffer[:-1]
text.key_buffer = text.key_buffer[:-1]
else:
text.buffer = []
text.key_buffer = []
elif event.key == pygame.K_LEFT:
print('Key Left')
text.image = font.render(''.join(text.buffer), 1, (0,200,0))
text.repr_image = font.render(str(text.buffer), 1, (200, 100, 0))
text.key_image = font.render(str(text.key_buffer), 1, (0, 100, 200))
elif event.type == pygame.QUIT:
running = False
surface.fill((0,0,0))
if text.image:
surface.blit(text.image, (10, 50))
surface.blit(text.repr_image, (10, 100))
surface.blit(text.key_image, (10, 150))
pygame.display.update()
clock.tick(30)
pygame.quit()
main()
I have to add event.unicode != '' to work.
if event.unicode in ACCEPTED and event.unicode != '':
99 percent of computer problems exists between chair and keyboard.
|