我正在使用Swift和SpriteKit构建我的第一个游戏,我最近把整个事情分开来确定我的CPU问题来自哪里(在游戏的几分钟内约80-100%)。我最近发现,最昂贵的操作之一位于touchesMoved
方法中。这并不令人惊讶,因为它被非常频繁地调用,并且用户可以用手指向整个游戏控制主角。然而,我惊讶地发现特别是一条线路造成了麻烦。这是我的代码:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch : AnyObject in touches {
let location = touch.location(in: self)
let touchStartPoint:CGPoint = startingTouches[touch as! UITouch]!
if(controlBase.frame.contains(touchStartPoint)){
controlStick.position = location
}
}
}
我用仪器进行了4次单独测试,每次测试都有另一行。我发现没有
if(controlBase.frame.contains(touchStartPoint)){
controlStick.position = location
}
当我的手指停止时,我在~5-10%的CPU上徘徊 - 所以touchesMoved
仍被调用。但是,在进行检查if(controlBase.frame.contains(touchStartPoint)){
时,即使括号内没有任何内容,我也会达到~20-30%的CPU。
这是我的仪器日志的图片:低点是当我的手指从屏幕抬起时,当我的手指移动控制器时高点。
有没有解决这个问题?我正在进行此检查以确保用户虚拟操纵杆上的句柄不能离开其基础。我只是不确定为什么这一行会如此昂贵,如果这是检查所固有的,如果有另一种方法来实现类似的功能。谢谢!
答案 0 :(得分:2)
啊,好吧,我在另一个帖子中留下了一些评论之后才看到这个。在你获得的触摸事件的频率方面,你有点受操作系统的支配。您可以尝试限制在这些时刻占用的带宽。
这里的问题是你的节点树有多大?临时添加一些代码以通过手动向下走树来物理地计算节点数可能是有用的。您是否还在节点上应用缩放和旋转并使用层次结构?如果是这样,你应该注意,因为它需要更多的计算。
仍然基于你之前描述的内容,如果这真的占用了你的带宽,我会感到惊讶(假设你说你有20个物体)。
或者如果你真的想要你可以创建自己的包含。这实际上就是我所做的,但是这部分是我对处理图形的特殊需求的副产品。话虽这么说,我有数百个对象,但我没有明显的问题。请注意,我没有像你那样仔细检查CPU使用情况。我只专注于FPS。
在此期间你的FPS是多少?此外,您应该在设备上运行此功能,并查看相对于帧的CPU / GPU使用情况。 %CPU也可能有点可疑我觉得基于其他因素,例如,如果你正在记录,甚至有时候,操作系统还会发生什么。
最后尝试将代码移至太空船示例,并在添加更多太空船时注意效果。
添加了详细信息
因此,当我最初回答问题时,我对你正在做的事情不太重视。我仍然相信你现在不应该过分努力优化这里,不过要看SKOOP的回答,特别是
https://github.com/MitrophD/Swift-SpriteKit-Analog-Stick
这是一个有趣的代码,让我更好地了解你真正想做什么以及如何在需要时挤出CPU使用率。游戏编码和优化背后的原则之一就是懒惰。 &#34;我如何预先计算或完成最少量的工作?&#34;
在这里,您需要对touchesBegin
和touchedEnd
/ touchesCancelled
进行一些调整。在正在进行触摸处理的类中,添加一个名为
var controllerTouch : UITouch?
请注意,这是可选的,将代表控制器中的当前触摸。另请注意,如果您正在进行多点触控,则将其定义为规则:控制器内的第一次触摸是控制触摸。
这提出了另一个关键点,即规则。您需要定义规则。在这里,我要说的是,触摸一个活动的控制器超出controllerBase
的界限(这就像最大限度地棒)并且控制器应该实际上是一个圆圈是可以的。最后,我将假设controllerBase
是静态的。
现在,我可以采用一些方法来最大限度地减少所需的工作。
<强> touchesBegin
强>
由于这是静态的,我应该通过比较触摸的位置与距controllerBase
中心的距离来使用简单的数学运算。我可以这样做,因为我们说这是一个圆圈。所以我应该知道半径。因此,我的检查将是:
let distance = computeDistance(pt0: controllerBaseCenter, pt1: location)
// We only care about doing this if we are not currently actively tracking
if controllerTouch == nil {
if distance < controllerBaseRadius {
// Where touch is the current touch
controllerTouch = touch
}
}
注意代码未经过编译测试或是否有效,而是一个示例,可以根据需要对其进行修复。我假设您将使用正确的数学替换computeDistance
来计算距离,这是一件微不足道的事情。你的距离也需要绝对。
要记住的另一件事。由于controllerBase
是静态的,因此您可以在变量中预先计算并存储实际的中心点(controllerBaseCenter
和半径。这可以保护您不必执行location
或contains
节点,因此每次touchesStart
运行时都会节省浪费的周期。
<强> touchesEnd
/ touchesCancelled
强>
如果 ,那么您将无法结束/取消触摸。如有必要,您也可以使用任何其他逻辑进行通知。
真正要处理的是
<强> controllerTouch
强>
所以我们知道我们已经通过touchesMoved
controllerTouch
请注意我推迟评论。在原始代码中,您只应在需要时执行override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch : AnyObject in touches {
if touch == controllerTouch {
// Only get location when needed (ie defer until you really need this)
let location = touch.location(in: self)
controlStick.position = location // You will need to adjust, see notes below to compensate for controllerBaseCenter if needed
}
}
}
分配,即触摸位于location
时。
现在,我遗漏了最后一件事。您将如何处理控制器可以移动的径向距离。例如,如果半径为64并且我的触摸开始于距离中心60的距离,那么如何处理?您是否将该偏移视为中心点?如果是这种情况,您还需要跟踪第一次触摸的位置,以正确调整数学。
请记住,您需要让玩家不必保持精确。另外请记住,他们不会经常低头看控制器区域来仔细检查。
答案 1 :(得分:1)
这是处理Apple Frameworks的心灵弯曲方面之一。
没有好的微型示例!
许多游戏引擎和框架提供了一系列小的,功能性的,相关的示例,有时数百个,展示了如何使用引擎功能的各个方面。
然后,这些(也)用作引擎的每次迭代的类似单元的测试,并相应地更新。 Apple的一些Sprite Kit演示代码是原始形式。
社区很少。
不幸的是,你最好去研究别人在github上做过类似的事情,因为那里甚至没有&#34;最佳做法&#34;来自Apple的Sprite Kit文档。
这是github上的虚拟操纵杆示例。
https://github.com/MitrophD/Swift-SpriteKit-Analog-Stick
这里有一个有趣的问题:Dpad for sprite kit
其中条件是关于触摸的节点的名称,而不是在.frame中搜索
//if control pad touched
if ([node.name isEqualToString:@"controlPadNode"]) {
....
}
可能是.frame内的搜索费用很高。它(应该是)简单的数学,所以我不知道为什么会这样,但是随后SpriteKit和SceneKit出现了很多奇怪的问题。