我正在学习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
答案 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的行为有点不合逻辑,或者至少很难预测......
是否有任何关于坐标系的手册或教程以及它如何在某处工作?我已经尝试了文档,但它无法以连贯的方式解释它是如何工作的......