UITableView& UIScrollview停止cocos2d动画

时间:2013-03-03 12:35:58

标签: iphone ios objective-c uitableview cocos2d-iphone

我有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);
}
}

2 个答案:

答案 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];