UITableView和UIScrollview停止cocos2d动画

B.S*_*.S. 3 iphone objective-c uitableview cocos2d-iphone ios

我有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);
}
}
Run Code Online (Sandbox Code Playgroud)

Lea*_*s2D 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

此代码将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
Run Code Online (Sandbox Code Playgroud)

在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);
}
Run Code Online (Sandbox Code Playgroud)

并在dealloc中发布:

-(void) dealloc
{
    dispatch_release(frameRenderingDispatchQueue);
    dispatch_release(frameRenderingSemaphore);
    [displayLink release];
    [super dealloc];
}
Run Code Online (Sandbox Code Playgroud)

这可以防止滚动视图阻止cocos2d框架绘制.我添加了cocoaFriendlyRenderingBOOL(我假设您知道如何添加BOOL属性)所以我可以仅对使用滚动视图的场景启用此行为.我认为这可能对性能更好,并防止在游戏过程中出现任何奇怪的问题,但可能没有必要.

此代码适用于cocos2d 1.0.1,可能需要稍微调整以适应cocos2d 2.x,例如EAGLView - > CCGLView,EAGLContext - > CCGLContext.

其他解决方案围绕定时器和/或降低帧速率(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];
}
Run Code Online (Sandbox Code Playgroud)