SpriteKit 游戏中的高 CPU 和帧率下降

Posted

技术标签:

【中文标题】SpriteKit 游戏中的高 CPU 和帧率下降【英文标题】:High CPU and frame rate drop in SpriteKit game 【发布时间】:2016-09-02 16:55:45 【问题描述】:

我正在使用 SpriteKit 制作我的第一款游戏,其中敌人从一侧进入屏幕并从另一侧离开屏幕。我注意到在游戏后期渲染不同类型的敌人时,FPS 下降,CPU 使用率接近 100%(~95-99%)。我使用了两个仪器调试工具,结果如下:

在时间分析器工具中,我发现main 我认为是appDelegate 的主体是问题所在。我知道这可能看起来很明显,但我很惊讶地看到像update 这样的函数在GameScene 几乎不是问题。有没有办法对此进行更深入的研究?如果它是我的功能之一导致问题,我觉得好像更容易评估我搞砸的地方。

我还使用了分配工具,发现构建新的外星精灵会占用大量内存:

这是我添加普通外星人的代码:

func addNormAlien()

    let mult = normAlienMultiplers

    let alienInst = normAlien(startPos:CGPoint(x: 10,y: 10), speed: random(UInt32(10),max: UInt32(50))*mult[0])

    let yStart = random(UInt32(alienInst.size.height/2), max: UInt32(size.height-alienInst.size.height))
    alienInst.position = CGPoint(x:size.width+alienInst.size.width/2, y:CGFloat(yStart))

    addChild(alienInst)
    totalNodes+=1

这是否有点意料之中,因为继续实例化新精灵的成本很高(尽管我很少一次超过 20 个)?我这样做只是为了确保它们的随机传播。

我也找不到太多关于纹理大小的信息——有使用约定吗?目前,我发现自己制作了相当大的精灵纹理,然后使用setScale 缩小它们,而不是在图像编辑器中这样做。

这些纹理和动画也(预加载?)在我的 GameScene 文件的开头,带有类似

的行
let laserTexture = SKTextureAtlas(named:"Sprites").textureNamed("laserTexture")`

let shipFrames = ["ship0","ship1","ship2","ship3","ship4","ship5","ship6","ship7","ship8","ship9","ship9","ship9","ship8","ship7","ship6","ship5","ship4","ship3","ship2","ship1","ship0"].maptextureAtlas.textureNamed($0)`

就在之前:class GameScene: SKScene, SKPhysicsContactDelegate

因此可以从我的 Sprite 类中访问,如下所示:

    super.init(texture: shipStartTexture, color: UIColor.clearColor(), size: shipStartTexture.size())

func animateShip1() 
    let animate = SKAction.animateWithTextures(shipFrames, timePerFrame: 0.1)
    let forever = SKAction.repeatActionForever(animate)
    self.runAction(forever)

如果有更好的预分配纹理的方法,我很想知道,并且任何关于如何评估其他一些问题的进一步见解都会很棒!

【问题讨论】:

【参考方案1】:

通常,您会发现 main 包含最高的 CPU 使用率,因为通常大多数事情都是从主线程运行的。您将需要深入了解您已经完成的实际违规者。请记住,这一切都是相对于您在 Instruments 中使用的运行完成的。所以如果你没有足够的数据点,你可能会得到红鲱鱼。这方面的一个例子是游戏加载。这可能是一项昂贵的操作。在某些情况下,它可能看起来相对较快(例如加载 1-5 秒),但是当您查看分析时,您可能会发现它消耗了大量时间。但是,如果您在运行时发现 FPS 很高,那么加载的开销是值得的。

转到您的场景,一个问题是您运行了多长时间?另一个是 addNormAlien 实际上多久被调用一次?是每一帧吗?您的 addNormAlien 有两个部分正在消耗时间,即创建和添加到层次结构。在不知道 Apple 是如何实现这些项目的情况下,它们实际上有点像黑匣子,但很明显,创作的某些方面是昂贵的。同样,不知道你的游戏的时间质量使得这很难完全确定下来。回到我之前提到的,如果所有这些工作都是在加载过程中完成的,那么这些数字可能是一个红鲱鱼。因此,您需要确保您的测试运行对游戏会话具有足够的代表性。

一个建议是创建一个现有的已经构建的外星人节点池。然后在需要时从池中拉出一个对象(顺便说一句,您可能也应该包含您的 normAlien 方法)。这是顺便说一句,有多少游戏做到了。他们基于使用加载时间来招致对象创建的“惩罚”的前提,创建了他们将在运行时需要的项目的预分配版本,从而使运行时相对没有这种开销。请注意,这有一个技巧。从池中拉出一个空闲对象时,您需要做一些最小的初始化。以前您会一直依赖构造函数/初始化程序。但是由于这是一个已经创建的对象,因此您将没有这种奢侈(请记住,您正在尝试在运行时消除不必要的开销)。这意味着您确实需要对此保持警惕,因为它可能会产生一些难以追踪的错误。

我在其他帖子中提到的另一件事是禁用某些东西。例如,在副本看起来很昂贵的地方发生了 runAction。

关于纹理大小。大有多大?一般来说,这是一个坏主意。你这样做会招致惩罚。但是,根据大小、缩放位置等,开销可能可以忽略不计。一个简单的测试是运行纹理大小合适而不是大的版本。如果您总是按比例缩小,则没有理由使用大的。

无论如何,这里有很多事情需要研究,这就是为什么游戏性能调整可能很棘手,以及为什么一种游戏的方法可能不适用于另一种游戏。只有你知道你如何构建它的细微差别。

添加了基于信息的问题更新。

关于您对问题的更新。在我看来,您现在正在将范围扩展到不相关的项目(预加载纹理),并且还在寻找解决所有问题的灵丹妙药或一站式解决方案。空无一人。如前所述,补救这些类型的事情可能很棘手并且高度依赖游戏。只有您知道您的游戏,也只有您可以在 Instruments 中运行它。有一种使用的策略。其中大部分来自经验和一些问题(例如,为什么初始化需要这么长时间?)。如果这会导致更多 SO 问题,那么请中断该问题。

关于预加载纹理,您需要为此创建一个新问题。想想你在很多游戏中看到的加载屏幕。为什么你认为它们存在?在背景中加载诸如纹理和其他数据之类的东西。是的,它增加了开发复杂性,但需要为用户提供更好的体验。预加载是通过preload 完成的。您在代码 sn-ps 中引用的只是数据的初始化,稍后可用于引用纹理实例。

我建议您做的是找到隔离部分代码的方法,以确定您的性能影响来自何处。例如,您检查了节点数。 20不是很大。 PI 会令人惊讶地动态创建它们会有这样的开销,但您的时间分析确实表明可能存在一些显着的开销。如果初始化开销确实很大,你可以通过简单地让你的游戏每帧分配新的外星人来衡量它。您应该能够运行几秒钟而不会耗尽内存。然后,您可以使用它开始删除部分代码,以查看这对性能有何影响。您应该希望在这里看到其中大部分确实是反复试验。没有灵丹妙药/固定答案,需要一定的心态才能擅长优化。

我在您的其他问题之一中提到了这一点,但如果您在模拟器上执行此操作,那么这不是调整性能的正确方法。必须在设备上完成。

【讨论】:

我刚刚用我的纹理预分配更新了我的帖子 - 希望这是正确的方法?我也相信runAction 是纹理精灵的动画。我也很好奇如何创建现有对象的“池”?那时我不能给他们不同的坐标/速度吗?还是我会构建它们并在之后分配这些属性?有没有办法在运行一些基本动画的同时进行预加载?在游戏开始之前加载精灵时,一切都冻结了,GameScene 的淡入动画不运行。 addNormAlien 每 1-2 秒调用一次,运行大约 5 分钟。 那么肯定有问题,如果你只调用它 1-2 秒,除非你的 addNormAlien 占用过多的 CPU 资源,否则我预计不会有太大影响。我在回答中添加了更多信息。

以上是关于SpriteKit 游戏中的高 CPU 和帧率下降的主要内容,如果未能解决你的问题,请参考以下文章

优化SpriteKit游戏性能的15条建议

处理草图没有流畅的动画,但 GPU/CPU 负载和帧率正常

SpriteKit 游戏在 AppDelegate 中花费大量时间

FPS在SpriteKit中下降

Unity--时间和帧率

Directshow:如何更改视频的大小和帧率?