Sprite Kit 中的 SKShapeNode 性能不佳 [英] Poor performance with SKShapeNode in Sprite Kit

查看:17
本文介绍了Sprite Kit 中的 SKShapeNode 性能不佳的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm making a "Achtung die kurve"-clone in Sprite Kit. For the constantly moving lines/players I'm using A CGMutablePathRef along with an SKShapeNode. In the update method I'm doing this

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

添加到该行.更新方法也是不断更新_xPos和_yPos,让它成长.

to add to the line. The update method is also updating the _xPos and _yPos constantly to make it grow.

我想我真正要问的是是否还有另一种更有效的画线方式,因为我现在这样做的方式会在一段时间后(大约 15-20 秒)大幅降低帧速率.此时 FPS 会不断下降,直到游戏无法玩.Time Profiler 告诉我这行:_lineNode.path = path 是导致 FPS 下降的原因.

I guess what I'm really asking is is there another, more efficient way of drawing the lines, since the way I'm doing it now drops the frame rate way too much after a while (about 15-20 seconds). At this point the FPS just drops constantly until the game is unplayable. The Time Profiler tells me that this line: _lineNode.path = path is the cause of the FPS drop.

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

Thanks for any help! It is greatly appreciated.

PS.我试图完全不使用 SKShapeNode,因为它们似乎无法很好地绘制线条(曲线中的小孔/伪影等)

PS. I'm trying to not use SKShapeNode at all since they seem to not being able to draw the lines too good (Small holes/artifacts in the curves etc.)

截图:

推荐答案

不幸的是,SKShapeNode 不适合您尝试做的事情.但是,有一种方法可以优化这一点,尽管有一些注意事项.

Unfortunately, SKShapeNode is not that great for what you are trying to do. However, there is a way to optimize this, albeit with some caveats.

fps 的第一个最大问题是绘制计数变得非常高,因为您添加的每个线段都是另一个绘制.如果你在 SKView 实例上设置 showsDrawCount,你就会明白我的意思.

First one of the largest problems with the fps is that the draw count gets extremely high because each line segment you add is another draw. If you set showsDrawCount on your SKView instance, you will see what I mean.

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

In this answer Multiple skshapenode in one draw?, you can get more information about how you can use shouldRasterize property of a SKEffectNode to solve the problem if you are drawing something once. If you don't do this, you will have processor time spent on numerous draws each frame.

因此,您可以看到,平局是您无法获得所需性能的主要问题.但是,您似乎希望随着时间的推移始终如一地绘制,所以我要建议的可能对您来说是一个可行的解决方案.

So you can see that the draws is the main issue with you not getting the performance you desire. However, you seem to want to be drawing consistently over time, so what I am going to suggest might be a viable solution for you.

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

The logic of the solution I am suggesting is as such :

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

1 - Create a SKSpriteNode that we can use as a canvas.

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

2 - Create one SKShapeNode that will be used to draw ONLY the current line segment.

3 - 使 SKShapeNode 成为画布的子节点.

3 - Make that SKShapeNode a child of the canvas.

4 - 通过 SKShapeNode

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

5 - Use the SKView method `textureFromNode to save what has currently been drawn on the canvas.

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

6 - set the texture of the canvas to that texture.

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

Loop back to #4 and make a new path for your SKShapeNode for the next line segment.

根据需要重复.

结果应该是您的抽奖次数永远不会超过 2 次,这将解决抽奖次数过多的问题.

The result should be that your draw count will never be higher than 2 draws, which would solve the problem of a high draw count.

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

Basically, you are preserving what has previously been drawn in a texture, therefore only ever needing one SKShapeNode draw for the latest line segment and one draw for the SKTexture.

同样,我还没有尝试过这个过程,如果有任何滞后,那将是 textureFromNode 调用每一帧.如果有什么是你的瓶颈,那就是它!

Again, I have not tried this process yet, and if there is any lag it would be in that textureFromNode call each frame. If anything would be your bottleneck, that would be it!

我今天可能会尝试这个理论,因为我需要 textureFromNode 来解决我正在尝试解决的另一个问题,所以我肯定会找出该方法的快/慢!哈哈

I might try this theory out some time today, as I need textureFromNode for another problem I am trying to solve, and so I'll definitely find out how fast/slow that method is! haha

更新

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

This is not complete code, but is the important parts to achieve the desired drawing performance (60fps) :

基本节点元素有:

container -> 包含所有需要缓存的元素的 SKNode

container -> SKNode that contains all elements that need to be cached

canvas -> SKSpriteNode 将显示绘制段的缓存版本

canvas -> SKSpriteNode that will display the cached version of drawn segments

段池 -> 最初用于绘制段,并根据需要重复使用

pool of segments -> used to draw segments initially, and get reused as needed

首先创建一个 SKShapeNodes 池:

First create a pool of 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 的创建方法:

Next create method for getting a SKShapeNode from pool :

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

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

Next create a method for getting a segment from pool and drawing the line :

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

Next 是一种创建纹理并将现有段返回到池中的方法:

Next is a method for creating a texture and returning existing segments to the pool :

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

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

As I said, this is not all inclusive code, I assume you understand enough about the concept that you can implement into your application. These are just examples of my barebones implementation.

这篇关于Sprite Kit 中的 SKShapeNode 性能不佳的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆