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 で実行される速度が大幅に低下することに気付きました。おそらく、Retina ディスプレイが大きいためでしょう。作成した別の CGBitmapContext を使用し、反復ごとにそれから ImageRef を作成し、CGContextDrawImage でペイントすると、同じ問題が発生します。

これに対処するには、どのようなアプローチを取ることができますか? 変更されたものの drawRect に小さな長方形を渡すだけでは十分ではないため、反復ごとにすべてを再描画する必要があるようです (反復ごとに指ごとにいくつかの長方形が必要になるため)

ありがとうございました

4

1 に答える 1

2

私はこれを次のように解決することができました:

新しいUIViewサブクラスヘッダーと実装ファイルを作成します。

@interface fingerView : UIView {
}

次に、メインビューのヘッダーで、これらのビューのうち5つを宣言します。

fingerView* fview[5];

メインビューの実装では、このインスタンスの5つのビューを、指ごとに1つずつ作成します。また、それらを作成し、それぞれに対してマルチタッチを有効にし、clearsContextBeforeDrawingがNOに設定されていることを確認する必要があります。これは、一度にそれぞれの小さなrectを更新するためであり、システムが仕事。

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;
}

これで、すべての指のビュー内に、指が描画したx位置とy位置の大きな配列(単純な配列、たとえば10,000の長さを使用)を保持します。指が動くたびに、メインビューはそれを検出し、[fview [i] updatePos(newx、newy)]を呼び出します。そして重要なことに、これらの座標の周りの小さな部分だけを更新するようにビューに命令します。

[fview[i] setNeedsDisplayInRect: fingerRect];

ここで、fingerRectは、(newx、newy)を中心とする小さなrectです。すべてのフィンガービューの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;

うまくいけば、これがどのように機能するかを理解できます。すべてのビューは、変更されたこの長方形を調べ、その長方形の周りを回ったすべての指の位置をチェックし、それらのみを描画します。これは非常に少数のストロークになるため、コード全体が非常に高速に実行されます。

全体的な教訓は、UIViewが非常に最適化されているということです。可能な限り、それらをたくさん作成し、ローカルでのみ更新して、Appleの魔法ですべてをブレンドするようにしてください。

于 2012-04-23T09:07:00.570 に答える