纹理地图集#
引言#
arcade.TextureAtlas
是纹理在精灵中使用时最终结束的位置。这是将图像数据移动到图形内存(OpenGL)的地方,也是我们能够以极快的速度批量绘制数十万精灵的原因之一。
纹理图集基本上是一个包含多个纹理的大纹理,我们跟踪这些纹理的位置。Arcade的纹理图集驻留在图形内存中,并且是动态的,这意味着可以动态添加和删除纹理。
Arcade的纹理图集还会在需要时自动调整大小,直到您的硬件支持的最大纹理大小。这需要完全重建地图集,这是我们在GPU本身上所做的,以将此操作的影响降至最低。对于一般的硬件,您在运行时不会注意到这一点。
同样重要的是要注意,纹理地图集只能在窗口被装箱后才能创建。纹理和精灵可以在窗口之前创建,因为它们不直接与OpenGL交互。这部分通常是最耗时的,而地图集的创建和构建非常快。
大小限制#
目前,我们使用一个非常简单的基于行的分配算法来随着时间的推移为新的纹理腾出空间。这意味着非常高的纹理最终可能会占用大量的垂直空间。
如果我们的目标是普通硬件,那么地图集的最大尺寸通常是16384 x 16384。
调整尺寸#
地图集已满时将自动调整大小。它还将尝试通过对纹理的高度进行排序来更好地包装它们。
默认纹理地图集#
大多数用户不会意识到Arcade正在使用引擎盖下的纹理地图集。如果遇到限制,更高级的用户可以利用这些功能。
Arcade的全局默认纹理图集存储在 window.ctx.default_atlas
。这是一个实例 arcade.ArcadeContext
访问低级渲染API的位置(OpenGL)。
自定义图集#
我们也可以创建自己的纹理图集,而不是依赖全局纹理图集。Sprite清单需要一份 atlas
提供您自己的纹理贴图集实例的参数。如果需要,这个图集也可以在几个精灵列表之间共享。
# Create an empty 256 x 256 texture atlas
my_atlas = TextureAtlas((256, 256))
spritelist = SpriteList(atlas=my_atlas)
当检测到新纹理时(将精灵添加到列表中),该纹理将添加到贴图集。
我们还可以在游戏开始前将纹理预先添加到地图集中,以避免潜在的小停顿。这通常不是问题,但当添加大量它们时,它可能会很明显。
# List of arcade.Texture instances
list_of_textures = ...
# Create an atlas with a reasonable size for a list of textures
atlas = TextureAtlas.create_from_texture_sequence(list_of_textures)
# Create an atlas with a specific size and initial textures
atlas = TextureAtlas((256, 256), textures=list_of_textures)
# We can also pre-add textures at any time using:
# (can also be done with the default texture atlas)
atlas.add(texture)
边境线#
地图集有一个 border
属性,即 1
默认情况下。这对于避免贴图集中纹理边界之间的“纹理渗漏”非常重要。这在使用基于GPU的显卡的游戏中是一个非常常见的问题,甚至是使用 NEAREST
精灵旋转时的插补。
保留此属性的默认值,除非您确切知道自己在做什么。
更新纹理#
在某些情况下,更新纹理可能很有用。我们通常会通过修改 arcade.Texture
举个例子。但是,这不会更新贴图集本身中的纹理。我们可以手动更新它:
# Change the internal image in a texture
texture.image # <- Modify or crate a new image with the same size
# Write the new image data to the atlas
atlas.update_texture_image(texture)
这将更新已分配的区域,并且图像需要完全相同的大小。这应该谨慎使用,或者至少不是每帧操作。IF可以作为每帧操作的速度,但您需要对其进行分析。动画精灵是更好的选择,但当然需要预先确定的纹理帧。
删除纹理#
如果您有陈旧的纹理,可以使用以下命令从地图集中删除它们:
atlas.remove(texture)
这将使该区域在下一次重建地图集时可以自由使用新纹理。您也可以拨打 arcade.TextureAtlas.rebuild()
如果您要移除大量纹理,则直接执行此操作,但通常情况下,在需要时自动执行此操作就足够了。
渲染到Atlas#
在贴图集中更新纹理的一种更快的方法是直接渲染到其中。例如,这可以用来为你的游戏制作一个小地图,或者在任何情况下,你需要精灵纹理是真正动态的(不是由预先制作的纹理帧决定的)。它可以以许多创造性的方式使用。
# --- Initialization ---
# Create an empty texture so we can allocate some space in the atlas
texture = arcade.Texture.create_empty("render_area_1", size=(256, 256))
# Assign the texture to a sprite
sprite = arcade.Sprite(center_x=200, center_y=300, texture=texture)
# Create the spritelist and add the sprite
spritelist = arcade.SpriteList()
# Adding the sprite will also add the texture to the atlas
spritelist.append(sprite)
# -- Rendering ---
# Let's render something into our texture directly.
# All operations will only affect the allocated portion of the atlas for texture.
# We are given a framebuffer instance representing this area
with spritelist.atlas.render_into(texture) as framebuffer:
# Clear the allocated region in the atlas (if you need it)
framebuffer.clear()
# From here on we can draw using any arcade draw functionality
arcade.draw_rectangle_filled(128, 128, 160, 160, arcade.color.WHITE, rotation)
# Draw the spritelist and see your animating sprite texture
spritelist.draw()
在每一帧上方执行渲染部分(并递增 rotation
增量时间)会给你一个旋转的矩形和纹理的精灵。同样,您可以将任何内容绘制到此纹理区域。Spritelist,形状等等。
我们还可以在地图集中指定应该投影到这个纹理区域中的内容。默认情况下,投影将为 (0, width, 0, height)
,但这并不总是你想要的 width
和 height
是区域/纹理大小)
# Assuming your window is 800 x 600 we could draw the entire game into this atlas region
projection = 0, 800, 0, 600
with spritelist.atlas.render_into(texture, projection=projection) as framebuffer:
framebuffer.clear()
# Draw your game here
# Draw sprite with a texture containing your entire game here
滚动也可以像相机一样应用于投影。
# Scroll projection (or even zoom)
projection = 0 + scroll_x, 800 + scroll_x, 0 + scroll_y, 600 + scroll_y
渲染到地图集比使用Pillow更新纹理数据更好(至少快100倍),但这并不意味着它是免费的。我们可能会在每帧50-100个这样的情况下逃脱惩罚,但这是你必须要分析的事情。
调试#
在使用地图集时,查看内容可能会很有用。为此,我们提供了两种方法。
arcade.TextureAtlas.show()
将使用枕头显示地图集::
atlas.show()
arcade.TextureAtlas.save()
将地图集内容保存为PNG文件::
atlas.write("path/to/atlas.png")
这两种方法都会从图形内存中“下载”地图集纹理,供您检查原始数据。