Libgdx 舞台添加演员低 FPS
Libgdx stage add actor low FPS
我正在使用 ApplicationAdapter。当我在 create() 方法中有下面的代码时,我的 fps 总是大约 14.
for (i in 0..1000) {
stage.addActor(Food(
Food.TEXTURE_PURPLE!!,
100f,
(random.nextInt(MAP_SIZE)).toFloat(),
(random.nextInt(MAP_SIZE)).toFloat()))
stage.addActor(Food(
Food.TEXTURE_GREEN!!,
100f,
(random.nextInt(MAP_SIZE)).toFloat(),
(random.nextInt(MAP_SIZE)).toFloat()))
}
但是当我在 create() 方法中有这个时,我的 fps 总是 60。为什么?
for (i in 0..1000) {
stage.addActor(Food(
Food.TEXTURE_PURPLE!!,
100f,
(random.nextInt(MAP_SIZE)).toFloat(),
(random.nextInt(MAP_SIZE)).toFloat()))
}
for (i in 0..1000) {
stage.addActor(Food(
Food.TEXTURE_GREEN!!,
100f,
(random.nextInt(MAP_SIZE)).toFloat(),
(random.nextInt(MAP_SIZE)).toFloat()))
}
问题是纹理交换。 SpriteBatch 的工作方式是您使用其 draw
函数之一提交要绘制到其队列中的项目。一旦您添加了无法与队列中的其他所有内容一起绘制的内容,它就会通过调用 OpenGL 绘制来刷新队列,然后启动一个新队列。
无法在同一个 OpenGL 绘制调用中绘制两个不同的纹理,因此当您的食物项未按纹理排序时,它们中的每一个都会触发另一次刷新。刷新是一项昂贵的操作,您希望避免超过几十次。
这个问题的解决方案是使用 TextureAtlas,它将多个图像组合成一个纹理,通过 TextureRegion 访问。您可以将 TextureRegions 提交到 SpriteBatch.draw(...)
。只要 TextureRegions 共享相同的 Texture,就可以在单个批处理队列中绘制具有不同 TextureRegions 的许多项目。
附带说明...我强烈建议不要对纹理使用静态引用。这里最常被问到的 libgdx 问题是 "why are my textures black",这总是因为他们使用的是静态纹理引用,而没有确切了解如何正确处理纹理和重新加载。静态引用在 Android 时比你的游戏寿命更长,这可能会导致大量内存泄漏,如果你延迟加载任何东西,就会出现奇怪的错误,这在使用静态引用时很容易意外发生。
更清晰的设计是拥有一个资产 class(如果您的游戏中每个阶段都有很多独特的东西,则可能每个阶段一个)有一个 AssetManager 用于加载您的所有东西。可以将单个资产实例传递给所有游戏对象构造函数。例如:
class Assets: Disposable {
private val assetManager = AssetManager()
lateinit val foodGreenTexture: Texture
lateinit val foodRedTexture: Texture
lateinit val foodBlueTexture: Texture
//...
init {
assetManager.load("green.png", Texture::class.java)
assetManager.load("red.png", Texture::class.java)
//...
assetManager.finishLoading()
foodGreenTexture = assetManager.get("green.png")
//...
}
override fun dispose (){
assetManager.dispose()
}
}
不要忘记在初始化它的同一个对象(您的游戏或屏幕)中对您的资产 class 调用处置。
我还建议研究使用 CoveTools 库,它有一个名为 AssignmentAssetManager 的帮助程序,可以删除上述一堆样板文件。同样的 class 看起来像这样。请注意 init
块将如何超短,无论您加载多少不同的资产:
class Assets: Disposable {
private val assetManager = AssignmentAssetManager()
@Asset("green.png") val foodGreenTexture: Texture = null
@Asset("red.png") val foodRedTexture: Texture = null
@Asset("blue.png") val foodBlueTexture: Texture = null
//...
init {
assetManager.loadAssetFields(this)
assetManager.finishLoading()
}
override fun dispose (){
assetManager.dispose()
}
}
第二个旁注...我强烈建议不要使用 Sprite class,尤其是在使用 Actors 时。请改用 TextureRegion class。 Sprite 是一个高度专业化的 class,它扩展了 TextureRegion 但包含额外的数据,如位置、大小、旋转等。如果您使用 Actor,这些额外的数据是多余的,所以它很容易出错并且浪费 CPU 和内存必须来回复制数据。
我正在使用 ApplicationAdapter。当我在 create() 方法中有下面的代码时,我的 fps 总是大约 14.
for (i in 0..1000) {
stage.addActor(Food(
Food.TEXTURE_PURPLE!!,
100f,
(random.nextInt(MAP_SIZE)).toFloat(),
(random.nextInt(MAP_SIZE)).toFloat()))
stage.addActor(Food(
Food.TEXTURE_GREEN!!,
100f,
(random.nextInt(MAP_SIZE)).toFloat(),
(random.nextInt(MAP_SIZE)).toFloat()))
}
但是当我在 create() 方法中有这个时,我的 fps 总是 60。为什么?
for (i in 0..1000) {
stage.addActor(Food(
Food.TEXTURE_PURPLE!!,
100f,
(random.nextInt(MAP_SIZE)).toFloat(),
(random.nextInt(MAP_SIZE)).toFloat()))
}
for (i in 0..1000) {
stage.addActor(Food(
Food.TEXTURE_GREEN!!,
100f,
(random.nextInt(MAP_SIZE)).toFloat(),
(random.nextInt(MAP_SIZE)).toFloat()))
}
问题是纹理交换。 SpriteBatch 的工作方式是您使用其 draw
函数之一提交要绘制到其队列中的项目。一旦您添加了无法与队列中的其他所有内容一起绘制的内容,它就会通过调用 OpenGL 绘制来刷新队列,然后启动一个新队列。
无法在同一个 OpenGL 绘制调用中绘制两个不同的纹理,因此当您的食物项未按纹理排序时,它们中的每一个都会触发另一次刷新。刷新是一项昂贵的操作,您希望避免超过几十次。
这个问题的解决方案是使用 TextureAtlas,它将多个图像组合成一个纹理,通过 TextureRegion 访问。您可以将 TextureRegions 提交到 SpriteBatch.draw(...)
。只要 TextureRegions 共享相同的 Texture,就可以在单个批处理队列中绘制具有不同 TextureRegions 的许多项目。
附带说明...我强烈建议不要对纹理使用静态引用。这里最常被问到的 libgdx 问题是 "why are my textures black",这总是因为他们使用的是静态纹理引用,而没有确切了解如何正确处理纹理和重新加载。静态引用在 Android 时比你的游戏寿命更长,这可能会导致大量内存泄漏,如果你延迟加载任何东西,就会出现奇怪的错误,这在使用静态引用时很容易意外发生。
更清晰的设计是拥有一个资产 class(如果您的游戏中每个阶段都有很多独特的东西,则可能每个阶段一个)有一个 AssetManager 用于加载您的所有东西。可以将单个资产实例传递给所有游戏对象构造函数。例如:
class Assets: Disposable {
private val assetManager = AssetManager()
lateinit val foodGreenTexture: Texture
lateinit val foodRedTexture: Texture
lateinit val foodBlueTexture: Texture
//...
init {
assetManager.load("green.png", Texture::class.java)
assetManager.load("red.png", Texture::class.java)
//...
assetManager.finishLoading()
foodGreenTexture = assetManager.get("green.png")
//...
}
override fun dispose (){
assetManager.dispose()
}
}
不要忘记在初始化它的同一个对象(您的游戏或屏幕)中对您的资产 class 调用处置。
我还建议研究使用 CoveTools 库,它有一个名为 AssignmentAssetManager 的帮助程序,可以删除上述一堆样板文件。同样的 class 看起来像这样。请注意 init
块将如何超短,无论您加载多少不同的资产:
class Assets: Disposable {
private val assetManager = AssignmentAssetManager()
@Asset("green.png") val foodGreenTexture: Texture = null
@Asset("red.png") val foodRedTexture: Texture = null
@Asset("blue.png") val foodBlueTexture: Texture = null
//...
init {
assetManager.loadAssetFields(this)
assetManager.finishLoading()
}
override fun dispose (){
assetManager.dispose()
}
}
第二个旁注...我强烈建议不要使用 Sprite class,尤其是在使用 Actors 时。请改用 TextureRegion class。 Sprite 是一个高度专业化的 class,它扩展了 TextureRegion 但包含额外的数据,如位置、大小、旋转等。如果您使用 Actor,这些额外的数据是多余的,所以它很容易出错并且浪费 CPU 和内存必须来回复制数据。