我正在用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"].map{textureAtlas.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)
}
如果有一种更好的预分配纹理的方法,我很乐意知道,并且如何进一步了解如何评估其他一些问题将会非常棒!
答案 0 :(得分:2)
通常,您会发现main包含最高的CPU使用率,因为大多数情况都是从主线程运行的。您将需要向下钻取以找到您已经完成的实际违规者。请记住,这一切都是相对于您使用仪器的运行完成的。因此,如果您没有足够的数据点,您可以获得红色鲱鱼。一个例子是游戏加载。这可能是一项昂贵的操作。在某些情况下,它可能看起来相对较快(比如加载1-5秒),但是当你查看你的分析时,你可能会发现它消耗了大量的时间。但是,如果您在运行时发现FPS很高,那么该负载的开销是值得的。
转到您的方案,一个问题是您运行了多长时间?另一个是addNormAlien实际调用的频率是多少?是每一帧吗?你的addNormAlien有2个部分正在耗费时间,这是创建以及添加到层次结构。在不知道Apple如何实现这些项目的情况下,它们实际上是一个黑盒子,但很明显有些方面的创建是昂贵的。同样,不知道你的游戏的时间质量使得这很难完全固定下来。回到我前面提到的,如果所有这些工作都是在加载过程中完成的,那么这些数字可能是一个红色的鲱鱼。因此,您需要确保您的测试运行具有足够的代表性。
一个建议是创建已构建的外星人节点的现有池。然后在需要时从池中拉出一个对象(顺便说一下,你可能也应该包括你的normAlien方法)。这是BTW,它有多少游戏。他们根据使用加载时间产生"惩罚的前提,在运行时创建他们将需要的预分配版本的项目。对象创建,保持运行时相对没有开销。请注意,这有一个技巧。从池中提取空闲对象时,您需要进行一些最小化初始化。以前你一直在依赖构造函数/初始化程序。但由于这是一个已经创建的对象,你将不会有这种奢侈(记住你在尝试去除运行时不必要的开销)。这意味着你需要对它保持警惕,因为它可能会产生一些难以追踪的错误。
另一件事,我在你发布的其他帖子中提到的就是禁用内容。例如,发生了一个runAction,副本看起来很昂贵。
关于纹理大小。大到多大?一般来说,这是一个坏主意。这样做你会受到处罚。但是,根据大的大小,缩放比例等,开销可以忽略不计。一个简单的测试是运行一个版本,其中纹理是正确的大小而不是大的。如果你总是按比例缩小,没有理由使用大的。
无论如何,这里有很多东西需要研究,这就是为什么游戏性能调整可能很棘手,以及为什么一种游戏的方法可能不适用于另一种游戏。只有你知道你如何建立它的细微差别。
关于您对问题的更新。在我看来,你现在正在扩大范围到不相关的项目(预加载纹理),并且还在寻找一个银弹或一站式解答你所有的困境。空无一人。如上所述,补救这些类型的东西可能是棘手的,并且高度依赖游戏。只有你知道你的游戏,只有你可以在乐器中运行它。有一种策略可供使用。其中很大一部分来自经验和一些问题(例如,为什么初始化需要这么长时间?)。如果这导致更多的SO问题,那么就可以解决问题。
关于预加载纹理,您需要为此创建一个新问题。想想你在很多游戏中看到的加载屏幕。为什么你认为他们存在?在背景中加载像纹理和其他数据的东西。是的,它增加了开发复杂性,但需要为用户提供更好的体验。预加载是通过preload
完成的。您在代码片段中引用的内容只是数据的初始化,以后可以用它来引用纹理实例。
我建议你做的是找到隔离部分代码的方法,以确定你的性能影响来自哪里。例如,您已检查节点计数。 20不是很大。 PI会令人惊讶的是,动态创建它们会产生这样的开销,但是你的时间分析确实表明可能会有一些重大的开销。如果init开销确实很大,你可以通过简单地让你的游戏每帧都分配新的外星人来测量它。您应该可以运行几秒钟而不会耗尽内存。然后,您可以使用它来开始删除部分代码,以查看它对性能的影响。你应该在这里看到,这很多都会成为试验和错误。没有银弹/设定答案,需要一定的心态才能善于优化。
我在其他一个问题中提到了这一点,但如果你在模拟器上这样做,那么调整性能并不是正确的方法。必须在设备上完成。