13

Sprite Kitで「 Achtung die kurve 」クローンを作っています。常に移動するライン/プレーヤーには、SKShapeNode と共に CGMutablePathRef を使用しています。updateメソッドで私はこれをやっています

// _lineNode is an instance of SKShapeNode and path is CGMutablePathRef
CGPathAddLineToPoint(path, NULL, _xPos, _yPos);
_lineNode.path = path;

行に追加します。update メソッドは、_xPos と _yPos を常に更新して成長させます。

私が実際に求めているのは、線を描画する別のより効率的な方法があることだと思います。これは、私が行っている方法では、しばらくするとフレームレートが大幅に低下するためです (約 15-20 秒)。この時点で、ゲームがプレイできなくなるまで FPS が絶えず低下します。Time Profiler は、この行 _lineNode.path = path が FPS ドロップの原因であることを示しています。

助けてくれてありがとう!大変感謝しております。

PS。SKShapeNode をまったく使用しないようにしています。線をうまく描画できないように見えるためです (曲線の小さな穴/アーティファクトなど)。

スクリーンショット: 常に引かれる線 引かれる

4

3 に答える 3

23

残念ながら、SKShapeNode は、あなたがしようとしていることにはあまり適していません。ただし、いくつかの注意点はありますが、これを最適化する方法があります。

fps の最大の問題の最初の 1 つは、追加する各線セグメントが別の描画であるため、描画カウントが非常に高くなることです。インスタンスを設定showsDrawCountするSKViewと、私が何を意味するかがわかります。

この回答では、1回の描画で複数のskshapenode? 、一度何かを描画している場合、問題を解決するためshouldRasterizeに a のプロパティを使用する方法に関する詳細情報を取得できます。SKEffectNodeこれを行わないと、フレームごとに多数の描画にプロセッサ時間が費やされます。

したがって、ドローが主な問題であり、希望するパフォーマンスが得られないことがわかります。ただし、時間の経過とともに一貫して描画したいと考えているようです。そのため、私が提案することは、実行可能な解決策になる可能性があります。

私が提案しているソリューションのロジックは次のとおりです。

1 -SKSpriteNodeキャンバスとして使用できる を作成します。

2 -SKShapeNode現在の線分のみを描画するために使用されるものを作成します。

3 - それをSKShapeNodeキャンバスの子にします。

4 - 新しい線分を描きますSKShapeNode

5 - SKView`textureFromNode' メソッドを使用して、キャンバスに現在描画されているものを保存します。

6 - キャンバスのテクスチャをそのテクスチャに設定します。

#4 に戻りSKShapeNode、次の線分の新しいパスを作成します。

必要に応じて繰り返します。

その結果、ドロー カウントが 2 ドローを超えることはなくなり、ドロー カウントが高くなる問題が解決されます。

基本的に、テクスチャで以前に描画されたものを保持しているSKShapeNodeため、最新の線分に対して 1 つの描画とSKTexture.

繰り返しますが、私はまだこのプロセスを試していません。ラグがある場合は、textureFromNode各フレームでその呼び出しになります。何かがあなたのボトルネックになるとしたら、それはそれです!

解決しようとしている別の問題が必要なので、今日この理論を試してみるかもしれませんtextureFromNode。そのため、その方法がどれほど速い/遅いかを確実に知ることができます! 笑

アップデート

これは完全なコードではありませんが、必要な描画パフォーマンス (60fps) を実現するための重要な部分です。

基本的なノード要素は次のとおりです。

コンテナー -> キャッシュする必要があるすべての要素を含む SKNode

canvas -> 描かれたセグメントのキャッシュされたバージョンを表示する SKSpriteNode

セグメントのプール -> 最初にセグメントを描画するために使用され、必要に応じて再利用されます

最初に SKShapeNodes のプールを作成します。

pool = [[NSMutableArray alloc]init];

//populate the SKShapeNode pool
// the amount of segments in pool, dictates how many segments
// will be drawn before caching occurs.
for (int index = 0; index < 5; index++)
{
    SKShapeNode *segment = [[SKShapeNode alloc]init];
    segment.strokeColor = [SKColor whiteColor];
    segment.glowWidth = 1;
    [pool addObject:segment];
}

プールから SKShapeNode を取得するための次の create メソッド:

-(SKShapeNode *)getShapeNode
{
    if (pool.count == 0)
    {
        // if pool is empty, 
        // cache the current segment draws and return segments to pool
        [self cacheSegments];
    }

    SKShapeNode *segment = pool[0];
    [pool removeObjectAtIndex:0];

    return segment;
}

次に、プールからセグメントを取得して線を描画するメソッドを作成します。

-(void)drawSegmentFromPoint:(CGPoint)fromPoint toPoint:(CGPoint)toPoint
{
    SKShapeNode *curSegment = [self getShapeNode];
    CGMutablePathRef path = CGPathCreateMutable();
    curSegment.lineWidth = 3;
    curSegment.strokeColor = [SKColor whiteColor];
    curSegment.glowWidth = 1;
    curSegment.name = @"segment";

    CGPathMoveToPoint(path, NULL, fromPoint.x, fromPoint.y);
    CGPathAddLineToPoint(path, NULL, toPoint.x, toPoint.y);
    curSegment.path = path;
    lastPoint = toPoint;
    [canvas addChild:curSegment];
}

次は、テクスチャを作成し、既存のセグメントをプールに返すメソッドです。

-(void)cacheSegments
{
    SKTexture *cacheTexture =[ self.view textureFromNode:container];
    canvas.texture = cacheTexture;
    [canvas setSize:CGSizeMake(canvas.texture.size.width, canvas.texture.size.height)];
    canvas.anchorPoint = CGPointMake(0, 0);
    [canvas enumerateChildNodesWithName:@"segment" usingBlock:^(SKNode *node, BOOL *stop)
     {
         [node removeFromParent];
         [pool addObject:node];
     }];

}

最後に、タッチ ハンドラー:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self cacheSegments];
    for (UITouch *touch in touches)
    {
        CGPoint location = [touch locationInNode:self];
        lastPoint = location;
        [self drawSegmentFromPoint:lastPoint toPoint:location];
    }
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    for (UITouch *touch in touches)
    {
        CGPoint location = [touch locationInNode:self];
        [self drawSegmentFromPoint:lastPoint toPoint:location];
    }
}

前述したように、これは包括的なコードではありません。アプリケーションに実装できる概念について十分に理解していることを前提としています。これらは、私のベアボーン実装の単なる例です。

于 2014-07-03T15:22:43.990 に答える
2

曲線の「穴」を修正するには、lineCapをゼロ以外の値に設定します。

curSegment.lineCap = 1;
于 2015-12-07T04:48:43.850 に答える