我有UITableView
,它显示在我的图层`[[CCDirector sharedDirector] .view addSubview:myTableView]前面,并占据了屏幕的一部分。
一切正常:它检测到触摸。 但是在tableView的滚动动画期间,所有cocos2d冻结,直到tableView结束此滚动。 (冻结时我的意思是所有节点的动作暂停)。
问题是:这种冻结的原因是什么,我该如何避免呢?
-(void) mainLoop:(id)sender
{
if (dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW) != 0)
{
return;
}
if (self.useCocoaFriendlyRendering)
{
dispatch_sync(frameRenderingDispatchQueue, ^{ // HERE dispatch_async make black screen, i shows everything but still bad performance.
CCGLView *openGLview = (CCGLView*)[self view];
[EAGLContext setCurrentContext:openGLview.context];
[self drawScene];
dispatch_semaphore_signal(frameRenderingSemaphore);
});
}
else
{
CCGLView *openGLview = (CCGLView*)[self view];
[EAGLContext setCurrentContext:openGLview.context];
[self drawScene];
dispatch_semaphore_signal(frameRenderingSemaphore);
}
}
答案 0 :(得分:7)
原因是因为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 drawcene方法放在其自己的调度队列中。我曾经理解它做了什么,但我不记得了,所以不要错误地解释它的作用或原因,我希望有人可以在评论中填写。 :)
注意:最后一次调度信号量可能不需要每次调用,但我这样做是因为切换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。
Other solutions revolve around timers和/或减少帧速率(animationInterval)和/或更改显示链接对象的运行循环模式。它们并没有真正起作用,滚动可能不顺畅或滚动可能会突然停止。我尝试了所有这些解决方案,因为我不想弄乱cocos2d的渲染循环 - 但信号量是使UIKit滚动与cocos2d合作的唯一完美工作解决方案(反之亦然)。
<强>更新强>
我忘了提到,我创建了一个UIScrollView的子类来处理cocoaFriendlyRendering(init上的YES,dealloc上的NO),更重要的是,它会覆盖触摸事件,以便根据需要将它们转发到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];
}
答案 1 :(得分:2)
进入CCDirector.m或CCDirectorIOS.m文件并找到以下行:
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
将其更改为:
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];