python - Kivy 性能随时间下降

标签 python python-3.x frameworks kivy kivy-language

我正在学习 Kivy,为了让它更有趣,我正在创建一个小型 2D 游戏。目前它只是一辆可以用 WASD 控制并用 o 发射炮弹的坦克。

问题是 FPS 会随着时间的推移而降低。即使我在游戏中什么都不做,也会发生这种情况。我没有 FPS 计数器,但游戏一分钟后 FPS 大约是一半。

我的感觉是问题出在小部件中 Canvas 的更新上。由于即使玩家在整个游戏中什么都不做,速度也会变慢,看起来好像某个地方有数据刚刚添加和添加。我不知道如何更好地解释它,除了它很奇怪......


快速概述到目前为止游戏的编程方式:

主要的小部件是 Game 类。它检测按键并运行“Clock.schedule_interval-function”。

坦克部件是游戏的子部件。它保存一些数据并通过 Kivys Image 小部件加载船体和炮塔 Sprite ,该小部件成为其子部件。它有自己的更新功能,可以更新与坦克相关的所有内容,包括设置 Canvas 的位置以及旋转船体和炮塔图像 Canvas 。 Tank 小部件类中的更新函数由 Game 类中的“Clock.schedule_interval”调用。

“射击”小部件的功能与“坦克”小部件相同,只是它保存每次射击的数据

“时钟计划间隔”函数保存每个镜头小部件的列表,并在它们离开屏幕时将其删除。然而,即使没有开枪,减速问题仍然存在。


我已经附上了完整的代码。这可能有点过分,但我不知道哪一部分会导致速度变慢。如果你想运行游戏,只需将这四个 python 文件放在同一个文件夹中,并将图像放在名为“images Tank”的子文件夹中即可。

希望有人能看一下

main.py:

#Import my own modules:
import tank
import shot
from stats import Stats
#Import kivy:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.clock import Clock

#Set window size properties:
from kivy.config import Config
Config.set('graphics','resizable',0)
from kivy.core.window import Window


class Game(Widget):
    def __init__(self):
        #General settings:
        super(Game, self).__init__()
        Clock.schedule_interval(self.update, 1.0/60.0)
        
        #Add keyboard: 
        self._keyboard = Window.request_keyboard (callback=None, target=self, input_type="text")
        #Bind the keyboard to a function:
        self._keyboard.bind(on_key_down=self.keypress)
        self._keyboard.bind(on_key_up=self.keyUp)
        
        #P1 tank starting values:
        self.keypress_p1 = {"forward":False, "backward":False, "left":False, "right":False, "turret_left":False, "turret_right":False, "fire":False}
        
        #P1 tank widget:
        self.tank_p1 = tank.Tank()
        self.add_widget(self.tank_p1)
        
        #P1 shots list:
        self.shotsList_p1 = []
        
        
    #Keyboard press detection:    
    def keypress(self, *args):
        key = args[2]
        if key == "w":
            self.keypress_p1["forward"]=True
        if key == "s":
            self.keypress_p1["backward"]=True
        if key == "a":
            self.keypress_p1["left"]=True
        if key == "d":
            self.keypress_p1["right"]=True
        if key == "q":
            self.keypress_p1["turret_left"]=True
        if key == "e":
            self.keypress_p1["turret_right"]=True
        if key == "o":
            self.keypress_p1["fire"]=True
            
    #Keyboard button up detection:    
    def keyUp(self, *args):
        key = args[1][1]
        if key == "w":
            self.keypress_p1["forward"]=False
        if key == "s":
            self.keypress_p1["backward"]=False
        if key == "a":
            self.keypress_p1["left"]=False
        if key == "d":
            self.keypress_p1["right"]=False
        if key == "q":
            self.keypress_p1["turret_left"]=False
        if key == "e":
            self.keypress_p1["turret_right"]=False
        if key == "o":
            self.keypress_p1["fire"]=False
            
            
    #Parent update function that the clock runs:   
    def update(self, dt):
    
        #Add new shots:
        if self.keypress_p1["fire"]:
            self.shot = shot.Shots(self.tank_p1.my_pos, self.tank_p1.my_angle+self.tank_p1.my_turretAngle)
            self.shotsList_p1.append(self.shot)
            self.add_widget(self.shot)
            self.keypress_p1["fire"] = False
            
        #P1 tank update:
        self.tank_p1.update(self.keypress_p1)
        
        #P1 shot update:
        for i in range(len(self.shotsList_p1)-1,-1,-1):
            self.shotsList_p1[i].update()
            #Remove widgets that are outside the screen:
            if ( 0<=self.shotsList_p1[i].my_pos[0]<Stats.winSize[0] and 0<=self.shotsList_p1[i].my_pos[1]<Stats.winSize[1] )==False:
                self.remove_widget(self.shotsList_p1[i])
                del self.shotsList_p1[i]
            

class MyApp(App):
    def build(self):
        game = Game()
        Window.size = Stats.winSize
        return game
        
MyApp().run()

坦克.py:

#Import own modules:
from stats import Stats
#import python:
import math
#Import Kivy:
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.graphics.context_instructions import PushMatrix, PopMatrix, Rotate, Translate, MatrixInstruction
from kivy.graphics.fbo import Fbo


class Tank(Widget):
    def __init__(self):
        super(Tank, self).__init__()
        
        #Position and rotation values for the tank:
        self.my_pos = [0,0]
        self.posChange = [0,0]
        self.my_angle = 0
        self.angleChange = 0
        self.my_turretAngle = 0
        self.turretAngleChange = 0
        
        #Hull widget:
        self.hull = Hull()
        self.add_widget(self.hull)
        self.hull.center_x = self.my_pos[0]
        self.hull.center_y = self.my_pos[1]
        
        #Turret widget:
        self.turret = Turret()
        self.add_widget(self.turret)
        self.turret.center_x = self.my_pos[0]
        self.turret.center_y = self.my_pos[1]
        
    def update(self, keypress):
        if keypress["forward"]:
            self.my_pos[0] -= Stats.hull_t1["forward"]*math.sin(math.radians(self.my_angle))
            self.my_pos[1] += Stats.hull_t1["forward"]*math.cos(math.radians(self.my_angle))
            self.posChange[0] = -Stats.hull_t1["forward"]*math.sin(math.radians(self.my_angle))
            self.posChange[1] = Stats.hull_t1["forward"]*math.cos(math.radians(self.my_angle))
            
        if keypress["backward"]:
            self.my_pos[0] -= Stats.hull_t1["backward"]*math.sin(math.radians(self.my_angle))
            self.my_pos[1] += Stats.hull_t1["backward"]*math.cos(math.radians(self.my_angle))
            self.posChange[0] = -Stats.hull_t1["backward"]*math.sin(math.radians(self.my_angle))
            self.posChange[1] = Stats.hull_t1["backward"]*math.cos(math.radians(self.my_angle))
             
        if keypress["left"]:
            self.my_angle += Stats.hull_t1["left"]
            self.angleChange = Stats.hull_t1["left"]
            
        if keypress["right"]:
            self.my_angle += Stats.hull_t1["right"]
            self.angleChange = Stats.hull_t1["right"]
            
        if keypress["turret_left"]:
            self.my_turretAngle += Stats.turret_t1["left"]
            self.turretAngleChange = Stats.turret_t1["left"]
            
        if keypress["turret_right"]:
            self.my_turretAngle += Stats.turret_t1["right"]
            self.turretAngleChange = Stats.turret_t1["right"]
            
        
            
        #Tank Position:
        with self.canvas.before:
            PushMatrix()
            Translate(self.posChange[0], self.posChange[1])
        with self.canvas.after:
            PopMatrix()
            
        #Rotate hull image:
        with self.hull.canvas.before:
            PushMatrix()
            self.rot = Rotate()
            self.rot.axis = (0,0,1)
            self.rot.origin = self.hull.center
            self.rot.angle = self.angleChange
        with self.hull.canvas.after:
            PopMatrix()
            
        #Rotate turret image:
        with self.turret.canvas.before:
            PushMatrix()
            self.rot = Rotate()
            self.rot.axis = (0,0,1)
            self.rot.origin = self.turret.center
            self.rot.angle = self.turretAngleChange + self.angleChange
        with self.turret.canvas.after:
            PopMatrix()
        
        #Reset pos, angle and turretAngle change values:
        self.posChange = [0,0]
        self.angleChange = 0
        self.turretAngleChange = 0
        
        
#--------------------------------------------------------------------------------------------------            
class Hull(Image):
    def __init__(self):
        super(Hull, self).__init__(source="images tank/Tank.png")
        self.size = self.texture_size
        
class Turret(Image):
    def __init__(self):
        super(Turret, self).__init__(source="images tank/GunTurret.png")
        self.size = self.texture_size
        

射击.py:

#Import own modules:
from stats import Stats
#import python:
import math
from copy import copy
#Import Kivy:
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.graphics.context_instructions import PushMatrix, PopMatrix, Rotate, Translate, MatrixInstruction
from kivy.graphics.fbo import Fbo

class Shots(Widget):
    
    def __init__(self, tankPos, turretAngle):
        super(Shots, self).__init__()
        #Shot data:
        self.my_pos = copy(tankPos)
        self.my_angle = turretAngle
        self.angleChange = self.my_angle
        self.posChange = [ -Stats.shot_t1["speed"]*math.sin(math.radians(self.my_angle)), Stats.shot_t1["speed"]*math.cos(math.radians(self.my_angle)) ]
        #Add image:
        self.shotImg = ShotImg()
        self.add_widget(self.shotImg)
        self.shotImg.pos = self.my_pos
        self.shotImg.center_x = self.my_pos[0]
        self.shotImg.center_y = self.my_pos[1]
        
        
        
    def update(self):
        self.my_pos[0] += self.posChange[0]
        self.my_pos[1] += self.posChange[1]
        #Shot Position:
        with self.canvas.before:
            PushMatrix()
            Translate(self.posChange[0], self.posChange[1])
        with self.canvas.after:
            PopMatrix()
        
        #Rotate shot image:
        if self.angleChange != 0:
            with self.shotImg.canvas.before:
                PushMatrix()
                self.rot = Rotate()
                self.rot.axis = (0,0,1)
                self.rot.origin = self.shotImg.center
                self.rot.angle = self.angleChange
            with self.shotImg.canvas.after:
                PopMatrix()
            self.angleChange = 0
            
    
class ShotImg(Image):
    def __init__(self):
        super(ShotImg, self).__init__(source="images tank/Bullet.png")
        self.size = self.texture_size
        

统计.py:

    class Stats:
    winSize = (800,800)
    hull_t1 = {"forward":2, "backward":-2, "left":2, "right": -2}
    turret_t1 = {"left":5, "right":-5}
    shot_t1 = {"speed":3}

图像应该放在名为“images Tank”的子文件夹中:

最佳答案

你管理位置的方式将为每个坦克创建 8 个新指令,每帧每个镜头创建 6 个新指令,在 60fps 下,将快速创建数千个指令,而 kivy 处理它们的速度会越来越慢。

    #Tank Position:
    with self.canvas.before:
        PushMatrix()
        Translate(self.posChange[0], self.posChange[1])
    with self.canvas.after:
        PopMatrix()

你不想这样做,你想在你的小部件中创建一个翻译指令(与旋转指令相同),并更新它,将此 block 移动到 __init__并保存翻译为self.translate例如,然后在更新中,不使用 posChange,只需执行 self.translate.x, self.translate.y = self.my_pos

对轮换和投篮应用相同的逻辑,随着时间的推移,性能应该会更加稳定。

关于python - Kivy 性能随时间下降,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49234333/

相关文章:

python - LinkedIn 公司搜索 API

python 3 : Exception or return code when resource limit exceeded?

java - 如何在 OpenXava 中创建复选框?

python - .join() 方法到底是做什么的?

python - 具有特定大小的组的聚类

python - 在 Python 中提取文件名的后缀

python - 如何打开与 Python 2 和 Python 3 兼容的 Tkinter AskOpenFileName 对话框

php - 用于 Web 开发的 MVC 架构的替代方案是什么?

c# - Entity Framework - 更新表中的一行

python - 连接WiFi python的最简单方法