ios - Sprite Kit 中可重用的多线程实现

标签 ios objective-c multithreading sprite-kit grand-central-dispatch

我正在开发一款 Sprite Kit 游戏,我需要进行一些多线程处理以维持健康的 fps。

在更新时,我调用一个函数来创建大量 UIBezierPaths 并使用 C++ 静态库合并它们。

如果我有超过 10 个形状,帧速率会急剧下降,所以我决定尝试 GCD 并尝试使用单独的线程来解决这个问题。

我把它放在 didMoveToView 中:

queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

在每一帧调用的函数中,我称之为:

dispatch_async(queue,^(void){[self heavyCalculationsFunc];});

对于非常了解 GCD 的人来说,它在每一帧上创建一个新线程可能是显而易见的,但对我来说还不清楚。

我的问题是,有什么方法可以重用我想在更新时调用的线程吗?

提前感谢您的帮助!

最佳答案

如果您需要在每一帧上完成工作,并且需要在渲染帧之前完成这些工作,那么多线程可能帮不了您,除非您愿意为此付出很多努力。

维持帧速率的关键在于时间——与 CPU 资源无关,只是等待时间。为了保持 60fps 的帧率,你有 16.67 毫秒的时间来完成所有的工作。(实际上,少于这个时间,因为 SpriteKit 和 OpenGL 需要一些时间来渲染你的工作结果。)这是一个同步问题 - 你有工作,您有特定的时间来完成它,因此提高绩效的第一步是减少工作量或提高工作效率。

另一方面,多线程通常用于异步问题 — 您需要做一些工作,但不需要现在完成,因此您可以继续处理其他问题你现在需要做的事情(比如在 16 毫秒内从你的更新方法返回以保持你的帧率)并稍后检查该工作的结果(比如,在后面的帧)。


不过,这两个定义之间有一点回旋余地:几乎所有的现代 iOS 设备都有多核 CPU,所以如果你玩得对,你可以在你的同步问题中加入一点异步性 并行化您的工作量。完成并做好这件事绝非易事——它一直是 serious research and investment by big game studios 的主题。多年来。

看看"How a Scene Processes Frames of Animation"下的图在 SpriteKit 编程指南中。那是你的 16 毫秒时钟。浅蓝色区域是 Apple 的 SpriteKit(以及 OpenGL 和其他系统框架)代码负责的那 16 毫秒的片段。其他切片是你的。让我们展开该图以获得更好的外观:

SpriteKit rendering loop, linear

如果您在任何这些切片中做太多工作,或者使 SpriteKit 的工作负载太大,整个事情就会超过 16 毫秒,并且您的帧率会下降。

线程的机会是在同一时间线内在另一个 CPU 上完成一些工作。如果 SpriteKit 对 Action 、物理和约束的处理不依赖于这项工作,您可以与这些事情并行进行:

Game code in parallel to SK code

或者,如果您的工作需要在 SpriteKit 运行 Action 和物理之前进行,但是您还有其他工作需要在 update 方法中完成,您可以将一些工作发送到另一个线程在完成剩余的 update 工作时,然后在 update 方法中检查结果:

Game code in parallel to itself


那么如何完成这些事情呢?这是一种使用调度组的方法,并假设 Action /物理/约束不依赖于您的背景工作——这完全不在我的脑海中,所以它可能不是最好的。 :)

// in setup
dispatch_queue_t workQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t resultCatchingGroup = dispatch_group_create();
id stuffThatGetsMadeInTheBackground;

- (void)update:(NSTimeInterval)currentTime {
    dispatch_group_async(group, queue, ^{
        // Do the background work 
        stuffThatGetsMadeInTheBackground = // ...
    });
    // Do anything else you need to before actions/physics/constraints
}

- (void)didFinishUpdate {
    // wait for results from the background work
    dispatch_group_wait(resultCatchingGroup, DISPATCH_TIME_FOREVER);

    // use those results
    [self doSomethingWith:stuffThatGetsMadeInTheBackground];
}

当然,正如其名称所示,dispatch_group_wait 会阻止执行以等待您的后台工作完成,因此您仍然有 16 毫秒的时间限制。如果前台工作(您的 update 的其余部分,加上 SpriteKit 的 Action /物理/约束工作以及您为响应这些事情而完成的任何其他工作)在您的后台工作之前完成,您会等着它。如果后台工作加上 SpriteKit 的渲染工作(加上您在生成后台工作之前在 update 中所做的任何事情)花费的时间超过 16 毫秒,您仍然会丢帧。因此,这样做的诀窍是足够详细地了解您的工作量,以便很好地安排它。

关于ios - Sprite Kit 中可重用的多线程实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28748774/

相关文章:

iphone - iOS/iPad 的消息队列 - 类似于 MSMQ?

.net - 限制进程使用的处理器数量

java - 多线程修改StringBuilder

java - ExecutorService关闭不中断线程

ios - Swift 并覆盖自定义类的 init 方法

ios - Collection View 单元格的动态宽度 - Xamarin iOS

iphone - iPad (iphone os) 内核扩展

ios - Swift 中的“NSInternalInconsistencyException”

ios - 触摸屏幕时以 NSException 类型的未捕获异常终止

iphone - UITabBarController 选中的项目