原因是因为 UIKit 视图没有设计为与其他视图协作。它们用于用户交互,因此滚动停止更新其他视图通常无关紧要。在用户界面驱动的应用程序中,这很有意义,因为您希望对具有用户焦点并需要大量资源来制作动画(即滚动)的视图提供最佳响应和感觉。
正确解决此问题的唯一方法是CCDirectorDisplayLink
使用信号量改进 cocos2d 渲染循环:
-(void) mainLoop:(id)sender
{
if (dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW) != 0)
{
return;
}
if (useCocoaFriendlyRendering)
{
dispatch_async(frameRenderingDispatchQueue, ^{
[EAGLContext setCurrentContext:openGLView_.context];
[self drawScene];
dispatch_semaphore_signal(frameRenderingSemaphore);
});
}
else
{
[EAGLContext setCurrentContext:openGLView_.context];
[self drawScene];
dispatch_semaphore_signal(frameRenderingSemaphore);
}
}
这段代码将 cocos2d drawscene 方法放在它自己的调度队列中。我曾经明白它做了什么,但我不记得了,所以与其错误地解释它的作用或它为什么起作用,我希望有人可以在评论中填写它。:)
注意:可能不需要每次都调用最后一个调度信号量,但我这样做是因为切换 cocoaFriendlyRendering 标志可能会导致 semaphore_wait 无限期等待(冻结渲染)。所以我只是每次都发出信号。
我更新CCDirectorDisplayLink
如下(CCDirectorIOS
有 cocoaFriendlyRendering BOOL):
@interface CCDirectorDisplayLink : CCDirectorIOS
{
id displayLink;
dispatch_semaphore_t frameRenderingSemaphore;
dispatch_queue_t frameRenderingDispatchQueue;
}
-(void) mainLoop:(id)sender;
@end
在 startAnimation 中创建信号量和调度队列:
- (void) startAnimation
{
...
displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(mainLoop:)];
[displayLink setFrameInterval:frameInterval];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
frameRenderingSemaphore = dispatch_semaphore_create(1);
frameRenderingDispatchQueue = dispatch_queue_create("render", DISPATCH_QUEUE_SERIAL);
}
并在 dealloc 中发布:
-(void) dealloc
{
dispatch_release(frameRenderingDispatchQueue);
dispatch_release(frameRenderingSemaphore);
[displayLink release];
[super dealloc];
}
这样可以防止滚动视图阻塞 cocos2d 框架绘制。我添加了cocoaFriendlyRendering
BOOL(我假设您知道如何添加 BOOL 属性),因此我可以仅对使用滚动视图的场景启用此行为。我认为这可能会更好地提高性能并防止游戏过程中出现任何奇怪的问题,但这可能不是必需的。
此代码适用于 cocos2d 1.0.1,可能需要稍作调整才能与 cocos2d 2.x 一起使用,例如 EAGLView -> CCGLView、EAGLContext -> CCGLContext。
其他解决方案围绕计时器和/或降低帧速率 (animationInterval) 和/或更改显示链接对象的运行循环模式。它们并不能很好地工作,滚动可能不流畅或滚动可能突然停止。我尝试了所有这些解决方案,因为我不想弄乱 cocos2d 的渲染循环 - 但信号量是唯一完美工作的解决方案,使 UIKit 滚动与 cocos2d 合作(反之亦然)。
更新:
我忘了提,我创建了一个 UIScrollView 的子类来处理 cocoaFriendlyRendering(初始化时是,dealloc 时否),更重要的是,它覆盖了触摸事件,以便根据需要将它们转发到 cocos2d(只要滚动视图不滚动) .
这些是覆盖的触摸方法:
-(void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
if (self.dragging)
[super touchesBegan:touches withEvent:event];
else
[[CCDirector sharedDirector].openGLView touchesBegan:touches withEvent:event];
}
-(void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
if (self.dragging)
[super touchesMoved:touches withEvent:event];
else
[[CCDirector sharedDirector].openGLView touchesMoved:touches withEvent:event];
}
-(void) touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event
{
[super touchesCancelled:touches withEvent:event];
[[CCDirector sharedDirector].openGLView touchesCancelled:touches withEvent:event];
}
-(void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
[super touchesEnded:touches withEvent:event];
[[CCDirector sharedDirector].openGLView touchesEnded:touches withEvent:event];
}