Kivy性能随着时间的推移而降低

时间:2018-03-12 11:39:03

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

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

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

我的感觉是,问题在于窗口小部件中画布的更新。由于即使玩家没有为整个游戏做任何事情,减速也会出现,所以似乎某些地方的数据只是添加和添加。我不知道如何更好地解释它,除了它的怪异......

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

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

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

“镜头”小部件与“坦克”小部件的作用相同,只是它保存每个镜头的数据

“clock schedule_interval”-function保存每个镜头窗口小部件的列表,并在它们离开屏幕时删除它们。然而,即使没有射击,减速问题仍然存在。

我附上了完整的代码。这可能是过度的,但我不知道它的一部分会引起减速。如果你想运行游戏,只需将这四个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()

tank.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

shot.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

stats.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”的子文件夹中:

[Bullet][1]
[GunTurret][2]
[Tank][3]


[1]: https://i.stack.imgur.com/bY5dw.png
[2]: https://i.stack.imgur.com/SGE50.png
[3]: https://i.stack.imgur.com/znjdx.png

2 个答案:

答案 0 :(得分:1)

你管理你的位置的方式将为每个坦克创造8个新指令,每个镜头每个镜头6个,每帧60fps,将快速创建成千上万的指令,而kivy将更慢更慢地处理它们。 / p>

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

您不想这样做,您想在窗口小部件中创建一个Translate insruction(和Rotate相同)并更新它,将此块移动到__init__并将Translate保存到{{1}例如,然后在更新中,而不是使用posChange,只需执行self.translate

应用相同的旋转逻辑和镜头,随着时间的推移,性能应该更加稳定。

答案 1 :(得分:0)

我明白了。不幸的是,接下来的几天我离开了我的电脑,但是当我回来时我会尝试你的解决方案:)

我觉得Kivy的行为确实很奇怪。在同一个小部件中设置位置和旋转也存在问题。我做的旋转似乎对小部件产生了永久性影响,这意味着当我尝试在当前更新轮次中设置位置时,坐标系统已从上次更新旋转。即使我在翻译和旋转之前和之后都使用了Push-和PopMatrix(),这种情况也会发生。 我通过在单独的子窗口小部件中进行旋转来解决这个问题,但我发现kivys的行为有点不合逻辑,或者至少很难预测......

是否有任何关于坐标系的手册或教程以及它如何在某处工作?我已经尝试了文档,但它无法以连贯的方式解释它是如何工作的......