我知道这个问题没有说太多,所以我只是尝试解释我的问题。
我正在 Pyglet 中开发一个项目,这是一个 2D 自上而下的游戏,标签代表网格上的图块(标签的原因是模拟 ASCII 图形,并且比手工制作艺术更容易)。我已经设置了所有批次和组,甚至还有相机,我正在寻找的是对我的一些代码的优化。
我认为,与其在屏幕外放置大量代表整个地图的精灵,不如重复使用预先存在的精灵来使游戏运行得更快,为此,我将图块分成了组,这些组将成为最初持有他们的主要团体。
基本上,我有一个
MapGroup
实例(父组),其中包含 36 个 MapGrid
实例(子组),每个 MapGrid
包含 1024 个 RenderTile
实例(图块)。
我最初所做的是,每当需要新的
MapGrid
时,假设相机向右移动到一些空白区域,MapGroup
就会查看相反方向最远的每个网格并设置这些 MapGrid
的内部坐标值覆盖了该空白区域,使其看起来像是出现了一个新网格,但它只是一个在屏幕外重复使用的网格。
主要问题是实际的
RenderTile
,这是我主要尝试解决的问题,到目前为止,移动它们的最佳方法是迭代移动的每个 RenderTile
中的每个 MapGrid
并以这种方式设置位置。它比渲染一个全新的MapGrid
要好,我什至安排每个动作与上次相比有 1 毫秒的延迟,使其根本不会滞后太多,但它仍然不是很好,我觉得应该有更好的。
我的主要优化尝试是尝试将
MapGroup
具有的相同翻译应用到每个 MapGrid
上,但由于某种原因,set_state
似乎只在第一个 MapGrid
上运行,所以从那里我尝试使 MapGrid
set_state
迭代每个 MapGrid
并使用迭代中的值,但这也不起作用。我什至尝试使 MapGroup
不是一个 pyglet 组,而只是管理组的东西,但这也存在 MapGrid
仍然只运行第一个实例的问题。如果有一种方法可以将这些 set_state
调用分开,以便它确实为每个单独的 MapGrid
进行设置和取消设置,那就太好了,否则我不知道该去哪里。
这是我正在使用的代码,其中包含关于什么作用的额外注释:
from pyglet.text import Label
from pyglet.graphics import Group
from pyglet.clock import schedule_once
from pyglet.math import clamp,Vec2,Vec3
from pyglet.gl import glEnable,glDisable,glScissor
from random import randint
class RenderTile(Label): # Main Tile
def __init__(self,internalCoords,*args,**kwargs):
super().__init__(*args,**kwargs)
self.internalCoords = internalCoords
self.x = internalCoords[0] * 16 + self.group.gridCoord[0] * 512
self.y = internalCoords[1] * 16 + self.group.gridCoord[1] * 512
#Initial values, fine on its own, but idealy the self.group.gridCoord[0] * 512 can be removed as set_state would handle the relative position
def rePos(self,dt,newPos): #Simply called by MapGrid's update
self.position = newPos
class MapGrid(Group): # Child Group
def __init__(self,gridCoord,win,batchPass,order=0,parent=None):
super().__init__(order,parent)
self.gridCoord = gridCoord
self._window = win
# pyglet window, passed from parent
self.internalTiles = {}
# Dict that holds each Tile, their keys are their coordinates in the grid
gridTestColor = (randint(0,255),randint(0,255),randint(0,255),255)
# Random color for whole grid, mostly for distinction
internalXSet = 0
while internalXSet < 32:
internalYSet = 0
while internalYSet < 1:
self.internalTiles[(internalXSet,internalYSet)] = RenderTile(internalCoords=(internalXSet,internalYSet),text="##",font_name="dayton",font_size=8,color=gridTestColor,batch=batchPass,group=self)
internalYSet += 1
internalXSet += 1
# While loops that add each RenderTile to the grid, currently makes the grid 32x1 for testing purposes, it will be 32x32 by the end
self.testInternalGrid = RenderTile(internalCoords=(16,16),text=str(self.gridCoord),font_name="dayton",font_size=24,color=(255,255,255,255),batch=batchPass,group=self,anchor_x="center",anchor_y="center")
# Debug item, simply shows the grids position at its center
def update(self):
eventIterations = 0
for tile in self.internalTiles:
schedule_once(self.internalTiles[tile].rePos,eventIterations,(tile[0] * 16 + self.gridCoord[0] * 512,tile[1] * 16 + self.gridCoord[1] * 512,0))
eventIterations += 0.001
self.testInternalGrid.position = (256 + self.gridCoord[0] * 512,256 + self.gridCoord[1] * 512,0)
self.testInternalGrid.text = str(self.gridCoord)
# Updates the grids position, this is the unideal solution that I want to replace
# def set_state(self):
# for grid in self.parent.internalGrids:
# self.parent.internalGrids[grid]._window.view = self.parent.internalGrids[grid]._window.view.translate(Vec3(-grid[0]*10, -grid[1]*10, 0))
# print(grid)
# def unset_state(self):
# for grid in self.parent.internalGrids:
# self.parent.internalGrids[grid]._window.view = self.parent.internalGrids[grid]._window.view.translate(Vec3(grid[0]*10, grid[1]*10, 0))
# This set_state was what I was messing with, if there is some way to make this work as intended that would be absolutley amazing
class MapGroup(Group):
def __init__(self,win,map,batchPass,x=-1040,y=-540,z=1.0,order=0,parent=None):
super().__init__(order,parent)
self._window = win
# pyglet window
self.mapData = map
# currently unused, will hold the data for the map, like what a tile should look like and what not
self.x = x
self.y = y
self._z = z
# Probaly should have just called this zoom instead of z, but it doesn't break anything yet, so its fine
self.internalGrids = {}
# Dict that holds each Grid, their keys are their coordinates in the map
self.memoryPos = 0
internalXSet = -3
while internalXSet < 3:
internalYSet = -3
while internalYSet < 3:
self.internalGrids[(internalXSet,internalYSet)] = MapGrid((internalXSet,internalYSet),win,batchPass,parent=self)
internalYSet += 1
internalXSet += 1
# While loops that add each MapGrid to the MapGroup
@property
def position(self):
return Vec2(self.x,self.y)
@position.setter
def position(self,new_pos):
self.x,self.y = new_pos
self.update()
@property
def z(self):
return self._z
@z.setter
def z(self,new_z):
self._z = clamp(round(new_z,1),0.5,2)
def update(self):
# All the following is frankly a pain to explain, but this is the code that looks for the furthest opposite grid and tells it to move
XGridOffset = (self.position[0]+1040)/128-self.memoryPos*2
XGridCheck = abs(XGridOffset)
self.memoryPos += int(XGridOffset)
while 1 <= XGridCheck:
xCheckList = [grid[0] for grid in self.internalGrids]
xMin = min(xCheckList)
xMax = max(xCheckList)
if 0 < XGridOffset:
xChangeList = [grid for grid in self.internalGrids if self.internalGrids[grid].gridCoord[0] == xMin]
for grid in xChangeList:
shiftingGrid = self.internalGrids.pop(grid,None)
newCoord = (xMax+1,shiftingGrid.gridCoord[1])
shiftingGrid.gridCoord = newCoord
self.internalGrids[newCoord] = shiftingGrid
shiftingGrid.update()
elif 0 > XGridOffset:
xChangeList = [grid for grid in self.internalGrids if self.internalGrids[grid].gridCoord[0] == xMax]
for grid in xChangeList:
shiftingGrid = self.internalGrids.pop(grid,None)
newCoord = (xMin-1,shiftingGrid.gridCoord[1])
shiftingGrid.gridCoord = newCoord
self.internalGrids[newCoord] = shiftingGrid
shiftingGrid.update()
else:
xChangeList = [grid for grid in self.internalGrids if self.internalGrids[grid].gridCoord[0] == xMin]
for grid in xChangeList:
shiftingGrid = self.internalGrids.pop(grid,None)
newCoord = (xMax+1,shiftingGrid.gridCoord[1])
shiftingGrid.gridCoord = newCoord
self.internalGrids[newCoord] = shiftingGrid
shiftingGrid.update()
XGridCheck -= 1
# Main set_state
def set_state(self):
glScissor(400, 0, 1280, 1080)
# Basically the space the map takes up, assuming a 1920x1080 display
glEnable(3089) #GL_SCISSOR_TEST = 3089
# Didn't feel like importing from pyglet.gl.gl, so I just put its number in instead
map_matrix = self._window.view.translate(Vec3(-self.x, -self.y, 0))
map_matrix = map_matrix.scale(Vec3(self.z, self.z, 1))
self._window.view = map_matrix
def unset_state(self):
glDisable(3089) #GL_SCISSOR_TEST = 3089
map_matrix = self._window.view.scale(Vec3(1/self.z, 1/self.z, 1))
map_matrix = map_matrix.translate(Vec3(self.x, self.y, 0))
self._window.view = map_matrix
除了渲染之外,以下是使代码正常运行所需的最低要求:
import pyglet
from render import MapGroup,MapGrid,RenderTile
# render.py is the name of the file holding the other code
config = Config(double_buffer=True)
display = pyglet.canvas.get_display()
screens = display.get_screens()
window = pyglet.window.Window(1920,1080,fullscreen=True,vsync=True,screen=screens[2])
# Change screen as needed, its just what I have it set to
main_batch = pyglet.graphics.Batch()
map_group = MapGroup(window,{},main_batch)
# Main batch and group
fps_display = pyglet.window.FPSDisplay(window=window,color=(0,255,0,255))
#gui_group = pyglet.graphics.Group(order=2)
#holdThingTest = pyglet.shapes.Rectangle(0,0,400,1080,color=(75,75,75,255),batch=main_batch,group=gui_group)
#holdThingTest2 = pyglet.shapes.Rectangle(1680,0,240,1080,color=(75,75,75,255),batch=main_batch,group=gui_group)
#Bad naming I know, but its just simple boxes to help show where the map should be, uncomment if wanted
@window.event
def on_mouse_drag(x,y,dx,dy,buttons,modifiers):
if buttons & pyglet.window.mouse.MIDDLE:
map_group.position = (map_group.x - dx,map_group.y - dy)
# Event for camera movement, just hold and drag middle mouse
@window.event
def on_mouse_scroll(x,y,scroll_x,scroll_y):
map_group.z += scroll_y/10
# Event for zooming, just scroll your mouse wheel
@window.event
def on_draw():
window.clear()
main_batch.draw()
fps_display.draw()
# Event for drawing, kind of a well duh
if __name__ == "__main__":
pyglet.app.run(1/60)
# Thing that runs the code, not much else to say
我知道我应该只显示必要的内容,爬过别人的 170 行奇怪的代码会有点痛苦,但据我所知,任何事情都可能是问题,所以我宁愿它可用,以防万一只是忽略了修复所有问题的一行更改。
好吧,经过一番折腾后,我发现只需将每个
MapGrid
设置为唯一的顺序即可修复它,所以固定的更改就是这样:
orderSet = 0
internalXSet = -3
while internalXSet < 3:
internalYSet = -3
while internalYSet < 3:
self.internalGrids[(internalXSet,internalYSet)] = MapGrid((internalXSet,internalYSet),win,batchPass,order=orderSet,parent=self)
orderSet += 1
internalYSet += 1
internalXSet += 1
我唯一要注意的是,每个
MapGrid
的顺序需要是唯一的,将它们全部设置为1或2,它们似乎会再次导致相同的错误。