1

我正在使用 Quartz 2D 制作一个简单的多点触控绘图 iPad 游戏。游戏要求我每 1/30 秒在手指位置画一个新的笔画。

据我所知,基本上没有办法让 drawRect() 每次调用时都不清除上下文(self.clearsContextBeforeDrawing = NO; 不起作用),所以我的解决方案是创建一个后台缓冲区位图(或层,我可以同时使用),每次迭代每个手指都将每个新的小笔划绘制到该后台缓冲区中,然后每次调用 drawRect() 时将缓冲区复制到屏幕中。换句话说:

backlayer = CGLayerCreateWithContext(context, CGSizeMake(W, H), NULL);
offctx = CGLayerGetContext (backlayer); 

进而

- (void)drawRect:(CGRect)rect
{

    CGContextRef context = UIGraphicsGetCurrentContext();

    //code here to draw small strokes from old finger position to new one

    CGContextDrawLayerInRect(context, [self bounds], backlayer);
}

这在我在 iPad 2 上测试时没有问题,但昨天我注意到同样的代码在新的 iPad 3 上运行速度要慢得多。性能非常糟糕,我的游戏从 30FPS 一直降低到大约 5 帧左右,可能是由于更大的视网膜显示器。如果我使用我创建的单独的 CGBitmapContext,然后每次迭代都从它创建一个 ImageRef 并用 CGContextDrawImage 绘制它,我会遇到同样的问题。

我可以采取什么方法来解决这个问题?似乎我必须在每次迭代时重绘所有内容,因为它甚至不足以将一个小矩形传递给 drawRect 改变了什么(因为每次迭代都需要为每个手指有几个矩形)

谢谢

4

1 回答 1

2

我设法解决了这个问题:

我创建了一个新的 UIView 子类头和实现文件:

@interface fingerView : UIView {
}

然后在我的主视图中,在标题中我声明了其中的 5 个视图:

fingerView* fview[5];

在我的主视图实现中,我创建了这个实例的 5 个视图,每个手指分别一个。此外,必须确保制作它们,为它们中的每一个启用多点触控,并确保 clearsContextBeforeDrawing 设置为 NO,因为我们将一次更新它们中的小矩形,并且我们不希望系统清除我们的工作。

for(int i=0;i<5;i++) {
        fview[i] = [[pView alloc] initWithFrame:topFrame];
        [self addSubview: fview[i]];
        [self sendSubviewToBack: fview[i]];
        fview[i].opaque= NO;
        fview[i].clearsContextBeforeDrawing = NO;
        fview[i].multipleTouchEnabled = YES;
}

现在在每个手指视图中保留一个大数组(我使用一个简单的数组,比如 10,000 长),其中包含手指绘制的 x 和 y 位置。每当手指移动时,主视图都会检测到它,并调用 [fview[i] updatePos(newx, newy)],至关重要的是,我们将命令视图仅在这些坐标周围更新其自身的一小部分:

[fview[i] setNeedsDisplayInRect: fingerRect];

其中 fingerRect 是一个以 (newx, newy) 为中心的小矩形。在每个手指视图的 drawRect 方法中,

- (void)drawRect:(CGRect)rect
{
    if (movep==0) return;

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetRGBStrokeColor(context, r, g, b, 1);
    CGContextSetLineWidth(context, linewidth);

    //paint finger
    CGContextBeginPath(context);
    CGFloat slack= 15;
    CGFloat minx= CGRectGetMinX(rect)-slack;
    CGFloat maxx= CGRectGetMaxX(rect)+slack;
    CGFloat miny= CGRectGetMinY(rect)-slack;
    CGFloat maxy= CGRectGetMaxY(rect)+slack;    
    bool drawing = NO;
    for(int i=0;i<movep;i++) {
        CGFloat xx= x[i];
        CGFloat yy= y[i];
        if(xx>minx && xx<maxx && yy>miny && yy<maxy) {

            if(drawing) {

                // continue line
                CGContextAddLineToPoint(context, xx, yy);
                CGContextMoveToPoint(context, xx, yy);

            } else {

                // start drawing
                CGContextMoveToPoint(context, xx, yy);
                drawing= YES;
            }

        } else {
            drawing= NO;  
        }
    }

    CGContextStrokePath(context);

而且,正如我提到的

- (void)updatePos: (CGFloat)xnew: (CGFloat) ynew
{
    x[movep]= xnew;
    y[movep]= ynew;
    movep= movep+1;

希望你能弄清楚这是如何工作的。每个视图都会查看这个已修改的矩形,并检查围绕该矩形的所有手指位置,并且只绘制那些。这将归结为很少的笔画,因此整个代码运行得非常快。

整体的教训是 UIViews 是非常优化的。尽可能地尝试制作一大堆,如果有的话,只在本地更新它们,然后让 Apple 的魔法将它们融合在一起。

于 2012-04-23T09:07:00.570 回答