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

Low frame rate, possibly because un-accelerated graphics

所以我几个月前开始使用 pygame 创建一个游戏,它的俯视图类似于星际争霸,帝国时代等......我写了大约 1320 行代码来创建基础我的游戏;但是,我在 blitting 图像时遇到了帧率问题,我相信这是因为我无法在 pygame 中使用加速图形。我目前使用 blit 图像的方式是提前将所有图像 blit 到一个表面上,然后在表面下创建整个屏幕的 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()

路径查找器的问题是由于在寻找最近点时在抖动线上拾取

这个问题真的很简单。
您认为线的数量是疾病,原因必须是Pygame。通常,或者至少在几乎任何情况下——如果你不是使用语言和库的神,问题可能不是语言或库。因为很有可能你没有接近突破两者可以为你提供的东西的极限。
这个事实的一个致命的泄露是,如果你看看你的眼睛在屏幕上看到的东西,你会很快注意到每当你的背景即将循环 - 那就是发生 glitch/frame 下降的时候。 显然,这可能是库做一些可疑的事情的原因。如果是库进行了实际的循环。

但在您的代码中,这是您自己完成的实现。
所以最好的办法是开始寻找那里。
只是为了对延迟发生的地方非常有信心 - 我们可以看看 Python (cProfiler).

附带的分析器
python -m cProfile -o sample_data.pyprof awesome_game.py
pyprof2calltree -i sample_data.pyprof -k

结果将显示如下内容:

由此可见,pygame.SurfaceenterFrameHandle的处理时间较多。我敢打赌 pygame.Surface 会在 enterFrameHandle 某处被调用。

所以最好的办法是从 eventFrameHandle 链的末尾开始。
在这个细分中,就是 loadTiles.

马上,这里有很多警告标志。
这里至少有四个循环..循环不好,因为它们需要处理时间。

我通过简单的操作添加了调试信息:

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

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

  • 循环一:耗时 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
这意味着此循环必须在调用 loadTiles("center") 的每个渲染周期迭代 25 次。

每个循环几乎正好 0.01 秒,即每个完整循环最多需要 0.25 秒。这并不可怕,但确实如此..如果你想在你的游戏中达到 60 FPS,没有循环或函数调用可以花费超过 0.0166 秒 总共 .

所以我们已经超过了我们想要的 FPS 目标。因此,如果我们想到达任何地方,我们必须缩短几毫秒。

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

看看其余的循环,它们本质上是相同的,只是数学中的数字更大,计算时间稍长,因此可能是循环中的时间差异。

那么,怎么做才能缩小.blit倍呢?
好吧,我们可以停止在每次循环迭代中调用 blit,只需移动精灵对象的位置,然后将它们一个一个地 blit。
但是老师,这几乎是一回事?
嗯,是的,我即将成为忍者神龟,它是..这就是为什么,我们将精灵移动到 batches/groups,并渲染组。因此,我们可以更改位置(快速操作),并且因为我们将渲染移到了循环之外,我们还可以将它们转换为组对象并渲染组。

首先,我们会将您的对象转换为 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)

砰,速度!

现在,您的代码不是为此而设计的。
所以这将需要一段时间来修复和纠正这个设计缺陷。
因为这对我来说需要几个小时才能使您的原始代码尽可能接近,所以我忽略了这一点并重新设计了您的整个 MapCreation 并修改了您的 PlayerCreation 以及您移动地图的方式(我从每次都尝试重新居中,这是一个不错的主意,但实施下午 1 点重写别人的代码要复杂得多)。所以..这是解决问题的新方法:

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 因为我移动了 window (这是一件很费力的事情,因为所有 OpenGL 引用都需要补偿新的 window 位置和触发的事件在 Pygame 内(调整大小事件、移动事件等很费力,但只发生一次,而不是在每个渲染循环中发生。所以这是可以接受的)。

结论

  1. 性能低下很少是图书馆的错。
  2. 暂时停止作为开发人员的思考,观察屏幕,看看您是否可以找到故障可能所在的线索。在这种情况下,每次图块围绕屏幕边缘旋转时都会出现明显的故障。这是一个线索!无需任何编程技能即可直观地看到此错误。
  3. 使用探查器并调试您的代码花费大部分时间的地方。
  4. 尝试看看其他人是如何解决游戏设计的。将所有内容都放在 类 中是正确的,但这主要有助于提高代码的可读性 - 尝试看看其他人的想法类 以及他们如何使用 Pygame 调用。

总而言之,您编写的代码非常有趣且不错。
它只是不是为了性能而设计的,而且之后很难编写代码……所以从头开始也许不是一个坏主意?但是请保留您的旧代码库以供参考并避免明显的陷阱:

  • 在渲染序列中循环
  • 逐个渲染对象,改为使用批处理
  • 只循环更新 positions/stats,不更新图形!

关于该主题的最后说明:图形卡是巨大的动力源。他们每秒可以计算几百万次操作,如果不是更多的话。每次你 blit 在屏幕上显示某些内容时,显卡都需要中断它的计算和内存分配,以翻转缓冲区并更新你在屏幕上看到的内容。这工作很慢,因为它不是计算,它是可操作的任务(通常包括等待队列和信号)。因此,与其向显卡发送数百个 blit 秒,不如通过向其发送纹理(描述事物外观的数学数据)来对其进行数学运算。 . 然后调用 blitdraw,在这种情况下)一次,让显卡接受你扔给它的所有数学 - 做一个大的逻辑运算 - 并且速度已经.. x100。