Search code examples
pythonpygameclockcycle

Python - How to speed up smoothness in pygame?


I'm wondering how to speed up the smoothness of my code written in Python using pygam. I'm guessing I have to make this more efficient somehow? When this is run, some balls move around randomly in a set area, however, the new position of each ball is not smooth at all, there is a jump between each movement as the cycle is very slow. How do I fix this? Or is there any suggestions on how to improve it? This is my code so far:

import pygame
from pygame import *
import random
pygame.init()
size = width, height = 800, 600
screen = display.set_mode(size)
pygame.display.set_caption("Year 12: Ideal Gas Simulation")


BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
WHITE=(255,255,255)
GREEN = (0, 255, 0)
BALLX = 0
BALLY = 1
BALLSPEEDX = 2
BALLSPEEDY = 3
List=[]
radius=5

running=True
myClock=time.Clock()


myClock.tick(60)
def initBall():


        for n in range(40):
            ballx = random.randint(0, 800) # randomly setting the x position
            bally = random.randint(0, 600) # randomly setting the y position
            dirx = random.randint(-5,5)    # randomly setting the x speed
            diry = random.randint(-5,5)    # randomly setting the y speed

            data=[ballx, bally, dirx, diry]
            List.append(data)
            # returning a list with all the data the ball needs
        return List # returning the list


def drawScreen(List):
    draw.rect(screen, WHITE, (0, 0, 800, 600))
    for x in range(40):
        BALLX=List[x][0]
        BALLY=List[x][1]
        draw.circle(screen, GREEN, (BALLX,BALLY),radius)
        display.flip()
        pygame.draw.rect(screen, BLACK, (100-radius,100-radius,600+(2*radius),400+(2*radius)), 1)

        f=pygame.font.SysFont(None,60)
        text=f.render("PV=nRT",True,(0,0,0))
        screen.blit(text,(300,height/20))


def moveBall(List):
    for x in range(40):
        BALLX=List[x][0]
        BALLY=List[x][1]
        SPEEDX=List[x][2]#####data[BALLX]== the first index of each list [x][0]
        SPEEDY=List[x][3]##data[BALLSPEEDX]= List[x][2]
        age=SPEEDX+BALLX
        List[x][0]=age
         # increases the position of the ball
        plus=SPEEDY+BALLY
        List[x][1]=plus
    # checks to see if the ball is hitting the walls in the x direction
        if BALLX > 700:
            List[x][0] = 700#NORMALLY 800
            third=List[x][2]
            answer=third*-1
            List[x][2]=answer
        elif BALLX < 100:#NORMALLY 0
            List[x][0] = 100
            third=List[x][2]
            answer=third*-1
            List[x][2]=answer

    # checks to see if the ball is hitting the walls in the y direction
        if BALLY < 100:
            List[x][1] = 100#NORMALLY 0
            third=List[x][3]
            answer=third*-1
            List[x][3]=answer
        elif BALLY > 500:
            List[x][1] = 500#NORMALLY 600
            third=List[x][3]
            answer=third*-1
            List[x][3]=answer
    return List#return updated list


List=initBall()
while running==True:
    for evnt in event.get():
        if evnt.type==QUIT:
            running=False
            quit()
        if evnt.type==MOUSEBUTTONDOWN:
            mx,my=evnt.pos
            button=evnt.button

    drawScreen(List)
    List=moveBall(List)

Solution

  • In addition to skrx's answer, you can also refactor the code and avoid a lot of duplicate calls. Also, indexing the BALLS array directly might improve performance slightly.

    Generally, avoid naming variables inside functions with uppercase. These names are typically given to constants defined at the top of your file.

    The version I came up with is below:

    import array
    import pygame
    pygame.init()
    import random
    
    from pygame import *
    
    size = WIDTH, HEIGHT = 800, 600
    screen = display.set_mode(size)
    pygame.display.set_caption("Year 12: Ideal Gas Simulation")
    
    
    BLACK = (0, 0, 0)
    RED = (255, 0, 0)
    BLUE = (0, 0, 255)
    WHITE = (255,255,255)
    GREEN = (0, 255, 0)
    BALLX = 0
    BALLY = 1
    BALLSPEEDX = 2
    BALLSPEEDY = 3
    RADIUS = 5
    
    BALLS = []
    
    myClock = time.Clock()
    
    myClock.tick(60)
    def initBalls():
        for n in range(40):
            props = array.array('i', [
                random.randint(0, WIDTH),
                random.randint(0, HEIGHT),
                random.randint(-5, 5),
                random.randint(-5, 5),
            ])
            BALLS.append(props)
    
    
    def drawScreen():
        draw.rect(screen, WHITE, (0, 0, 800, 600))
        props = (100-RADIUS, 100-RADIUS, 600+(2*RADIUS), 400+(2*RADIUS))
        pygame.draw.rect(screen, BLACK, props, 1)
        f = pygame.font.SysFont(None, 60)
        text = f.render("PV=nRT", True,(0, 0, 0))
        screen.blit(text,(300, HEIGHT / 20))
        for i in range(len(BALLS)):
            draw.circle(screen, GREEN, BALLS[i][:2],RADIUS)
        display.flip()
    
    
    def moveBalls():
        for i in range(len(BALLS)):
    
            if BALLS[i][0] > 700:
                BALLS[i][0] = 700
                BALLS[i][2] *= -1
            elif BALLS[i][0] < 100:
                BALLS[i][0] = 100
                BALLS[i][2] *= -1
            else:
                BALLS[i][0] += BALLS[i][2]
    
            if BALLS[i][1] < 100:
                BALLS[i][1] = 100
                BALLS[i][3] *= -1
            elif BALLS[i][1] > 500:
                BALLS[i][1] = 500
                BALLS[i][3] *= -1
            else:
                BALLS[i][1] += BALLS[i][3]
    
    
    def main():
        initBalls()
        while True:
            for evnt in event.get():
                if evnt.type == QUIT:
                    pygame.quit()
                    return
                elif evnt.type == MOUSEBUTTONDOWN:
                    mx, my = evnt.pos
                    button = evnt.button
    
            drawScreen()
            moveBalls()
    
    
    if __name__ == "__main__":
        main()