简体   繁体   中英

Why does this pygame code lag?

I am trying to write this simple pygame pong game. I am not yet finished but my code has a lot of lag and i dont know why. The lag occurs when the ball bounces off the paddle, it seems to have an unintentional burst of speed. The best way to see this is to run the code :

import pygame, sys
import time
from pygame.locals import *

pygame.init()
fpsClock=pygame.time.Clock()
screen= pygame.display.set_mode((640,480))
pygame.display.set_caption('Test')
fontobj= pygame.font.Font('LCD_Solid.ttf',50)
mousex,mousey=0,0
x_1=15
x_2=600 #these varaibles (x_1, x_2) are different, but they are constants-- they will never change; think jon, the paddle will not move from left to right
y=0 #the y variable changes, but for this test it will be the same for both paddles bc they are moving in unisen.
x_ball=320
y_ball=240
direction=""
def draw_stuff (y):
        msg=str(x_ball)
        global x_ball,y_ball,direction
        textobj=fontobj.render(msg, False , pygame.Color('green'))
        screen.blit(textobj,(160,5))
        screen.blit(textobj,(480,5))
        pygame.draw.line(screen,pygame.Color('grey'),(320,0), (320,480), 4)
        pygame.draw.line(screen,pygame.Color('grey'),(0,3), (640,3), 10)
        pygame.draw.line(screen,pygame.Color('grey'),(0,475), (640,475), 10)
        pygame.draw.rect(screen, pygame.Color('grey'),(x_1,y,30,192))
        pygame.draw.rect(screen, pygame.Color('grey'),(x_2,y,30,192))
        if x_ball==60 or x_ball==570:
            print "we have reached the side",fpsClock.get_fps()
            if ball_hit(y,x_ball,y_ball):
                topl,middlel,bottoml=loc_of_ball_hitl(y,x_ball,y_ball)
                topr,middler,bottomr=loc_of_ball_hitr(y,x_ball,y_ball)
                if topl:
                    direction="upleft"
                elif middlel:
                    direction='midleft'
                elif bottoml:
                    direction='downleft'
                elif topr:
                    direction="upright"
                elif middler:
                    direction="midright"
                elif bottomr:
                    direction="downright"
                else:
                    direction=""
        if not direction:
            print "we have ",fpsClock.get_fps()
            x_ball+=2
        elif direction=="upleft":
            x_ball+=2
            y_ball-=2
        elif direction=="midleft":
            x_ball+=2
        elif direction=="downleft":
            x_ball+=2
            y_ball+=2
        elif direction=="upright":
            x_ball-=2
            y_ball-=2
        elif direction=="midright":
            x_ball-=2
        elif direction=="downright":
            x_ball-=2
            y_ball+=2
        ball(x_ball,y_ball)



def ball(x,y):
    pygame.draw.circle(screen, pygame.Color('red'), (x,y), 15, 0)
    pygame.display.update()
def ball_hit(y,ball_x,ball_y):
    if ball_x==60 and ball_y>=y and ball_y<y+192 or ball_x==570 and ball_y>=y and ball_y<y+192:
        return True
    return False
def loc_of_ball_hitl(y,ball_x,ball_y):
   middle=False
   top=False
   bottom=False
   if ball_x==60 and ball_y>=y+64 and ball_y<y+128:
        middle=True
   elif ball_x==60 and ball_y>=y and ball_y<y+64:
        top=True
   elif ball_x==60 and ball_y>=y+128 and ball_y<y+192:
        bottom=True
   return top, middle, bottom
def loc_of_ball_hitr(y,ball_x,ball_y):
   middle=False
   top=False
   bottom=False
   if ball_x==570 and ball_y>=y+64 and ball_y<y+128:
        middle=True
   elif ball_x==570 and ball_y>=y and ball_y<y+64:
        top=True
   elif ball_x==570 and ball_y>=y+128 and ball_y<y+192:
        bottom=True
   return top, middle, bottom
while True:
    screen.fill(pygame.Color('black'))
    if mousey>y:
        draw_stuff(y)
        y+=2
    if mousey<y:
        draw_stuff(y)
        y-=2
    if mousey==y:
        draw_stuff(y)
    for event in pygame.event.get():
        if event.type==QUIT:
            pygame.quit()
            sys.exit()
        elif event.type== MOUSEMOTION:
            mousex,mousey=event.pos
    pygame.display.update()
    fpsClock.tick(200)

Four main things wrong:

  • Your ball moves vertically and horizontally at a speed of 2 pixels ( x+=2 respectively y+=2 ). But if your ball moves vertically (let's x+=2 and y+=2 ), the actual speed of the ball is not 2, but sqrt(2**2 + 2**2) = 2.828 .

    So to do things like diagonal movement, you move to create a movement vector, normalize it, and then apply a speed to it (via multiplication).

  • Rendering a text via a Font is a very expensive operation. You should cache newly created surfaces so you don't have to render the same text again and again. This will improve performance greatly if you use a lot of text.

  • draw_stuff can get called multiple times per loop.

  • 200 FPS is way to high. Try something lower, like 60 FPS

Full code with these issues fixed (my changes are below the ### -comments):

import pygame, sys
import time
from pygame.locals import *

import math

### some simple vector helper functions, stolen from http://stackoverflow.com/a/4114962/142637
def magnitude(v):
    return math.sqrt(sum(v[i]*v[i] for i in range(len(v))))

def add(u, v):
    return [ u[i]+v[i] for i in range(len(u)) ]

def sub(u, v):
    return [ u[i]-v[i] for i in range(len(u)) ]    

def dot(u, v):
    return sum(u[i]*v[i] for i in range(len(u)))

def normalize(v):
    vmag = magnitude(v)
    return [ v[i]/vmag  for i in range(len(v)) ]

pygame.init()
fpsClock=pygame.time.Clock()
screen= pygame.display.set_mode((640,480))
pygame.display.set_caption('Test')
fontobj= pygame.font.SysFont('Arial',50)
mousex,mousey=0,0
x_1=15
x_2=600 #these varaibles (x_1, x_2) are different, but they are constants-- they will never change; think jon, the paddle will not move from left to right
y=0 #the y variable changes, but for this test it will be the same for both paddles bc they are moving in unisen.
x_ball=320
y_ball=240
direction=""

### the speed of the ball and the paddles
speed = 5

### a cache for font objects
cache={}
def get_msg(msg):
    if not msg in cache:
      cache[msg] = fontobj.render(msg, False , pygame.Color('green'))
    return cache[msg]

def draw_stuff (y):
        msg=str(x_ball)
        global x_ball,y_ball,direction

        ### get the font surface from the cache
        textobj=get_msg(msg)
        screen.blit(textobj,(160,5))
        screen.blit(textobj,(480,5))
        pygame.draw.line(screen,pygame.Color('grey'),(320,0), (320,480), 4)
        pygame.draw.line(screen,pygame.Color('grey'),(0,3), (640,3), 10)
        pygame.draw.line(screen,pygame.Color('grey'),(0,475), (640,475), 10)
        pygame.draw.rect(screen, pygame.Color('grey'),(x_1,y,30,192))
        pygame.draw.rect(screen, pygame.Color('grey'),(x_2,y,30,192))
        if x_ball==60 or x_ball==570:
            print "we have reached the side",fpsClock.get_fps()
            if ball_hit(y,x_ball,y_ball):
                topl,middlel,bottoml=loc_of_ball_hitl(y,x_ball,y_ball)
                topr,middler,bottomr=loc_of_ball_hitr(y,x_ball,y_ball)
                if topl:
                    direction="upleft"
                elif middlel:
                    direction='midleft'
                elif bottoml:
                    direction='downleft'
                elif topr:
                    direction="upright"
                elif middler:
                    direction="midright"
                elif bottomr:
                    direction="downright"
                else:
                    direction=""

        ### create a vector
        move = (0, 0)
        if not direction:
            print "we have ",fpsClock.get_fps()
            move = (1, 0)
        elif direction=="upleft":
            move = (1, -1)
        elif direction=="midleft":
            move = (1, 0)
        elif direction=="downleft":
            move = (1, 1)
        elif direction=="upright":
            move = (-1, -1)
        elif direction=="midright":
            move = (-1, 0)
        elif direction=="downright":
            move = (-1, 1)
        ### normalize it and apply the speed
        move = [int(c * speed) for c in normalize(move)]
        ### update ball position with movement vector
        x_ball, y_ball = x_ball + move[0], y_ball + move[1]
        ball(x_ball, y_ball)

def ball(x,y):
    pygame.draw.circle(screen, pygame.Color('red'), (x,y), 15, 0)
    pygame.display.update()
def ball_hit(y,ball_x,ball_y):
    if ball_x==60 and ball_y>=y and ball_y<y+192 or ball_x==570 and ball_y>=y and ball_y<y+192:
        return True
    return False
def loc_of_ball_hitl(y,ball_x,ball_y):
   middle=False
   top=False
   bottom=False
   if ball_x==60 and ball_y>=y+64 and ball_y<y+128:
        middle=True
   elif ball_x==60 and ball_y>=y and ball_y<y+64:
        top=True
   elif ball_x==60 and ball_y>=y+128 and ball_y<y+192:
        bottom=True
   return top, middle, bottom
def loc_of_ball_hitr(y,ball_x,ball_y):
   middle=False
   top=False
   bottom=False
   if ball_x==570 and ball_y>=y+64 and ball_y<y+128:
        middle=True
   elif ball_x==570 and ball_y>=y and ball_y<y+64:
        top=True
   elif ball_x==570 and ball_y>=y+128 and ball_y<y+192:
        bottom=True
   return top, middle, bottom
while True:
    screen.fill(pygame.Color('black'))
    if mousey>y:
        y+=speed
    if mousey<y:
        y-=speed

    ### call draw_stuff only once
    draw_stuff(y)
    for event in pygame.event.get():
        if event.type==QUIT:
            pygame.quit()
            sys.exit()
        elif event.type== MOUSEMOTION:
            mousex,mousey=event.pos
    pygame.display.update()
    ### realistic framerate
    fpsClock.tick(60)

Note that there are some other improvements possible, but this should fix your issues.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM