帧速率低,可能是因为未加速的图形

时间:2017-07-20 00:34:37

标签: python pygame pyglet

因此我几个月前开始使用pygame创建一个游戏,它有一个类似于星际争霸,帝国时代等的顶视图......我已经编写了大约1320行代码来创建我的游戏基础;然而,我在渲染图像时遇到帧速率问题,并且相信这是因为我无法使用pygame加速图形。我当前blitting图像的方式是提前在一个表面上将所有图像blitting,然后我在表面下创建一个我整个屏幕的blit图像。我应该使用更有效的方法吗?

所以我的假设是看起来很乱,而且我不想浪费你的家伙的时间。基本上任何时候我对我的屏幕大小的表面进行blit我的帧率下降了大约20帧,有没有办法在pygame中避免这种情况?

    ##PYGAME INITATE##
import pygame, os
os.environ['SDL_VIDEO_CENTERED'] = '1'
pygame.init()

_W,_H = pygame.display.Info().current_w, pygame.display.Info().current_h
flags = pygame.DOUBLEBUF | pygame.HWSURFACE
gameDisplay = pygame.display.set_mode((_W,_H),pygame.FULLSCREEN ) ## CREATES SCREEN YOU DISPLAY ON ##
gameDisplay.fill((0,0,0)) ## FILL COLOR OF SCREEN ##
pygame.display.set_caption("Dope Game")  ## SETS NAME ##
gameClock = pygame.time.Clock() ## CLOCK OF THE GAME ##
import math
import os
import random
import copy


SQRT = math.sqrt
PI = math.pi
cos = math.cos
sin = math.sin


## REPEATABLE FUNCTIONS ##

def loadScale(file,command,sizeX,sizeY):
    temp = pygame.image.load(file)
    tempInfo = temp.get_rect()
    tempInfo1,tempInfo2,tempInfo3,tempInfo4 = temp.get_rect()
    tempInfo3 = int(tempInfo3)
    tempInfo4 = int(tempInfo4)
    if (command == "ratio"):    
        tempInfo3 = tempInfo3*sizeX
        tempInfo4 = tempInfo4*sizeY
        temp = pygame.transform.scale(temp,(int(tempInfo3),int(tempInfo4) ) )

    elif (command == "size"):
        temp = pygame.transform.scale(temp, (sizeX,sizeY) )         

    return(temp)

## NON GAME RELATED CLASSES ##

class EnterFrame():
    def __init__(self,frameReset,function,parse,reset):
        self.frameReset = frameReset
        self.currentFrame = frameReset
        self.function = function
        self.parse = parse
        self.reset = reset
        if (self.reset != "onComplete"):
            self.reset = (reset-1)
        enterFrameTable.append(self)

    def step(self,enterFrameTable):
        if (self.currentFrame == 0):
            self.function(self.parse)
            if (self.reset != "onComplete"):
                if (self.reset > 0): 
                    self.currentFrame = self.frameReset
                    self.reset = self.reset-1
                else:
                    enterFrameTable.remove(self)
                    del self
            else:
                self.currentFrame = self.frameReset
        else:
            self.currentFrame = self.currentFrame-1

class PlayerCreation():
    def __init__(self): 
        self.x = _W
        self.y = _H
        self.view = [1600,1600]
        self.viewShift = []
        self.viewChangeSpeed = 25

    def moveView(self,key):
        add = EnterFrame(0,self.moveViewAction,key,"onComplete")
        self.viewShift.append([add,key])

    def moveViewAction(self,key):
        if (key == "up"):
            self.view[1] = self.view[1]-self.viewChangeSpeed
            Map.recenterView()
            if (self.view[1] < 0):
                self.view[1] = 0

        elif (key == "right"):
            self.view[0] = self.view[0]+self.viewChangeSpeed
            Map.recenterView()
            if (self.view[0] > Map.tileSize*4):
                self.view[0] = Map.tileSize*4


        elif (key == "down"):
            self.view[1] = self.view[1]+self.viewChangeSpeed
            Map.recenterView()
            if (self.view[1] > Map.tileSize*4):
                self.view[1] = Map.tileSize*4


        elif (key == "left"):
            self.view[0] = self.view[0]-self.viewChangeSpeed
            Map.recenterView()
            if (self.view[0] < 0):
                self.view[0] = 0

    def endMoveView(self,key):
        for i in range(len(self.viewShift)-1,-1,-1 ):
            if (self.viewShift[i][1] == key):
                enterFrameTable.remove(self.viewShift[i][0])
                del self.viewShift[i]               

class ImageCreation():
    def __init__(self,name,image,type,hitBox):
        self.name = name
        self.image = image
        self.type = type
        self.hitBox = hitBox
        self.rect = self.image.get_rect()
        if (self.hitBox != "none"):
            self.shiftX = hitBox[0][0]
            self.shiftY = hitBox[0][1]
            for i in range(1,len(hitBox) ):
                if (hitBox[i][0] < self.shiftX):
                    self.shiftX = hitBox[i][0]

                if (hitBox[i][1] < self.shiftY):
                    self.shiftY = hitBox[i][1]

        else:
            self.shiftX = self.rect[2]/2
            self.shiftY = self.rect[3]/2

        imageTable.append(self)

    def draw(self,x,y):
        image = self.image
        self.blit = gameDisplay.blit(image,(x,y) )

class MapCreation():
    def __init__(self):
        self.tileSize = 800
        self.size = self.tileSize*10
        self.tiles = []
        self.loadedTiles = []
        self.surface = pygame.Surface([self.tileSize*5,self.tileSize*5], pygame.SRCALPHA, 32) 
        self.centerTile = [5,5] 
        self.drawPoint = [(_W-self.tileSize)/2,(_H-self.tileSize)/2]
        self.amount = round(self.size/self.tileSize)

        backGround = loadScale("Grass.png","size",self.tileSize,self.tileSize)
        for i in range(0,self.amount):
            for u in range(0,self.amount):
                image = copy.copy(backGround)
                newTile = Tile(image,[u*self.tileSize,i*self.tileSize])
                self.tiles.append(newTile)



        info = imageFind("House.png")
        tile = self.tiles[55]
        imageObject(info,tile,[240,50])

        self.loadTiles("center")

    def recenterView(self):

        if (Player.view[0] > 3*self.tileSize):
            self.centerTile[0] = self.centerTile[0]+1
            Player.view[0] = Player.view[0]-self.tileSize
            self.loadTiles("right")
            print("right")

        elif (Player.view[0] < 1*self.tileSize):
            self.centerTile[0] = self.centerTile[0]-1
            Player.view[0] = Player.view[0]+self.tileSize
            self.loadTiles("center")
            print("center")


        if (Player.view[1] > 3*self.tileSize):
            self.centerTile[1] = self.centerTile[1]+1
            Player.view[1] = Player.view[1]-self.tileSize
            self.loadTiles("center")
            print("center")


        elif (Player.view[1] < 1*self.tileSize):
            self.centerTile[1] = self.centerTile[1]-1
            Player.view[1] = Player.view[1]+self.tileSize
            self.loadTiles("center")
            print("center")


    def loadTiles(self,load):
        tileIndex = self.centerTile[0]+self.centerTile[1]*self.amount
        if (load == "center"):
            self.loadedTiles = []
            for i in range(-2,3):
                for u in range(-2,3):
                    loadTile = tileIndex + i*self.amount + u
                    self.loadedTiles.append(self.tiles[loadTile])
                    self.tiles[loadTile].loaded = [u+2,i+2]

            self.surface = pygame.Surface([self.tileSize*5,self.tileSize*5], pygame.SRCALPHA, 32)       

            for i in range(0,len(self.loadedTiles) ):           
                sx = self.loadedTiles[i].loaded[0]*self.tileSize
                sy = self.loadedTiles[i].loaded[1]*self.tileSize
                self.surface.blit(self.loadedTiles[i].buttomLayer,(sx,sy) )


            for i in range(0,len(self.loadedTiles) ):           
                sx = self.loadedTiles[i].loaded[0]*(self.tileSize+2)
                sy = self.loadedTiles[i].loaded[1]*(self.tileSize+2)
                self.surface.blit(self.loadedTiles[i].middleLayer,(sx,sy) )

            for i in range(0,len(self.loadedTiles) ):           
                sx = self.loadedTiles[i].loaded[0]*(self.tileSize+2)
                sy = self.loadedTiles[i].loaded[1]*(self.tileSize+2)
                self.surface.blit(self.loadedTiles[i].topLayer,(sx,sy) )

        elif (load == "right"):
            self.loadedTiles = []
            for i in range(-2,3):
                for u in range(-2,3):
                    loadTile = tileIndex + i*self.amount + u
                    self.loadedTiles.append(self.tiles[loadTile])
                    self.tiles[loadTile].loaded = [u+2,i+2]

            ## OLD METHOD THAT WASNT WORKING ##
            ##subSurf = self.surface.subsurface(self.tileSize,0,self.tileSize*1,self.tileSize*5)
            ##self.surface = pygame.Surface([self.tileSize*5,self.tileSize*5], pygame.SRCALPHA, 32)
            ##self.surface.blit(subSurf,(0,0) )

            ## NEW METHOD ##
            self.surface.scroll(dx=-self.tileSize*1,dy=0)

    def draw(self):
        global Player       
        image = self.surface.subsurface(Player.view[0],Player.view[1],self.tileSize,self.tileSize)      
        image = pygame.transform.scale(image,(self.tileSize,self.tileSize) )
        gameDisplay.blit(image,((_W-self.tileSize)/2,(_H-self.tileSize)/2) )
        image = pygame.transform.scale(self.surface,(300,300) )
        gameDisplay.blit(image,(0,0 ) )

class Tile():
    def __init__(self,image,coords):
        transparentSurface = pygame.Surface([1000,1000], pygame.SRCALPHA, 32)
        self.x = coords[0]
        self.y = coords[1]
        self.loaded = False
        self.buttomLayer = image
        self.middleLayer = copy.copy(transparentSurface)
        self.topLayer = transparentSurface      

class imageObject():
    def __init__(self,info,tile,coords):
        self.info = info
        self.image = info.image
        self.rect = self.image.get_rect()

        self.x = coords[0]
        self.y = coords[1]

        self.hitBox = []

        if (self.info.hitBox != "none"):
            for i in range(0,len(self.info.hitBox) ):
                self.hitBox.append([self.info.hitBox[i][0]+self.x,self.info.hitBox[i][1]+self.y])
        #self.object = createObject(self.hitBox,True,"none")

        if (info.type == "background"):
            tile.buttomLayer.blit(self.image,(self.x,self.y) )
        if (info.type == "object"):
            tile.middleLayer.blit(self.image,(self.x,self.y) )
        if (info.type == "object alphas"):
            tile.topLayer.blit(self.image,(self.x,self.y) )


def imageFind(name):
    for i in range(0,len(imageTable) ):
        if (name == imageTable[i].name):
            return(imageTable[i])

    return("none") 

def imageLoad(types):
    if (types == "basic"):
        image = loadScale("House.png","ratio",1,1)
        basicHouse = ImageCreation("House.png",image,"object",[[190, 375], [350, 375], [350, 235], [190, 235]])

        image = loadScale("Grass.png","ratio",1,1)
        grass = ImageCreation("Grass.png",image,"background","none")

def enterFrameHandle(enterFrameTable): 
    for i in range(len(enterFrameTable)-1,-1,-1 ):
        enterFrameTable[i].step(enterFrameTable)

def EventHandle(event):
    global Player

    if (event.type == pygame.QUIT):
        endGame()

    elif(event.type == pygame.KEYDOWN):
        key = (pygame.key.name(event.key) )

        if (key == "escape"):
            endGame()       

        elif (key == "up" or key == "right" or key == "down" or key == "left"):
            Player.moveView(key)

    elif(event.type == pygame.KEYUP):
        key = (pygame.key.name(event.key) )
        if (key == "up" or key == "right" or key == "down" or key == "left"):
            Player.endMoveView(key)

def endGame():
    global QuitGame
    QuitGame = True

def mainLoop(): 

    ## GLOBALS ##
    global QuitGame
    QuitGame = False

    global Player
    Player = PlayerCreation()

    ## MAIN TABLES ##
    global enterFrameTable
    enterFrameTable = []

    global basicObjectTable
    basicObjectTable = []

    ## TEMP TABLES ##
    global imageTable
    imageTable = []


    ## START UP LOOPS ##

    imageLoad("basic")
    global Map
    Map = MapCreation()

    ## Temporary ##

    while (QuitGame == False):
        enterFrameHandle(enterFrameTable)
        for event in pygame.event.get():
            EventHandle(event)


        Map.draw()
        pygame.display.update() ## updates the screen ##

        gameDisplay.fill([0,0,0]) ## Clears screen for next frame ##
        gameClock.tick(64) ## The FPS ##
        fps = gameClock.get_fps()
        if (fps < 64 and fps != 0):
            fps = gameClock.get_fps()
            print("FPS HAS DROPPED TOO LOW DOWN TO",fps)


runGameNow = True

mainLoop()
pygame.quit()
quit()

带有路径查找器的问题是在找到最近点时在找到问题的路线

1 个答案:

答案 0 :(得分:0)

问题非常直接。
你认为疾病的线条数量和原因必须是Pygame。通常,或者至少几乎在任何情况下 - 如果您不是使用语言和库的上帝,问题可能不是语言或库。因为可能性很小,所以你无法接近推动两者为你提供的极限 如果你看一下你的眼睛在屏幕上看到的东西,你会立即注意到,无论什么时候你的背景即将循环 - 当发生毛刺/帧丢失时,你会发现这个事实。 。 显然,这可能是图书馆做一些可疑事情的原因..如果是图书馆做了实际的循环。

但在您的代码中,这是您自己完成的一项实施 所以最好的选择是开始寻找那里 只是对延迟发生的地方非常有信心 - 我们可以看看Python附带的分析器(cProfiler)。

python -m cProfile -o sample_data.pyprof awesome_game.py
pyprof2calltree -i sample_data.pyprof -k

结果将显示以下内容: enter image description here

由此可以看出,大量处理时间流向pygame.SurfaceenterFrameHandle。我敢打赌pygame.Surface会在enterFrameHandle内的某个地方调用。

所以最好的选择是从eventFrameHandle链的末尾开始 在这个细分中,loadTiles

直接蝙蝠,这里有很多警告标志 这里至少有四个循环..循环不好,因为那些需要处理时间。

我只是简单地添加了调试信息:

def loadTiles
    if load == "center":
        print('Loading center')
    elif load == "right":
        print('Loading right')

显然,只要出现故障,center就会被触发 这样可以进一步缩小范围。所以我在每个循环周围添加了计时器。

  • 循环一:花费0.0秒
  • 循环二:花费0.29秒
  • 循环三:需要0.5秒
  • 循环四:需要0.6秒

总而言之,这些对您来说非常糟糕,因为它们会直接影响渲染序列。那么为什么呢?让我们进一步分解。
我们将从第一个占用0.3秒的循环开始:

for i in range(0,len(self.loadedTiles) ):           
    sx = self.loadedTiles[i].loaded[0]*self.tileSize
    sy = self.loadedTiles[i].loaded[1]*self.tileSize
    self.surface.blit(self.loadedTiles[i].buttomLayer,(sx,sy) )

所以len(self.loadedTiles)将是25 这意味着这个循环必须在每个渲染周期迭代25次,其中loadTiles("center")被调用。

每个周期几乎完全0.01秒,每个完整循环最多可达0.25秒。这不是很糟糕,好吧..如果你想要60 FPS的游戏,没有循环或函数调用总共需要超过0.0166秒

所以我们已经超出了我们想要的FPS目标。如果我们想去任何地方,我们就要敲几毫秒。

循环本身并没有那么糟糕,我的意思是25次迭代,现代PC可以在比time()可以测量的时间更短的时间内完成。所以,这一切都指向self.surface.blit() ..从经验来看,确实如此 这也与上面我们的图表在pygame.surface.blit中花费30%的总CPU时间的事实相关联。所以这是我们的第一个小偷。

看看其余的循环,它们基本上是相同的,除了数学中的数字越大,计算时间越长,因此可能是循环中的时间差异。

那么,我们可以做些什么来缩减.blit次呢? 好吧,我们可以在每次循环迭代中停止调用blit,只需移动精灵对象位置然后逐个blit它们。
但是,老乡,这几乎是一回事吗? 好吧,我很快就会成为忍者龟,它就是......那就是为什么,我们将精灵移动到批次/组中​​,然后渲染组。因此,我们可以更改位置(快速操作),因为我们将渲染移到循环之外,我们也可以将它们转换为组对象并渲染组。

首先,我们将您的对象转换为pygame.sprite.Sprite,这些是巧妙的小对象,包含碰撞检测,移动等等。

self.grass = pygame.sprite.Sprite()
self.grass.image = pygame.image.load("Grass.png")
self.grass.rect = pygame.Rect(0, 0, 60, 60)

然后将其添加到渲染组(批处理)中:

self.main_group = pygame.sprite.Group(self.grass)

Bam,而不是更新25个精灵,现在你可以这样做:

self.main_group.draw(gameDisplay)

和bam,SPEED!

现在,您的代码并非专为此而设计 所以这需要一段时间来修复和纠正这个设计缺陷 因为我需要花费数小时才能保持原始代码尽可能接近,所以我忽略了这一点并重新设计了整个MapCreation并修改了PlayerCreation以及如何移动地图(我改变了每次试图重新定位,这是一个好主意,但很难实现1PM重写其他人的代码)..所以..这是解决问题的新方法:

import pygame, os
from time import time
from collections import OrderedDict
os.environ['SDL_VIDEO_CENTERED'] = '1'
pygame.init()

_W,_H = pygame.display.Info().current_w, pygame.display.Info().current_h
flags = pygame.DOUBLEBUF | pygame.HWSURFACE | pygame.RESIZABLE
gameDisplay = pygame.display.set_mode((500, 500))
gameDisplay.fill((0,0,0)) ## FILL COLOR OF SCREEN ##
pygame.display.set_caption("Dope Game")  ## SETS NAME ##
gameClock = pygame.time.Clock() ## CLOCK OF THE GAME ##
import math
import os
import random
import copy


SQRT = math.sqrt
PI = math.pi
cos = math.cos
sin = math.sin


## REPEATABLE FUNCTIONS ##

def loadScale(file,command,sizeX,sizeY):
    temp = pygame.image.load(file)
    tempInfo = temp.get_rect()
    tempInfo1,tempInfo2,tempInfo3,tempInfo4 = temp.get_rect()
    tempInfo3 = int(tempInfo3)
    tempInfo4 = int(tempInfo4)
    if (command == "ratio"):    
        tempInfo3 = tempInfo3*sizeX
        tempInfo4 = tempInfo4*sizeY
        temp = pygame.transform.scale(temp,(int(tempInfo3),int(tempInfo4) ) )

    elif (command == "size"):
        temp = pygame.transform.scale(temp, (sizeX,sizeY) )         

    return(temp)

## NON GAME RELATED CLASSES ##

class EnterFrame():
    def __init__(self,frameReset,function,parse,reset):
        self.frameReset = frameReset
        self.currentFrame = frameReset
        self.function = function
        self.parse = parse
        self.reset = reset
        if (self.reset != "onComplete"):
            self.reset = (reset-1)
        enterFrameTable.append(self)

    def step(self,enterFrameTable):
        if (self.currentFrame == 0):
            self.function(self.parse)
            if (self.reset != "onComplete"):
                if (self.reset > 0): 
                    self.currentFrame = self.frameReset
                    self.reset = self.reset-1
                else:
                    enterFrameTable.remove(self)
                    del self
            else:
                self.currentFrame = self.frameReset
        else:
            self.currentFrame = self.currentFrame-1

class PlayerCreation():
    def __init__(self): 
        self.x = _W
        self.y = _H
        self.view = [1600,1600]
        self.viewShift = []
        self.viewChangeSpeed = 25

    def moveView(self,key):
        add = EnterFrame(0,self.moveViewAction,key,"onComplete")
        self.viewShift.append([add,key])

    def moveViewAction(self,key):
        if (key == "up"):
            self.view[1] = self.view[1]-self.viewChangeSpeed
            Map.move_tile(0, 1) # Player moves up, so the tiles should move down ->  (0, 1) == (x, y)
            if (self.view[1] < 0):
                self.view[1] = 0

        elif (key == "right"):
            self.view[0] = self.view[0]+self.viewChangeSpeed
            Map.move_tile(-1, 0)
            if (self.view[0] > Map.tileSize*4):
                self.view[0] = Map.tileSize*4

        elif (key == "down"):
            self.view[1] = self.view[1]+self.viewChangeSpeed
            Map.move_tile(0, -1)
            if (self.view[1] > Map.tileSize*4):
                self.view[1] = Map.tileSize*4

        elif (key == "left"):
            self.view[0] = self.view[0]-self.viewChangeSpeed
            Map.move_tile(1, 0) 
            if (self.view[0] < 0):
                self.view[0] = 0

    def endMoveView(self,key):
        for i in range(len(self.viewShift)-1,-1,-1 ):
            if (self.viewShift[i][1] == key):
                enterFrameTable.remove(self.viewShift[i][0])
                del self.viewShift[i]               

class ImageCreation():
    def __init__(self,name,image,type,hitBox):
        self.name = name
        self.image = image
        self.type = type
        self.hitBox = hitBox
        self.rect = self.image.get_rect()
        if (self.hitBox != "none"):
            self.shiftX = hitBox[0][0]
            self.shiftY = hitBox[0][1]
            for i in range(1,len(hitBox) ):
                if (hitBox[i][0] < self.shiftX):
                    self.shiftX = hitBox[i][0]

                if (hitBox[i][1] < self.shiftY):
                    self.shiftY = hitBox[i][1]

        else:
            self.shiftX = self.rect[2]/2
            self.shiftY = self.rect[3]/2

        imageTable.append(self)

    def draw(self,x,y):
        image = self.image
        self.blit = gameDisplay.blit(image,(x,y) )

class MapCreation():
    def __init__(self):
        self.tileSize = 800
        self.size = self.tileSize*10
        self.tiles = []
        self.centerTile = [5,5] 
        self.amount = round(self.size/self.tileSize)

        self.sprites = OrderedDict()
        self.grass_image = pygame.image.load("Grass.png")
        self.grass_group = pygame.sprite.Group()

        for x in range(0, self.amount*60, 60):      ## 10*60, but we step 60 pixels, so in the end, this will be 10 steps.
            for y in range(0, self.amount*60, 60):  ## Which is the same as `for x in range(self.amount)` but we scale it up
                                                    ## to give us pixels instead of just the ammount.
                index = len(self.sprites) # -- Generate a index for this sprite. Used for access later (to update pos for instace)

                ## == Create the sprite, add a image to it and define a position and size.
                self.sprites[index] = pygame.sprite.Sprite()
                self.sprites[index].image = self.grass_image
                self.sprites[index].rect = pygame.Rect(x, y, 60, 60)

                ## == Then add the sprite to the grass group.
                self.grass_group.add(self.sprites[index])

    def move_tile(self, dx, dy):
        for index in self.sprites:
            x, y, width, height = self.sprites[index].rect
            ## == this is how you move the sprites:d
            self.sprites[index].rect = pygame.Rect(x+dx, y+dy, 60, 60)

    def draw(self):
        self.grass_group.update()
        self.grass_group.draw(gameDisplay)

class Tile():
    def __init__(self,image,coords):
        transparentSurface = pygame.Surface([1000,1000], pygame.SRCALPHA, 32)
        self.x = coords[0]
        self.y = coords[1]
        self.loaded = False
        self.buttomLayer = image
        self.middleLayer = copy.copy(transparentSurface)
        self.topLayer = transparentSurface      

class imageObject():
    def __init__(self,info,tile,coords):
        self.info = info
        self.image = info.image
        self.rect = self.image.get_rect()

        self.x = coords[0]
        self.y = coords[1]

        self.hitBox = []

        if (self.info.hitBox != "none"):
            for i in range(0,len(self.info.hitBox) ):
                self.hitBox.append([self.info.hitBox[i][0]+self.x,self.info.hitBox[i][1]+self.y])
        #self.object = createObject(self.hitBox,True,"none")

        if (info.type == "background"):
            tile.buttomLayer.blit(self.image,(self.x,self.y) )
        if (info.type == "object"):
            tile.middleLayer.blit(self.image,(self.x,self.y) )
        if (info.type == "object alphas"):
            tile.topLayer.blit(self.image,(self.x,self.y) )


def imageFind(name):
    for i in range(0,len(imageTable) ):
        if (name == imageTable[i].name):
            return(imageTable[i])

    return("none") 

def imageLoad(types):
    if (types == "basic"):
        image = loadScale("House.png","ratio",1,1)
        basicHouse = ImageCreation("House.png",image,"object",[[190, 375], [350, 375], [350, 235], [190, 235]])

        image = loadScale("Grass.png","ratio",1,1)
        grass = ImageCreation("Grass.png",image,"background","none")

def enterFrameHandle(enterFrameTable): 
    for i in range(len(enterFrameTable)-1,-1,-1 ):
        enterFrameTable[i].step(enterFrameTable)

def EventHandle(event):
    global Player

    if (event.type == pygame.QUIT):
        endGame()

    elif(event.type == pygame.KEYDOWN):
        key = (pygame.key.name(event.key) )

        if (key == "escape"):
            endGame()       

        elif (key == "up" or key == "right" or key == "down" or key == "left"):
            Player.moveView(key)

    elif(event.type == pygame.KEYUP):
        key = (pygame.key.name(event.key) )
        if (key == "up" or key == "right" or key == "down" or key == "left"):
            Player.endMoveView(key)

def endGame():
    global QuitGame
    QuitGame = True

def mainLoop(): 

    ## GLOBALS ##
    global QuitGame
    QuitGame = False

    global Player
    Player = PlayerCreation()

    ## MAIN TABLES ##
    global enterFrameTable
    enterFrameTable = []

    global basicObjectTable
    basicObjectTable = []

    ## TEMP TABLES ##
    global imageTable
    imageTable = []


    ## START UP LOOPS ##

    imageLoad("basic")
    global Map
    Map = MapCreation()

    ## Temporary ##

    while (QuitGame == False):
        enterFrameHandle(enterFrameTable)
        for event in pygame.event.get():
            EventHandle(event)


        pygame.display.update() ## updates the screen ##
        gameDisplay.fill([0,0,0]) ## Clears screen for next frame ##

        #Map.draw()
        Map.grass_group.update()
        Map.grass_group.draw(gameDisplay)

        gameClock.tick(64) ## The FPS ##
        fps = gameClock.get_fps()
        if (fps < 64 and fps != 0):
            fps = gameClock.get_fps()
            print("FPS HAS DROPPED TOO LOW DOWN TO",fps)

        pygame.display.flip()


runGameNow = True

mainLoop()
pygame.quit()
quit()

此代码很少在FPS中下拉。
当它发生时,它下降到~63 FPS,因为我移动了窗口(这是一件令人讨厌的事情,因为所有OpenGL引用都需要补偿新的窗口位置和在Pygame中触发的事件(调整大小事件) ,移动事件等正在征税,但只发生一次,而不是每个渲染循环......所以它是可以接受的。)

结论

  1. 由于性能缓慢,图书馆的故障很少。
  2. 停止思考作为开发人员一秒钟,并观看屏幕,看看你是否可以找到故障所在的线索。在这种情况下,每次瓷砖围绕屏幕边缘旋转时都会出现明显的毛刺。这是一个线索!没有一种编程技能可以直观地看到这个bug。
  3. 使用分析器并调试代码在大部分时间内花费的时间。
  4. 试着了解其他人如何解决游戏设计问题。通过将所有内容放入课堂中,您已走上正轨,但这主要有助于您对代码的可读性 - 尝试了解其他人的内容类和他们如何使用Pygame调用。
  5. 总而言之,这是一个非常有趣和优秀的代码 它只是没有为性能而做,以后很难编写代码......所以也许从头开始并不是一个坏主意?但请保留旧代码库以供参考,避免明显的陷阱:

    • 循环渲染序列
    • 逐个渲染对象,改为使用批次
    • 只做循环来更新位置/统计数据,而不是图形!

    关于这个主题的最后一点:图形卡是巨大的动力房。如果不是更多的话,他们可以每秒计算几百万次操作。每当你blit某些东西进入屏幕时,显卡需要中断它的计算和内存分配,以翻转缓冲区并更新你在屏幕上看到的东西..而且这很慢工作,因为它不是计算,它的操作任务(通常包括等待队列和信号)..所以而不是向显卡投掷数百blit秒通过发送纹理(描述事物外观的数学数据)向它投掷数学运算..然后调用blit(在这种情况下为draw)一次让图形卡全部用数学计算你扔了它 - 做一个大的逻辑操作 - 并且已经预先设定了x100速度。