pyglet 中的平铺地图,fps 下降
Tiled map in pyglet, fps drop
我的英语很烂,我尽力写这个问题:D
我正在用 Pyglet 制作 2D 游戏,我需要制作一个平铺地图。
1 个图块 = 50 像素 x 50 像素
但是,当我绘制带有 20 个敌人的 50x50 地图时,我的 fps 从 60fps 下降到 10fps,这会占用大量 PC 资源
每个瓷砖和敌人都是批量绘制的。
我可以做些什么来提高我的游戏效率?
我尝试缩放图块,但每个图块上都有黑色边框,我想要 50x50 像素的图块,而不是 50x50/scale
#Create blocks 50x50 pixels and replace with image names to get what i see.
from pyglet.window import key, FPSDisplay
import pyglet
import math
Background = pyglet.graphics.OrderedGroup(0)
Walls_Group = pyglet.graphics.OrderedGroup(1)
def preload_image(image):
img = pyglet.image.load('images/' + image)
return img
map_x = 50
map_y = 50
window_X = 1500
window_Y = 900
class GameWindow(pyglet.window.Window):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_location(100, 30)
self.frame_rate = 1.0 / 100.0
self.fps_display = FPSDisplay(self)
self.fps_display.label.font_size = 30
self.player_speed = 550
self.right = False
self.left = False
self.up = False
self.down = False
self.map_load = False
self.Walls_load = False
self.map_scale = 1
self.wall = preload_image('block.png')
self.wall_list = []
self.map_1_list = []
self.sprite = preload_image('Grass_Green.png')
self.main_batch = pyglet.graphics.Batch()
def Mapka(self, x_size, y_size):
for Y in range(y_size):
for X in range(x_size):
self.map_1_list.append(pyglet.sprite.Sprite(self.sprite, x=X * (self.sprite.width*self.map_scale), y=Y * (self.sprite.height*self.map_scale), batch=self.main_batch, group=Background))
for i in self.map_1_list:
i.scale = self.map_scale
def Walls(self):
self.times = math.ceil(map_x * (self.sprite.width*self.map_scale) / self.wall.width) # Oblicza ilość ścian na dolnej części
# mapy z zaokrągleniem
self.times_y = math.ceil(map_y * (self.sprite.height*self.map_scale) / self.wall.height)
for x in range(int(self.times)):
self.wall_list.append(
pyglet.sprite.Sprite(self.wall, x=x * self.wall.width, y=0, batch=self.main_batch, group=Walls_Group))
self.wall_list.append(
pyglet.sprite.Sprite(self.wall, x=x * self.wall.width, y=(self.times_y - 1) * self.wall.height,
batch=self.main_batch, group=Walls_Group))
for y in range(int(self.times_y)):
self.wall_list.append(
pyglet.sprite.Sprite(self.wall, x=0, y=y * self.wall.height, batch=self.main_batch, group=Walls_Group))
self.wall_list.append(pyglet.sprite.Sprite(self.wall, x=self.times * self.wall.width - self.wall.width,
y=y * self.wall.height, batch=self.main_batch,
group=Walls_Group))
def on_draw(self):
self.clear()
if not self.map_load:
self.Mapka(map_x, map_y)
self.map_load = True
if not self.Walls_load:
self.Walls()
self.Walls_load = True
self.main_batch.draw()
self.fps_display.draw()
def on_key_press(self, symbol, modifiers):
if symbol == key.D:
self.right = True
if symbol == key.A:
self.left = True
if symbol == key.W:
self.up = True
if symbol == key.S:
self.down = True
if symbol == key.ESCAPE:
pyglet.app.exit()
def on_key_release(self, symbol, modifiers):
if symbol == key.D:
self.right = False
if symbol == key.A:
self.left = False
if symbol == key.W:
self.up = False
if symbol == key.S:
self.down = False
def update_space(self, dt):
for space in self.map_1_list:
space.update()
space.y -= 0 * dt
if self.right:
space.x -= self.player_speed * dt
if self.left:
space.x += self.player_speed * dt
if self.up:
space.y -= self.player_speed * dt
if self.down:
space.y += self.player_speed * dt
def update_wall(self, dt):
for wall in self.wall_list:
wall.update()
if self.right:
wall.x -= self.player_speed * dt
if self.left:
wall.x += self.player_speed * dt
if self.up:
wall.y -= self.player_speed * dt
if self.down:
wall.y += self.player_speed * dt
def update(self, dt):
self.update_wall(dt)
self.update_space(dt)
if __name__ == "__main__":
window = GameWindow(window_X, window_Y, "Gra", resizable=False)
pyglet.clock.schedule_interval(window.update, window.frame_rate)
pyglet.app.run()
我想要一个至少有 100 x 100 瓦片且可以 60 fps 运行的瓦片地图。
平铺 = 50x50 像素
如果可以绘制图块,但只能绘制屏幕上可见的图块,则不超过屏幕 X、Y。
好的,所以这里的主要问题可能是 schedule_interval
没有尽可能快地运行。主要是因为它更新场景 和 导致渲染触发。第二个是 wall.update()
调用 (我不确切知道你为什么调用) 非常慢,你正在做 width*height^2 次。而且非常效率低下。
有一个快速解决方法。下面是建议的方法。
#Create blocks 50x50 pixels and replace with image names to get what i see.
from pyglet.window import key, FPSDisplay
import pyglet
import math
import time
Background = pyglet.graphics.OrderedGroup(0)
Walls_Group = pyglet.graphics.OrderedGroup(1)
def preload_image(image):
img = pyglet.image.load('images/' + image)
return img
map_x = 50
map_y = 50
window_X = 1500
window_Y = 900
class GameWindow(pyglet.window.Window):
def __init__(self, *args, **kwargs):
super(GameWindow, self).__init__(*args, **kwargs)
self.set_location(100, 30)
self.frame_rate = 1.0 / 100.0
self.fps_display = FPSDisplay(self)
self.fps_display.label.font_size = 30
self.player_speed = 550
self.right = False
self.left = False
self.up = False
self.down = False
self.map_load = False
self.Walls_load = False
self.map_scale = 1
self.wall = preload_image('block.png')
self.wall_list = []
self.map_1_list = []
self.sprite = preload_image('Grass_Green.jpg')
self.main_batch = pyglet.graphics.Batch()
self.alive = True
self.last_scheduled_update = time.time()
if not self.map_load:
self.Mapka(map_x, map_y)
self.map_load = True
if not self.Walls_load:
self.Walls()
self.Walls_load = True
def Mapka(self, x_size, y_size):
for Y in range(y_size):
for X in range(x_size):
self.map_1_list.append(pyglet.sprite.Sprite(self.sprite, x=X * (self.sprite.width*self.map_scale), y=Y * (self.sprite.height*self.map_scale), batch=self.main_batch, group=Background))
for i in self.map_1_list:
i.scale = self.map_scale
def Walls(self):
self.times = math.ceil(map_x * (self.sprite.width*self.map_scale) / self.wall.width) # Oblicza ilość ścian na dolnej części
# mapy z zaokrągleniem
self.times_y = math.ceil(map_y * (self.sprite.height*self.map_scale) / self.wall.height)
for x in range(int(self.times)):
self.wall_list.append(
pyglet.sprite.Sprite(self.wall, x=x * self.wall.width, y=0, batch=self.main_batch, group=Walls_Group))
self.wall_list.append(
pyglet.sprite.Sprite(self.wall, x=x * self.wall.width, y=(self.times_y - 1) * self.wall.height,
batch=self.main_batch, group=Walls_Group))
for y in range(int(self.times_y)):
self.wall_list.append(
pyglet.sprite.Sprite(self.wall, x=0, y=y * self.wall.height, batch=self.main_batch, group=Walls_Group))
self.wall_list.append(pyglet.sprite.Sprite(self.wall, x=self.times * self.wall.width - self.wall.width,
y=y * self.wall.height, batch=self.main_batch,
group=Walls_Group))
def render(self):
self.clear()
self.main_batch.draw()
self.fps_display.draw()
self.flip()
def on_key_press(self, symbol, modifiers):
if symbol == key.D:
self.right = True
if symbol == key.A:
self.left = True
if symbol == key.W:
self.up = True
if symbol == key.S:
self.down = True
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
self.update(1)
def on_key_release(self, symbol, modifiers):
if symbol == key.D:
self.right = False
if symbol == key.A:
self.left = False
if symbol == key.W:
self.up = False
if symbol == key.S:
self.down = False
def update_space(self, dt):
for space in self.map_1_list:
space.update()
space.y -= 0 * dt
if self.right:
space.x -= self.player_speed * dt
if self.left:
space.x += self.player_speed * dt
if self.up:
space.y -= self.player_speed * dt
if self.down:
space.y += self.player_speed * dt
def update_wall(self, dt):
for wall in self.wall_list:
wall.update()
if self.right:
wall.x -= self.player_speed * dt
if self.left:
wall.x += self.player_speed * dt
if self.up:
wall.y -= self.player_speed * dt
if self.down:
wall.y += self.player_speed * dt
def update(self, dt):
self.update_wall(dt)
self.update_space(dt)
def run(self):
while self.alive == 1:
if time.time() - self.last_scheduled_update > 0.25:
self.update(time.time() - self.last_scheduled_update)
self.last_scheduled_update = time.time()
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
if __name__ == "__main__":
window = GameWindow(window_X, window_Y, "Gra", resizable=False)
window.run()
这是对代码的最小更改,但您使用自己的事件循环更改内置调度程序,您负责 运行 宁 self.update()
的时间和标准。在这种情况下,它是 运行 每 0.25
秒,并且在每个 on_keypress
被触发之后。
除非您四处走动,否则没有理由更新精灵位置。或者将来,当 enemies/objects 执行操作时。
我使用此代码获得稳定的 144 FPS。
但是这里还有很多事情要做……所以我会留下一个 long lasting tip 来帮助您解决未来的瓶颈问题。我将向您演示如何使用它。
运行 上面的命令,会给你这样的东西:
这清楚地表明了主要的周期霸王为 _wait_vsync
。
这让我意识到我忘了检查这里最明显的问题。那就是你和我忘记了 vsync=False
到 window 对象。
这里的解决方法是:
window = GameWindow(window_X, window_Y, "Gra", resizable=False, vsync=False)
这给了我:
是的,高于 2700 FPS。
而且调用堆栈看起来更均匀:
从现在开始,真正的挑战才刚刚开始。
所做的任何优化都将是次要的,很难找到并且调试起来很困难。
希望您有工具并了解在哪里以及为什么要看地方:)
我的英语很烂,我尽力写这个问题:D
我正在用 Pyglet 制作 2D 游戏,我需要制作一个平铺地图。 1 个图块 = 50 像素 x 50 像素 但是,当我绘制带有 20 个敌人的 50x50 地图时,我的 fps 从 60fps 下降到 10fps,这会占用大量 PC 资源 每个瓷砖和敌人都是批量绘制的。 我可以做些什么来提高我的游戏效率?
我尝试缩放图块,但每个图块上都有黑色边框,我想要 50x50 像素的图块,而不是 50x50/scale
#Create blocks 50x50 pixels and replace with image names to get what i see.
from pyglet.window import key, FPSDisplay
import pyglet
import math
Background = pyglet.graphics.OrderedGroup(0)
Walls_Group = pyglet.graphics.OrderedGroup(1)
def preload_image(image):
img = pyglet.image.load('images/' + image)
return img
map_x = 50
map_y = 50
window_X = 1500
window_Y = 900
class GameWindow(pyglet.window.Window):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_location(100, 30)
self.frame_rate = 1.0 / 100.0
self.fps_display = FPSDisplay(self)
self.fps_display.label.font_size = 30
self.player_speed = 550
self.right = False
self.left = False
self.up = False
self.down = False
self.map_load = False
self.Walls_load = False
self.map_scale = 1
self.wall = preload_image('block.png')
self.wall_list = []
self.map_1_list = []
self.sprite = preload_image('Grass_Green.png')
self.main_batch = pyglet.graphics.Batch()
def Mapka(self, x_size, y_size):
for Y in range(y_size):
for X in range(x_size):
self.map_1_list.append(pyglet.sprite.Sprite(self.sprite, x=X * (self.sprite.width*self.map_scale), y=Y * (self.sprite.height*self.map_scale), batch=self.main_batch, group=Background))
for i in self.map_1_list:
i.scale = self.map_scale
def Walls(self):
self.times = math.ceil(map_x * (self.sprite.width*self.map_scale) / self.wall.width) # Oblicza ilość ścian na dolnej części
# mapy z zaokrągleniem
self.times_y = math.ceil(map_y * (self.sprite.height*self.map_scale) / self.wall.height)
for x in range(int(self.times)):
self.wall_list.append(
pyglet.sprite.Sprite(self.wall, x=x * self.wall.width, y=0, batch=self.main_batch, group=Walls_Group))
self.wall_list.append(
pyglet.sprite.Sprite(self.wall, x=x * self.wall.width, y=(self.times_y - 1) * self.wall.height,
batch=self.main_batch, group=Walls_Group))
for y in range(int(self.times_y)):
self.wall_list.append(
pyglet.sprite.Sprite(self.wall, x=0, y=y * self.wall.height, batch=self.main_batch, group=Walls_Group))
self.wall_list.append(pyglet.sprite.Sprite(self.wall, x=self.times * self.wall.width - self.wall.width,
y=y * self.wall.height, batch=self.main_batch,
group=Walls_Group))
def on_draw(self):
self.clear()
if not self.map_load:
self.Mapka(map_x, map_y)
self.map_load = True
if not self.Walls_load:
self.Walls()
self.Walls_load = True
self.main_batch.draw()
self.fps_display.draw()
def on_key_press(self, symbol, modifiers):
if symbol == key.D:
self.right = True
if symbol == key.A:
self.left = True
if symbol == key.W:
self.up = True
if symbol == key.S:
self.down = True
if symbol == key.ESCAPE:
pyglet.app.exit()
def on_key_release(self, symbol, modifiers):
if symbol == key.D:
self.right = False
if symbol == key.A:
self.left = False
if symbol == key.W:
self.up = False
if symbol == key.S:
self.down = False
def update_space(self, dt):
for space in self.map_1_list:
space.update()
space.y -= 0 * dt
if self.right:
space.x -= self.player_speed * dt
if self.left:
space.x += self.player_speed * dt
if self.up:
space.y -= self.player_speed * dt
if self.down:
space.y += self.player_speed * dt
def update_wall(self, dt):
for wall in self.wall_list:
wall.update()
if self.right:
wall.x -= self.player_speed * dt
if self.left:
wall.x += self.player_speed * dt
if self.up:
wall.y -= self.player_speed * dt
if self.down:
wall.y += self.player_speed * dt
def update(self, dt):
self.update_wall(dt)
self.update_space(dt)
if __name__ == "__main__":
window = GameWindow(window_X, window_Y, "Gra", resizable=False)
pyglet.clock.schedule_interval(window.update, window.frame_rate)
pyglet.app.run()
我想要一个至少有 100 x 100 瓦片且可以 60 fps 运行的瓦片地图。 平铺 = 50x50 像素 如果可以绘制图块,但只能绘制屏幕上可见的图块,则不超过屏幕 X、Y。
好的,所以这里的主要问题可能是 schedule_interval
没有尽可能快地运行。主要是因为它更新场景 和 导致渲染触发。第二个是 wall.update()
调用 (我不确切知道你为什么调用) 非常慢,你正在做 width*height^2 次。而且非常效率低下。
有一个快速解决方法。下面是建议的方法。
#Create blocks 50x50 pixels and replace with image names to get what i see.
from pyglet.window import key, FPSDisplay
import pyglet
import math
import time
Background = pyglet.graphics.OrderedGroup(0)
Walls_Group = pyglet.graphics.OrderedGroup(1)
def preload_image(image):
img = pyglet.image.load('images/' + image)
return img
map_x = 50
map_y = 50
window_X = 1500
window_Y = 900
class GameWindow(pyglet.window.Window):
def __init__(self, *args, **kwargs):
super(GameWindow, self).__init__(*args, **kwargs)
self.set_location(100, 30)
self.frame_rate = 1.0 / 100.0
self.fps_display = FPSDisplay(self)
self.fps_display.label.font_size = 30
self.player_speed = 550
self.right = False
self.left = False
self.up = False
self.down = False
self.map_load = False
self.Walls_load = False
self.map_scale = 1
self.wall = preload_image('block.png')
self.wall_list = []
self.map_1_list = []
self.sprite = preload_image('Grass_Green.jpg')
self.main_batch = pyglet.graphics.Batch()
self.alive = True
self.last_scheduled_update = time.time()
if not self.map_load:
self.Mapka(map_x, map_y)
self.map_load = True
if not self.Walls_load:
self.Walls()
self.Walls_load = True
def Mapka(self, x_size, y_size):
for Y in range(y_size):
for X in range(x_size):
self.map_1_list.append(pyglet.sprite.Sprite(self.sprite, x=X * (self.sprite.width*self.map_scale), y=Y * (self.sprite.height*self.map_scale), batch=self.main_batch, group=Background))
for i in self.map_1_list:
i.scale = self.map_scale
def Walls(self):
self.times = math.ceil(map_x * (self.sprite.width*self.map_scale) / self.wall.width) # Oblicza ilość ścian na dolnej części
# mapy z zaokrągleniem
self.times_y = math.ceil(map_y * (self.sprite.height*self.map_scale) / self.wall.height)
for x in range(int(self.times)):
self.wall_list.append(
pyglet.sprite.Sprite(self.wall, x=x * self.wall.width, y=0, batch=self.main_batch, group=Walls_Group))
self.wall_list.append(
pyglet.sprite.Sprite(self.wall, x=x * self.wall.width, y=(self.times_y - 1) * self.wall.height,
batch=self.main_batch, group=Walls_Group))
for y in range(int(self.times_y)):
self.wall_list.append(
pyglet.sprite.Sprite(self.wall, x=0, y=y * self.wall.height, batch=self.main_batch, group=Walls_Group))
self.wall_list.append(pyglet.sprite.Sprite(self.wall, x=self.times * self.wall.width - self.wall.width,
y=y * self.wall.height, batch=self.main_batch,
group=Walls_Group))
def render(self):
self.clear()
self.main_batch.draw()
self.fps_display.draw()
self.flip()
def on_key_press(self, symbol, modifiers):
if symbol == key.D:
self.right = True
if symbol == key.A:
self.left = True
if symbol == key.W:
self.up = True
if symbol == key.S:
self.down = True
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
self.update(1)
def on_key_release(self, symbol, modifiers):
if symbol == key.D:
self.right = False
if symbol == key.A:
self.left = False
if symbol == key.W:
self.up = False
if symbol == key.S:
self.down = False
def update_space(self, dt):
for space in self.map_1_list:
space.update()
space.y -= 0 * dt
if self.right:
space.x -= self.player_speed * dt
if self.left:
space.x += self.player_speed * dt
if self.up:
space.y -= self.player_speed * dt
if self.down:
space.y += self.player_speed * dt
def update_wall(self, dt):
for wall in self.wall_list:
wall.update()
if self.right:
wall.x -= self.player_speed * dt
if self.left:
wall.x += self.player_speed * dt
if self.up:
wall.y -= self.player_speed * dt
if self.down:
wall.y += self.player_speed * dt
def update(self, dt):
self.update_wall(dt)
self.update_space(dt)
def run(self):
while self.alive == 1:
if time.time() - self.last_scheduled_update > 0.25:
self.update(time.time() - self.last_scheduled_update)
self.last_scheduled_update = time.time()
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
if __name__ == "__main__":
window = GameWindow(window_X, window_Y, "Gra", resizable=False)
window.run()
这是对代码的最小更改,但您使用自己的事件循环更改内置调度程序,您负责 运行 宁 self.update()
的时间和标准。在这种情况下,它是 运行 每 0.25
秒,并且在每个 on_keypress
被触发之后。
除非您四处走动,否则没有理由更新精灵位置。或者将来,当 enemies/objects 执行操作时。
我使用此代码获得稳定的 144 FPS。
但是这里还有很多事情要做……所以我会留下一个 long lasting tip 来帮助您解决未来的瓶颈问题。我将向您演示如何使用它。
运行 上面的命令,会给你这样的东西:
这清楚地表明了主要的周期霸王为 _wait_vsync
。
这让我意识到我忘了检查这里最明显的问题。那就是你和我忘记了 vsync=False
到 window 对象。
这里的解决方法是:
window = GameWindow(window_X, window_Y, "Gra", resizable=False, vsync=False)
这给了我:
是的,高于 2700 FPS。
而且调用堆栈看起来更均匀:
从现在开始,真正的挑战才刚刚开始。
所做的任何优化都将是次要的,很难找到并且调试起来很困难。
希望您有工具并了解在哪里以及为什么要看地方:)