2

I am making a python game with pygame on my home computer for school running with python 3.6.3 and pygame 1.9.3. When working on my game I also worked at school where it was working correctly just slower as expected but when I started to load images it randomly choose the images and didn't load the correct ones on school computer not home one (I think this might be due to change in arrays and loading them). I soon found I was running python 3.5.3 at school and then when updating it the python and pygame, The game ran extremely slow and was unplayable. The images loaded correctly but the change in speed was extreme.

Summary:

Pygame at home working great pygame 1.9.3 python 3.6.3. Working fine at school until loading images python 3.5.3. Updating school corrected it but is now running unbearably slow.

I would love a fix for this problem either working for other python version or running at a variable speed for school computers. If there is any optimisation that could be easily implemented that would be very useful as well as I am not sure how to load images effectively

Code:

import pygame import random import math

def main():

    #load images?:
    bg = pygame.image.load("background6.png")

    wall = 100
    zombies = []
    currentnumber = 0
    gamepaused = False

    moveupx = False
    movedownx = False
    moveupy = False
    movedowny = False

    movex = 0
    movey = 0



    movespeed = 10
    time10 = False

    white = (255, 255, 255)
    black = (0, 0, 0)
    red = (255, 0, 0)
    blue = (0, 0, 255)
    fullscreen = pygame.FULLSCREEN



    pygame.init()
    pygame.font.init()
    myfont = pygame.font.SysFont('Comic Sans MS', 100)

    clock = pygame.time.Clock()
    clock.tick(30)

    #creating sprite groups
    heads = pygame.sprite.Group()
    bodys = pygame.sprite.Group()
    bullets = pygame.sprite.Group()
    allsprites = pygame.sprite.Group()
    players = pygame.sprite.Group()

    playerr = Player()
    allsprites.add(playerr)
    players.add(playerr)
    heads.add()
    bodys.add()



    gameDisplay = pygame.display.set_mode((1920, 1080), fullscreen)
    pygame.display.set_caption("Last Stand")

    gameExit = False

    pygame.display.update()
    #This will get the ticks from inisilising pygame in ms
    pretime = pygame.time.get_ticks()


    while not gameExit:
        time = pygame.time.get_ticks()-pretime
        #print (time/1000)
        timerounded = round(time/1000)
        timedisplay = round(time/1000,1)
        spawnbullet = False

        mouse = pygame.mouse.get_pos()

        # background things
        gameDisplay.fill(white)
        gameDisplay.blit(bg, (0, 0))

        if timerounded % 10 == 0:
            if timerounded != currentnumber:

                time10 = True
                currentnumber = timerounded

            else:
                time10 = False


        #start gameimput look e is event and it loops for each event in pygame
        #IT WILL GO THOUGH EACH EVEN!!!!!!! SO CHEACK FOR EACH EVENT
        for e in pygame.event.get():

            if e.type == pygame.MOUSEBUTTONDOWN:
                #so only if it will shoot foward NEED TO ADD FEEDBACK LATER
                if mouse[0] < 1500:
                    spawnbullet = True


            #so the problem is that if there is a key up for s then it would kill the key down for w as it would be set
            # to 0

            if e.type == pygame.KEYDOWN and e.key == pygame.K_w:
                moveupy = True
            if e.type == pygame.KEYDOWN and e.key == pygame.K_s:
                movedowny = True

            if e.type == pygame.KEYUP and e.key == pygame.K_w:
                moveupy = False
            if e.type == pygame.KEYUP and e.key == pygame.K_s:
                movedowny = False



            if e.type == pygame.KEYDOWN and e.key == pygame.K_a:
                moveupx = True
            if e.type == pygame.KEYDOWN and e.key == pygame.K_d:
                movedownx = True

            if e.type == pygame.KEYUP and e.key == pygame.K_a:
                moveupx = False
            if e.type == pygame.KEYUP and e.key == pygame.K_d:
                movedownx = False



            if e.type == pygame.QUIT:
                gameExit = True

            if e.type == pygame.KEYDOWN and e.key == pygame.K_ESCAPE:
                gameDisplay = pygame.display.set_mode((1920, 1080), fullscreen)
                gameExit = True
                # CAHNGE BACK LATER

        # END event loop

        #LOGIC FOR MOVING THE PLAYER:
        if moveupy == True:
            movey = -movespeed
        if movedowny == True:
            movey = movespeed
        if movedowny == False and moveupy == False:
            movey = 0

        if moveupx == True:
            movex = -movespeed
        if movedownx == True:
            movex = movespeed
        if movedownx == False and moveupx == False:
            movex = 0



        # Updating player have to do this at the start as need the angle and the top left cords
        for player in players:
            #REALLY NOT WOKRING THE MOVEX DOESNT WORK AND THE CHANGING HTE X AND WHY DOESNT WORK!!!

            player.move(movex, movey)
            angle, topleft = player.update(mouse)

        # needed another name for bullet so went for bullett
        if spawnbullet == True:
            bullett = Bullet(angle, topleft)
            #having the bullets only in the bullets class so it can render after the player
            bullets.add(bullett)

        #creating zombies
        if random.randint(0,2) == 1:
            ztype = "fast"
        else:
            ztype = "slow"

        #so at the start it spawns one for sure
        if time == 0:
            startlocation = [5, random.randint(320, 920)]

            zombies, heads, bodys = createz(zombies, ztype, startlocation, heads, bodys)


        if random.randint(0,10000) >9950-(timerounded**2): #So the time incraeed the odds expernetray
            startlocation = [5, random.randint(320, 920)]

            # WOULD LIKE THE ZOMBIES TO RUN FASTER AS THE GAME GOES ON!
            zombies, heads, bodys = createz(zombies, ztype, startlocation, heads, bodys)

        #updating Z

        zombies, totaldamnge  = updatez(zombies, heads, bodys)
        #updating bullets
        for bullet in bullets:
            bullet.update()

        #print(totaldamnge)
        wall -= totaldamnge
        #Cheacking for hitting bullets
        colstions(zombies, heads, bodys, bullets)

        if time10 == True:
            wall += 25
            if wall >100: wall = 100
        walltext = myfont.render(str(wall)+"%",False, (0, 0, 0))
        gameDisplay.blit(walltext,(1600,100))
        #print(wall)
        timedisplayer = myfont.render(str(timedisplay) + "Seconds", False, (0, 0, 0))
        gameDisplay.blit(timedisplayer, (10, 5))



        #drawing:
        #so cant use allsprites so might just wont have zomnbie head and zombie body in it

        #I CAN CHANGE WHEN WHAT IS DRAW AND WHAT IS ON WHAT
        bullets.draw(gameDisplay)
        allsprites.draw(gameDisplay)
        heads.draw(gameDisplay)
        bodys.draw(gameDisplay)


        pygame.display.update()

        clock.tick(30)

def colstions(zombies, heads, bodys, bullets):
    #so as having both hp and having the zombie having a head and body with 2 diffrent hitboxes
    headdamage = 140
    bodydamge = 60

    #so this part had sooooooo many proplems frist with like all the while loops next spend a long time trying to find what was wrong when it was just the head in bodys and vis versa


    if len(pygame.sprite.groupcollide(bullets, heads,False, False)) != 0 or len(pygame.sprite.groupcollide(bullets, bodys,False, False)) != 0:
        for bullet in bullets:
            hitthehead = False
            hitthebody = False
            zheadkilledzombie = False
            zbodykilledzombie = False
            counter1 = 0
            counter2 = 0
            for head in heads:
                #so it doesnt make the bullet hit more than one zombie head
                if hitthehead == False:
                    if pygame.sprite.collide_circle(bullet, head):
                        hp = zombies[counter1].hpordead(headdamage)
                        hitthehead = True
                        if hp <= 0:
                            zheadkilledzombie = True
                            head.kill()

                        print("hithead")
                counter1 += 1
            #so it doesnt check for body hits if it has hit a heah
            if hitthehead == False:
                for body in bodys:
                    if hitthebody == False:

                        #If it colldes and if the zombie is not already dead from the head shots
                        if pygame.sprite.collide_rect(bullet, body):
                            print("hitbody")
                            hp = zombies[counter2].hpordead(bodydamge)
                            hitthebody = True
                            if hp <= 0:

                                zbodykilledzombie = True
                                body.kill()
                    counter2 += 1

            if hitthehead == True or hitthebody == True:
                bullet.kill()
            #so killing the head if the body hit killed the zombie
            if zheadkilledzombie == True:
                zombiekilled = False
                counter3 = 0
                for body in bodys:
                    if zombiekilled == False:
                        hp = zombies[counter3].hpordead(0)
                        if hp <= 0:
                            body.kill()
                            zombies.pop(counter3)
                            zombiekilled = True
                        counter3 += 1

            #killing the body if killing is arealy dead
            if zbodykilledzombie == True:
                zombiekilled = False
                counter4 = 0
                for head in heads:
                    if zombiekilled == False:

                        hp = zombies[counter4].hpordead(0)
                        if hp <= 0:
                            head.kill()
                            zombies.pop(counter4)
                            zombiekilled = True
                        counter4 += 1




def createz(zombies, ztype, startlocation, heads, bodys, ):
    createdzombie = Zombie(startlocation, ztype)

    zombies.append(createdzombie)

    Hcords = (startlocation[0]+20, startlocation[1]-57)
    Bcords = (startlocation[0], startlocation[1])

    #need to have it also say what type of z it is.
    heads.add(head(Hcords,ztype))
    bodys.add(body(Bcords,ztype))


    print ("created")
    #dont think i need to return?
    return zombies, heads, bodys


def updatez(zombies, heads, bodys):
    totaldamnge = 0
    #somthing ent working
    Hcordsx = []
    Hcordsy = []
    Bcordsx = []
    Bcordsy = []
    ATWALLupdateanimationN = []



    for x in range(len(zombies)):


        cords, walldamge, updateanimationN = zombies[x].update()
        ATWALLupdateanimationN.append(updateanimationN)
        totaldamnge += walldamge

#putting all the cords into arrays just after reading them
        Hcordsx.append(cords[0]+ 20)
        Hcordsy.append(cords[1] - 57)

        Bcordsx.append(cords[0])
        Bcordsy.append(cords[1])

    counter = 0

    for body in bodys:

        Bcords = (Bcordsx[counter], Bcordsy[counter])
        body.update(Bcords, ATWALLupdateanimationN[counter])


        counter += 1

    counter = 0

    #I have to use the counter as cant do 1 per 1 on the head in heads thing so have to have a seperat counter
    for head in heads:
        Hcords = (Hcordsx[counter],Hcordsy[counter])
        head.update(Hcords,ATWALLupdateanimationN[counter])

        counter += 1





    return zombies, totaldamnge

class Bullet(pygame.sprite.Sprite):
    def __init__(self,angle, playerpos):
        pygame.sprite.Sprite.__init__(self)
        #Chanig the scale of the image so it doesnt stick out the gun too much
        self.image = pygame.transform.rotozoom(pygame.image.load("bulletpixel 6.png"), angle, 0.8)

        self.rect = self.image.get_rect()

        self.speed = 60
        self.angle = angle
        self.rect.x =  (self.rect.x + playerpos[0])
        self.rect.y = (self.rect.y + playerpos[1])

    def update(self):
        #moging it depending on its angle. so spliting up to horizontal and vertical bny taking thee cos and sin of the angle
        self.rect.x -= int(self.speed*math.cos(-self.angle*math.pi/180))

        self.rect.y -= int(self.speed*math.sin(-self.angle*math.pi/180))
        #could make this better for so when top of screen also deleate
        if self.rect.x <= -5:
            pygame.sprite.Sprite.kill(self)

        if self.rect.y <= -20 or self.rect.y >= 2000:
            pygame.sprite.Sprite.kill(self)
    def kill(self):
        pygame.sprite.Sprite.kill(self)


class Player(pygame.sprite.Sprite):

    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.angle = 0
        #for now sttg to to zombie as didnt save the player image
        self.image = pygame.image.load("player2.png")
        self.rect = self.image.get_rect()
        #need to create an image for player.
        #ok so got too choess so i could     if e.type == MOUSEMOTION:
        #and just have less than more than or like so i can 3 locations it is pointed or could have the player angle towards the mouse pointer and also the shotsneed to be angled anywhay so.

        self.rect.x = 1600
        self.rect.y = 600
        self.center = self.image.get_rect().center
        #setting the starting location
        self.movingamoutx = 1640
        self.movingamouty = 500
        self.posforbullet = (1640, 500)



    def  update(self,mouse):
        # SO THIS THIS IS MAD
        #Code for  making the player follow the mouse:
        #First sets the image to the stating image if this is not here the image gets bugged out from rotating many times
        self.image = pygame.image.load("player2.png")
        #Taking the x and y apart from the mouse
        mouse_x, mouse_y = mouse
        #calulationg the relative position of the mouse by subtracting the player postion
        #I subtracke the previs frame player pos to calulate the new anlge as that is the only way i can get the angle from the gun and not the player  and so it will go to the curcer
        #This does in fact make it be one frame behind for the angle but as it is 30 fps this will not make a differnce
        #subtracking 10 so it comes from a but up from the center and so is on the cetner of the curcer
        rel_x, rel_y = mouse_x - self.posforbullet[0], mouse_y - self.posforbullet[1]-10
        #calqulating the angle by using atan2
        #this takes 2 vectors and calclated the angle inbeween them NOT 100 ON THIS this is in rad so it needs to be degress so is changed to degress by timesing my 180/ pi
        # also Return atan(y / x), in radians. The result is between -pi and pi. The vector in the plane from the origin to point (x, y) makes this angle with the positive X axis.
        #The point of atan2() is that the signs of both inputs are known to it, so it can compute the correct quadrant for the angle. For example, atan(1) and atan2(1, 1) are both pi/4, but atan2(-1, -1) is -3*pi/4.
        self.angle = (180 / math.pi) * -math.atan2(rel_y, rel_x)
        #the angle is inverted so to make it be where it is pointed subtracted 180 degress
        self.angle -= 180
        #seting self.rotated to the image rotated to by the angle
        self.rotated = pygame.transform.rotate(self.image, self.angle)
        #getting the cords of the new rotated image
        rotRect = self.rotated.get_rect()
        #keeping the old center from before
        rotRect.center = self.rotated.get_rect().center
        #setting the new cords to image rotated cords, the adding is the amout that is getting moved by
        self.rect = (rotRect[0]+ self.movingamoutx), (rotRect[1]+ self.movingamouty)
        #setting the image to the new rotated one
        self.image = self.rotated

        #Creating a variable basited of the center of the rotated image so the bullet can shoot from the image then
        #adding the move amouts to put it into spot then subtracting to make it really shoot from the barrel and not the
        #true center

        #self.posforbullet = (rotRect.center[0] + self.movingamoutx-int(math.cos(self.changedangle)*20) ,rotRect.center[1]+ self.movingamouty-40)
        self.posforbullet = (rotRect.center[0] + self.movingamoutx-15 ,rotRect.center[1]+ self.movingamouty-45)
        return self.angle, self.posforbullet

    def move(self,movex, movey):
        self.movingamoutx += movex
        self.movingamouty += movey

        #cheaking to see if the player is trying to go to far away
        if self.movingamouty <= 320:
            self.movingamouty = 320
        elif self.movingamouty >= 840:
            self.movingamouty = 840

        if self.movingamoutx >= 1700:
            self.movingamoutx = 1700
        elif self.movingamoutx <= 1500:
            self.movingamoutx = 1500

class head(pygame.sprite.Sprite):
    def __init__(self,cords, type):

        pygame.sprite.Sprite.__init__(self)
        if type == "fast":
            self.type = "fast"
            self.image = pygame.transform.scale(pygame.image.load("fasthead.png"),[70,60])
        else:
            self.type = "slow"
            self.image = pygame.transform.scale(pygame.image.load("slowhead.png"),[70,60])


        self.rect = self.image.get_rect()

        self.rect.x = cords[0]
        self.rect.y = cords[1]
        self.ticks = 0
        self.putheadonbodyx = -11
        self.putheadonbodyy = -2
        self.firsttimewall = True


    def update(self,newcords,updateanimationNumber):
        self.ticks += 1


        if updateanimationNumber != 100:
            if self.firsttimewall == True:
                self.firsttimewall = False
                updateanimationNumber = 1

            if self.type == "slow":

                if updateanimationNumber == 2:
                    self.image = pygame.image.load("satwallhead1.png")
                elif updateanimationNumber == 1:
                    self.image = pygame.image.load("satwallhead2.png")

            if self.type == "fast":

                if updateanimationNumber == 2:
                    self.image = pygame.image.load("fatwallhead1.png")
                elif updateanimationNumber == 1:
                    self.image = pygame.image.load("fatwallhead2.png")


            self.rect = self.image.get_rect()
        self.rect.x = newcords[0] + self.putheadonbodyx
        self.rect.y = newcords[1] + self.putheadonbodyy


    def kill(self):
        pygame.sprite.Sprite.kill(self)


class body(pygame.sprite.Sprite):
    def __init__(self, cords, type):
        pygame.sprite.Sprite.__init__(self)
        if type == "fast":
            self.type = "fast"
            self.image = pygame.image.load("fastbody1.png")
            self.tickamout = 10
        else:
            self.type = "slow"
            self.image = pygame.image.load("slowbody1.png")
            self.tickamout = 15

        self.rect = self.image.get_rect()
        self.rect.x = cords[0]
        self.rect.y = cords[1]
        self.ticks = 0
        self.number = 0
        self.firsttimewall = True

    def update(self, newcords, Atwallnumber):
        self.ticks += 1
        if Atwallnumber == 100:


        #This is so it only trys to change image it if is time to changer
            if self.ticks % self.tickamout == 0:
                self.number += 1
                if self.number == 5:
                    self.number = 1
                if self.type == "slow":
                    if self.number == 1:
                        self.image = pygame.image.load("slowbody1.png")
                    elif self.number == 2:
                        self.image = pygame.image.load("slowbody2.png")
                    elif self.number == 3:
                        self.image = pygame.image.load("slowbody3.png")
                    elif self.number == 4:
                        self.image = pygame.image.load("slowbody4.png")

                elif self.type == "fast":
                    if self.number == 1:
                        self.image = pygame.image.load("fastbody1.png")

                    elif self.number == 2:
                        self.image = pygame.image.load("fastbody2.png")
                    elif self.number == 3:
                        self.image = pygame.image.load("fastbody3.png")
                    elif self.number == 4:
                        self.image = pygame.image.load("fastbody4.png")
                self.rect = self.image.get_rect()
        else:
            self.atwall(Atwallnumber)


        #Dont think that chaning the codes from the get rect will be too  much of a problem and also could save the
        #images beofre having ot load them
        self.rect.x = newcords[0]
        self.rect.y = newcords[1]



    def atwall(self,updateanimationNumber):
        #seeing if this is the first time

        if self.firsttimewall == True:
            self.firsttimewall = False
            updateanimationNumber = 1

        if self.type == "slow":

            if updateanimationNumber == 2:
                self.image = pygame.image.load("satwallbody1.png")

            elif updateanimationNumber == 1:
                self.image = pygame.image.load("satwallbody2.png")
        else:
            if updateanimationNumber == 2:
                self.image = pygame.image.load("fatwallbody1.png")

            elif updateanimationNumber == 1:
                self.image = pygame.image.load("fatwallbody2.png")
        self.rect = self.image.get_rect()


    def kill(self):
        pygame.sprite.Sprite.kill(self)


class Zombie():


    def  __init__(self, startlocation, ztype):
        if ztype == "fast":
            self.speed = 11
            self.hp = 100
        else:
            self.speed = 6
            self.hp = 200

        self.location = startlocation
        self.walldamage = 0
        self.ticks = 0
        #So it will stay 100 before it gets to the wall
        self.updateanimationnumber = 100




    def update(self):

        if self.location[0] < 1300: #the y direction
            self.walldamage = 0
            self.location[0] += self.speed #as this should be 30 frams a second it should move 3 pixies each time
        #so if at the wall
        elif self.location[0] >= 1300:
            self.walldamage = 0
            #cjamge the % number to change how fast the wall dies
            if self.ticks % 30 == 0:
                self.walldamage = 5
                self.updateanimationnumber = 1
            #so it will have to hitting animation when dealing damgage
            elif self.ticks % 15 == 0:
                self.updateanimationnumber = 2
            if self.ticks % 15 != 0:
                self.updateanimationnumber = 0
        self.ticks += 1



        return self.location, self.walldamage, self.updateanimationnumber



    def hpordead(self,subtrackhp):
        self.hp -= subtrackhp
        return self.hp


if __name__ == '__main__':
    main()
Marcus
  • 23
  • 3
  • you laod images too many times and it can slow down code. Load image in `__init__` and keep it as `self.original` and later assign to `self.image` this original image or rotate it and assign rotated version. – furas Dec 18 '17 at 21:36
  • Don't use so much code on Stack Overflow. We want [concise but runnable](https://stackoverflow.com/help/mcve) examples, so that we can copy and paste the code and test it properly. We can't say if the images are the only problem or if there's something else wrong with the code. – skrx Dec 19 '17 at 09:04
  • @skrx Sorry for the post of code, this is my first time on stack overflow. The code works completely apart from the images which are choosing form what has previously been loaded randomly and so i'm not sure how to post the code that could have stock images in then. Thanks – Marcus Dec 19 '17 at 12:16

2 Answers2

3

You should load the images in the global scope or another module and import them and then reuse them in your program. Do not load them in your functions or __init__ methods that get called in the while loop all the time or when you create an instance. Reading from the hard disk is slow.

Also, use the convert or convert_alpha methods of surfaces to improve the performance (converted surfaces get blitted faster), e.g.:

# All uppercase letters indicate that it's a constant and should never be changed.
MY_IMAGE = pygame.image.load('my_image.png').convert()  # Or `.convert_alpha()`

# Load the images once at program start. You can also do that in
# another module and import them or load all images automatically
# and put them into a dictionary.
PLAYER_IMAGE = pygame.image.load("player2.png").convert_alpha()
BULLET_IMAGE = pygame.image.load("bulletpixel 6.png").convert_alpha()

class Player(pygame.sprite.Sprite):

    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = PLAYER_IMAGE  # Either assign the global image directly ...

#-----------------------------------
    # or pass the image to __init__.
    def __init__(self, image):
        pygame.sprite.Sprite.__init__(self)
        self.image = image

player = Player(PLAYER_IMAGE)

In the Bullet class you can rotate the original image. transform.rototzoom creates a new surface/image and doesn't modify the original.

class Bullet(pygame.sprite.Sprite):
    def __init__(self, angle, playerpos):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.transform.rotozoom(BULLET_IMAGE, angle, 0.8)
skrx
  • 19,980
  • 5
  • 34
  • 48
  • Thanks but how would I pass in each image from the global scope without having to pass the many images every time though many functions. For the player I think I should be able to use in the __init__. Should I use convert for all my loads? And do you have any idea why it would be having random images when on the older python. Thanks – Marcus Dec 19 '17 at 12:06
  • Either reference the global images directly or pass them to the `__init__` methods and assign them to `self.image`. – skrx Dec 19 '17 at 12:15
  • *"And do you have any idea why it would be having random images when on the older python."* I'm not sure what you mean, please elaborate. – skrx Dec 19 '17 at 12:17
  • Sorry I'm confused on how I could reference the "global images". If passing could I use the 2D array system mentioned, or one dictionary with two lists, to pass in the create function then into the __init__. When going from the schools older version of python to the same one as at home, It fixed a problem where the zombies would have "randomly" chosen images to each zombie, eg each zombie would have another zombies random images. When updating this fixed the bug but caused the game to run much slower. Thanks – Marcus Dec 19 '17 at 12:29
  • I've added another example. And yes, you should usually `convert` or `convert_alpha` (for images with transparency) your images, otherwise the performance will really be a lot worse. – skrx Dec 19 '17 at 12:30
  • Ahh thanks a lot, now makes sense, will compressing the images before hand have a large improvement? – Marcus Dec 19 '17 at 12:41
  • If the sprite class has several images, for example for an animation, it's a good idea to put them into a list. Then do the same as for the single images and pass the list to the instance or assign it directly to an attribute. Use an index to get the current image out of the list and increment it to animate the sprite. – skrx Dec 19 '17 at 12:41
  • *"will compressing the images"* I just ran a little test and I don't see a difference between compressed and uncompressed image files. The difference between converted and unconverted images/pygame.Surfaces on the other hand is huge. – skrx Dec 19 '17 at 12:48
  • Thanks and will I use convert on the background image when I blit it. Is there anything that could help with drawing the groups (later I will be adding a dead zombie group that will not move thought the game). – Marcus Dec 19 '17 at 13:02
  • Hi sorry to post again but couldn't find a way to private message you. I have used convert and that has increased speed by over 500% and completely fixed my problem thanks. I am rewriting the body code and was not sure if it is bad to keep resetting the self.image or the current code is fine( changing the loading to global variables). Thanks – Marcus Dec 21 '17 at 18:22
  • *"increased speed by over 500%"* Nice! What exactly do you mean with resetting the `self.image`? If you just assign another preloaded image to `self.image` that's fine. – skrx Dec 21 '17 at 18:27
  • "resetting the self.image" I meant to say setting the self.image each frame even if the image number hasn't changed – Marcus Dec 21 '17 at 19:00
  • That should be okay. If you get performance problems again, you can [profile](https://stackoverflow.com/q/582336/6220679) your program to see which part of it has the biggest impact on the performance and then try to refactor it. – skrx Dec 21 '17 at 21:07
1

You load images too many times. Load them only once in __init__ and later assign from one variable to another.

For example in Player load image to self.original and later assign

 self.image = self.original

or

 self.image = pygame.transform.rotate(self.original, self.angle)

Similar with

            self.number += 1
            if self.number == 5:
                self.number = 1

            if self.type == "slow":
                if self.number == 1:
                    self.image = pygame.image.load("slowbody1.png")
                elif self.number == 2:
                    self.image = pygame.image.load("slowbody2.png")
                elif self.number == 3:
                    self.image = pygame.image.load("slowbody3.png")
                elif self.number == 4:
                    self.image = pygame.image.load("slowbody4.png")

            elif self.type == "fast":
                if self.number == 1:
                    self.image = pygame.image.load("fastbody1.png")

                elif self.number == 2:
                    self.image = pygame.image.load("fastbody2.png")
                elif self.number == 3:
                    self.image = pygame.image.load("fastbody3.png")
                elif self.number == 4:
                    self.image = pygame.image.load("fastbody4.png")

load all images in __init__ to two list

 self.slowimages = [
      pygame.image.load("slowbody1.png"),
      pygame.image.load("slowbody2.png"),
      pygame.image.load("slowbody3.png"),
      pygame.image.load("slowbody4.png"),
 ]

 self.fastimages = [
      pygame.image.load("fastbody1.png"),
      pygame.image.load("fastbody2.png"),
      pygame.image.load("fastbody3.png"),
      pygame.image.load("fastbody4.png"),
 ]

and later you can assign image without if/elif using one line

 if self.type == "slow":
     self.image = self.slowimages[self.number - 1]
 elif self.type == "fast":
     self.image = self.fastimages[self.number - 1]

or even load all in one dictionary with two lists

 self.all_images = {
      "slow": [
          pygame.image.load("slowbody1.png"),
          pygame.image.load("slowbody2.png"),
          pygame.image.load("slowbody3.png"),
          pygame.image.load("slowbody4.png"),
      ],
      "fast": [
          pygame.image.load("fastbodu1.png"),
          pygame.image.load("fastbodu2.png"),
          pygame.image.load("fastbodu3.png"),
          pygame.image.load("fastbodu4.png"),
      ],
 ]

and later you can assign image without if/elif using one line

 self.image = self.all_images[self.type][self.number - 1]

BTW in self.number you could keep values 0..3 instead of 1..4 and then in self.all_images[self.type][self.number - 1] you could use self.number instead of self.number - 1

And then instead of

     self.number += 1
     if self.number == 5:
         self.number = 1

you can use modulo and get one line

     self.number = (self.number + 1) % 4

Instead of

  self.rect.x = newcords[0]
  self.rect.y = newcords[1]

you can do in one line

  self.rect.topleft = newcords
furas
  • 134,197
  • 12
  • 106
  • 148
  • 1
    Thanks for all the help if I load the images in the __init__, as mentioned by @skrx, this will be called each time I create a zombie. – Marcus Dec 19 '17 at 12:21
  • yes, it is next step to speed up, You can keep in global variable or in class variable (not instance variable like in current example) - both will be loaded only once. You can also generate all rotated images at start and keep them on list and later get only from list `image = rotated[angle]` but for many images it will need more memory :) – furas Dec 19 '17 at 14:15
  • Or you can create class ResourceManager which will load image/sound/etc. when you will need it first time and later it will keep it in memory ("caching") to get it from memory next time, it can also generate rotated images when you ask for it first time and later it can keep it in memory so it doesn't have to rotate it again. – furas Dec 19 '17 at 14:21