2

I read the other questions about pygame bogging down and all I could find was limit the FPS, I tried that but it's still bogging down, I've tried everything but in the end I think it's just because my code is inefficient or something, any help would be appreciated.

Here's a video of the performance since i'm sure none of you want to actually download this,

https://www.youtube.com/watch?v=vmZaxb0zQR0

when it first starts it's way slower than it should be, at 0:12 in the video is when it's running at the speed it should be, and at 0:50 it bogs down again.

I tried looking everywhere to see what it could be and i found nothing, frankly just posting this source is embarassing

main.py

import sys, pygame
import random
import time
from resources import _time_
from pygame.locals import *

debug = True
start = time.time()
pygame.mixer.pre_init(44100, -16, 2, 2048)
pygame.init()

size = width, height = 800, 600
speed = [1, 1]
ai_speed = [1, 1]
black = (0, 0, 0)
text_color = (255, 255, 255)
dad_count = 0

#values change to True if appropriate key is pressed
keys = [False, False, False, False]

#set title and screen width and height
pygame.display.set_caption("Dad Explorer v0.02")
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
screen_rect = screen.get_rect()

#set boundary variables
b_height = height - 26
b_width = width - 25
newdad = False

#load our beautiful sounds
loop = pygame.mixer.music.load("sound/loop.wav")
intro = pygame.mixer.Sound("sound/intro.wav")
alarm1 = pygame.mixer.Sound("sound/klaxon_alert1.wav")

beeps = pygame.mixer.Sound("sound/beeps.wav")
beeps.set_volume(0.1)

defeated = pygame.mixer.Sound("sound/defeatednormal.wav")
defeated.set_volume(0.5)

yell = pygame.mixer.Sound("sound/yell.wav")
yell.set_volume(0.7)

#spawn objects at random coordinates
playerspawn = (random.randrange(b_width), random.randrange(b_height))
enemyspawn = (random.randrange(b_width), random.randrange(b_height))

#player
player = pygame.image.load("images/smug.gif")
player_rect = player.get_rect()
player_rect[:2] = playerspawn

#enemy
enemy = pygame.image.load("images/dad.png")
enemy_rect = enemy.get_rect()
enemy_rect[:2] = enemyspawn

#loop music
pygame.mixer.music.set_volume(0.2)
pygame.mixer.music.play(-1)

#pre set collision to false
collision = False

#fpsCounter = text_font.render("FPS: {}".format(pygame.time.Clock()))

while 1:
    respawn = (random.randrange(b_width), random.randrange(b_height))

    #display text on the screen
    text_font = pygame.font.SysFont("monospace", 14)
    player_label = text_font.render("-- Player --", 1, text_color)
    player_velocity = text_font.render("Velocity: {}".format(speed), 1, text_color)
    player_position = text_font.render("Position: {}".format((player_rect.x, player_rect.y)), 1, text_color)
    text_debug = text_font.render("Debug: {}".format(debug), 1, text_color)
    time_label = text_font.render("Time wasted: {}".format(_time_()), 1, text_color)
    dad_kills = text_font.render("{} Dads defeated".format(dad_count), 1, text_color)

    enemy_label = text_font.render("-- Enemy --", 1, text_color)
    enemy_velocity = text_font.render("Velocity: {}".format(ai_speed), 1, text_color)
    enemy_currentpos = text_font.render("Position: {}".format(enemy_rect[:2]), 1, text_color)

    #move the enemy
    enemy_rect = enemy_rect.move(ai_speed)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

        if event.type == pygame.KEYDOWN:
            if event.key==K_UP:
                keys[0]=True

            elif event.key==K_LEFT:
                keys[1]=True

            elif event.key==K_DOWN:
                keys[2]=True

            elif event.key==K_RIGHT:
                keys[3]=True

            if event.key == K_r:
                #press r to reset player position
                if debug == True:
                    player_rect[:2] = [100, 100]
                    speed = [1, 1]
                else:
                    pass
        if event.type == pygame.KEYUP:
            if event.key==pygame.K_UP:
                keys[0]=False

            elif event.key==pygame.K_LEFT:
                keys[1]=False

            elif event.key==pygame.K_DOWN:
                keys[2]=False

            elif event.key==pygame.K_RIGHT:
                keys[3]=False


    #set enemy boundaries
    if enemy_rect.left < 0 or enemy_rect.right > width:
        ai_speed[0] = -ai_speed[0]

    if enemy_rect.top < 0 or enemy_rect.bottom > height:
        ai_speed[1] = -ai_speed[1]

    #set player boundaries
    if not screen_rect.contains(player_rect):
        if player_rect.right > screen_rect.right:
            player_rect.right = screen_rect.right

        if player_rect.bottom > screen_rect.bottom:
            player_rect.bottom = screen_rect.bottom

        if player_rect.left < screen_rect.left:
            player_rect.left = screen_rect.left

        if player_rect.top < screen_rect.top:
            player_rect.top = screen_rect.top

    #check for collision
    new_collision = enemy_rect.colliderect(player_rect)

    if not collision and new_collision:
        print "alarm"
        dad_count += 1
        defeated.play()
        newdad = True
        if dad_count == 1:
            enemy = pygame.image.load("images/maddad.png")
            yell.play()

    elif collision and not new_collision:
        print "done colliding"
        beeps.play()

    collision = new_collision

    screen.fill(black) #display background
    screen.blit(player, player_rect) #display player object and player movement

    if newdad is True:
        enemy_rect[:2] = respawn
        ai_speed = [random.randrange(-5, 5), random.randrange(-5, 5)]
        screen.blit(enemy, enemy_rect)
        newdad = False
    else:
        screen.blit(enemy, enemy_rect)

    #player data
    screen.blit(player_label, (5, 10))
    screen.blit(player_velocity, (5, 30))
    screen.blit(player_position, (5, 50))
    screen.blit(text_debug, (5, 70))
    screen.blit(time_label, (5, b_height))
    screen.blit(dad_kills, (5, b_height - 30))

    #AI data
    screen.blit(enemy_label, (5, 120))
    screen.blit(enemy_velocity, (5, 140))
    screen.blit(enemy_currentpos, (5, 160))

    if keys[0]:
        speed[1] = -2
    elif keys[2]:
        speed[1] = 2        
    else:
        speed[1] = 0        
    if keys[1]:
        speed[0] = -2
    elif keys[3]:
        speed[0] = 2       
    else:
        speed[0] = 0

    player_rect.move_ip(speed[0], speed[1])

    pygame.display.flip()
    clock.tick(150)

resources.py (keeps track of time played)

import random
import time, datetime
from random import choice

start_datetime = datetime.datetime.now()

def _time_():    
    delta = datetime.datetime.now() - start_datetime
    days = delta.days
    hours = delta.seconds / 3600
    minutes = (delta.seconds / 60) % 60
    seconds = delta.seconds % 60

    runningtime = []

    if days > 0:
      runningtime.append("{} day{}".format(days, "" if days == 1 else "s"))

    if hours > 0:
        runningtime.append("{} hour{}".format(hours, "" if hours == 1 else "s"))

    if minutes > 0:
        runningtime.append("{} minute{}".format(minutes, "" if minutes == 1 else "s"))

    if seconds > 0:
        runningtime.append("{} second{}".format(seconds, "" if seconds == 1 else "s"))

    if len(runningtime) < 2:
        time = " and ".join(runningtime)
    else:
        time = ", ".join(runningtime[:-1]) + ", and " + runningtime[-1]
    return time
sloth
  • 99,095
  • 21
  • 171
  • 219
CrackSmoker9000
  • 319
  • 4
  • 13
  • In the future, if your code is running slow, you should break your code (main.py) up into functions (you should be doing this anyway, as your while True loop is more lines of code than it really should be) then [profile](http://docs.python.org/2/library/profile.html) your code. The profiling process will make the code run significantly slower. However, that'll tell you what function and/or module is taking up the most time either by a function taking a long time to run, or a function that is run many times, which is again helpful if you actually have functions in main.py. – Nuclearman Mar 11 '14 at 23:21
  • yea i'm gonna have too, right now main.py has 280 lines and its a terrible mess – CrackSmoker9000 Mar 14 '14 at 06:10
  • Quite the general rule (and to a fair degree absolute rule) is that a function/method should do one thing and one thing only. A secondary rule of thumb is that 10-20 lines of code per function. Based on that. A quick scan suggest that you've probably got 8-11 or so functions there. A few questions that might help, [here](http://stackoverflow.com/questions/611304/how-many-lines-of-code-should-a-function-procedure-method-have), [here](http://stackoverflow.com/questions/475675/when-is-a-function-too-long) and [here](http://stackoverflow.com/questions/20981/how-many-lines-of-code-is-too-many). – Nuclearman Mar 14 '14 at 08:26

1 Answers1

3

I think your problem is the font rendering. Font rendering is a very expensive operation, and you should always cache and re-use those rendered surfaces.

Also, you're loading the font every iteration of the main loop. Just load it once.

The game is quite simple, nonetheless I recommend a framerate of 60 (this is a good value based on my experience, YMMV).

An example of a cache could look like:

...
text_font = pygame.font.SysFont("monospace", 14)
cache={}
def get_msg(msg):
    if not msg in cache:
      cache[msg] = text_font.render(msg, 1 , text_color)
    return cache[msg]

while 1:
    respawn = (random.randrange(b_width), random.randrange(b_height))

    player_label = get_msg("-- Player --")
    player_velocity = get_msg("Velocity: {}".format(speed))
    player_position = get_msg("Position: {}".format((player_rect.x, player_rect.y)))
    text_debug = get_msg("Debug: {}".format(debug))
    time_label = get_msg("Time wasted: {}".format(_time_()))
    dad_kills = get_msg("{} Dads defeated".format(dad_count))
...

That should boost your framerate and prevent the slowdown.


As a side note, instead of:

#set player boundaries
if not screen_rect.contains(player_rect):
    if player_rect.right > screen_rect.right:
        player_rect.right = screen_rect.right

    if player_rect.bottom > screen_rect.bottom:
        player_rect.bottom = screen_rect.bottom

    if player_rect.left < screen_rect.left:
        player_rect.left = screen_rect.left

    if player_rect.top < screen_rect.top:
        player_rect.top = screen_rect.top

simply use:

#set player boundaries
player_rect.clamp_ip(screen_rect)

There are some other small issues with your code, but that's out of this question/answer.

sloth
  • 99,095
  • 21
  • 171
  • 219