使用Sprite Kit中的SKShapeNode表现不佳

oyv*_*uge 11 frame-rate sprite-kit skshapenode

我正在使用Sprite Kit 制作一个" Achtung die kurve ".对于不断移动的线/玩家,我正在使用A CGMutablePathRef和SKShapeNode.在更新方法中,我正在这样做

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

添加到该行.更新方法还在不断更新_xPos和_yPos以使其增长.

我想我真正要问的是有另一种更有效的绘制线条的方法,因为我现在这样做的方式现在会在一段时间后(大约15-20秒)将帧速率降低太多.此时FPS不断下降,直到游戏无法播放.时间分析器告诉我这一行:_lineNode.path = path是FPS丢弃的原因.

谢谢你的帮助!非常感谢.

PS.我试图不使用SKShapeNode,因为他们似乎无法画得太好(曲线中的小孔/文物等)

截图: 线被不断绘制

pro*_*cal 19

不幸的是,SKShapeNode并不适合你想做的事情.然而,有一种方法可以优化这一点,尽管有一些警告.

fps最大的问题之一是绘图计数非常高,因为你添加的每个线段都是另一个绘图.如果您showsDrawCountSKView实例上设置,您将看到我的意思.

在这个答案中,一个平局中有多个skshapenode?,如果您正在绘制一次,您可以获得有关如何使用a的shouldRasterize属性SKEffectNode来解决问题的更多信息.如果不这样做,每帧将在多次绘制上花费处理器时间.

所以你可以看到抽奖是主要问题,你没有得到你想要的表现.但是,您似乎希望随着时间的推移不断绘制,所以我建议的可能是一个可行的解决方案.

我建议的解决方案的逻辑是这样的:

1 - 创建一个SKSpriteNode我们可以用作画布的东西.

2 - 创建一个SKShapeNode仅用于绘制当前线段的一个.

3 - 让它SKShapeNode成为画布的孩子.

4 - 通过绘制新的线段 SKShapeNode

5 - 使用SKView`textureFromNode方法保存当前在画布上绘制的内容.

6 - 将画布的纹理设置为该纹理.

循环回到#4并SKShapeNode为下一个线段创建一条新路径.

根据需要重复.

结果应该是你的抽奖计数永远不会高于2抽奖,这将解决高抽奖计数的问题.

基本上,你保留了之前在纹理中绘制的内容,因此只需要一次SKShapeNode绘制最新的线段和一次绘制SKTexture.

同样,我还没有尝试过这个过程,如果有任何延迟,那么textureFromNode每个帧都会进行调用.如果有什么东西会成为你的瓶颈,那就是它!

我可能会在今天的某个时候尝试这个理论,因为我需要textureFromNode解决另一个问题,所以我肯定会发现这个方法有多快/慢!哈哈

UPDATE

这不是完整的代码,但是实现所需绘图性能(60fps)的重要部分:

基本节点元素是:

container - >包含需要缓存的所有元素的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];
}
Run Code Online (Sandbox Code Playgroud)

接下来创建从池中获取SKShapeNode的方法:

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

接下来,创建一个从池中获取段并绘制线的方法:

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

接下来是一种创建纹理并将现有段返回池的方法:

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

}
Run Code Online (Sandbox Code Playgroud)

最后是触摸处理程序:

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

正如我所说的,这不是全包代码,我假设您对可以在应用程序中实现的概念有足够的了解.这些只是我的准系统实现的例子.