创建没有丢帧的无尽 cgpath

Posted

技术标签:

【中文标题】创建没有丢帧的无尽 cgpath【英文标题】:Create endless cgpath without framedrops 【发布时间】:2015-08-11 10:36:48 【问题描述】:

我需要不断地创建一个 cgpath。目前我是这样做的:

 func createLine()
        var rand = randomBetweenNumbers(1, 2)
        currentY--
        if rand < 1.5
            currentX--
            CGPathAddLineToPoint(leftPath, nil, currentX, currentY)
        else
            currentX++
            CGPathAddLineToPoint(leftPath, nil, currentX, currentY)
        
        CGPathAddLineToPoint(rightPath, nil, currentX+tileSize, currentY)
        lineNode.path = leftPath
        rightNode.path = rightPath


然后这样称呼它:

NSTimer.scheduledTimerWithTimeInterval(0.05, target: self, selector: Selector("startTile"), userInfo: nil, repeats: true)

但问题是,随着时间的推移,帧数会越来越低。有什么我必须改变的,以使帧率不再下降吗?

我的目标是创建一条随机的无限路径。

【问题讨论】:

我猜您需要转储路径的旧部分。否则,您最终会拥有越来越多的路径段。或者,为什么不在每次到达当前路径的末端时创建一个新路径? 我无法真正创建新路径,因为我需要它是无止境的,或者至少新路径对玩家不可见。 这应该有助于line caching 你能不能把你的路径点放在一个数组中,然后画出应该在屏幕上实际可见的东西?顺便说一句,尽管有NSTimer,您可能更喜欢使用CADisplayLink,因为它会与显示刷新同步触发。 【参考方案1】:

在绘制线数逐渐增加的同时保持高 FPS 计数的关键是快速达到在场景中添加更多线对帧速率影响很小或没有影响的状态。至少有两种方法可以做到这一点。

两者中最直接的方法是定期将先前绘制的线条转换为SKTexture,并将结果显示为SKSpriteNode 的纹理。步骤如下:

    创建一个SKNode 用作行容器 创建将用作线条画布的SKSpriteNode 创建一个SKShapeNode 用于绘制新线 将容器添加到场景中,并将画布和形状节点添加到容器中 使用形状节点的path 属性绘制一组连接线段 当行数达到预定值时,将容器的内容转换为“SKTexture” 将画布的纹理属性设置为SKTexture。请注意,由于画布也是容器的子项,其内容也将添加到纹理中 起泡、冲洗、重复步骤 5 - 7

这是一个在 Swift 中的示例实现,它在 iPhone 6 设备上以 60 FPS 的速度绘制无穷无尽的线条(您应该在不使用模拟器的设备上测试性能):

class GameScene: SKScene 
    // 1. Create container to hold new and old lines
    var lineContainer = SKNode()
    // 2. Create canvas
    var lineCanvas:SKSpriteNode?
    // 3. Create shape to draw new lines
    var lineNode = SKShapeNode()

    var lastDrawTime:Int64 = 0
    var lineCount = 0
    var timeScan:Int64 = 0
    var path = CGPathCreateMutable()

    var lastPoint = CGPointZero

    override func didMoveToView(view:SKView) 
        scaleMode = .ResizeFill

        // 4. Add the container to the scene and the canvas to the container 
        addChild(lineContainer)
        lineCanvas = SKSpriteNode(color:SKColor.clearColor(),size:view.frame.size)
        lineCanvas!.anchorPoint = CGPointZero
        lineCanvas!.position = CGPointZero
        lineContainer.addChild(lineCanvas!)
        lastPoint = CGPointMake(view.frame.size.width/2.0, view.frame.size.height/2.0)
    

    // Returns a random value in the specified range
    func randomInRange(minValue:CGFloat, maxValue:CGFloat) -> CGFloat 
        let r = CGFloat(Double(arc4random_uniform(UInt32.max))/Double(UInt32.max))
        return (maxValue-minValue) * r + minValue
    

    func drawLine() 
        if (CGPathIsEmpty(path)) 
            // Create a new line that starts where the previous line ended
            CGPathMoveToPoint(path, nil, lastPoint.x, lastPoint.y)
            lineNode.path = nil
            lineNode.lineWidth = 1.0
            lineNode.strokeColor = SKColor.blueColor()
            lineNode.zPosition = 100
            lineContainer.addChild(lineNode)
        
        // Add a random line segment
        let x = randomInRange(size.width*0.1, maxValue: size.width*0.9)
        let y = randomInRange(size.height*0.1, maxValue: size.height*0.9)
        CGPathAddLineToPoint(path, nil, x, y)
        lineNode.path = path
        // Save the current point so we can connect the next line to the end of the last line
        lastPoint = CGPointMake(x, y)
    

    override func update(currentTime: CFTimeInterval) 
        let lineDrawTime = timeScan / 10
        // 5. Draw a new line every 10 updates. Increment line count
        if (lineDrawTime != lastDrawTime) 
            drawLine()
            ++lineCount
        
        // 6. and 7. Add all newly and previously drawn lines to the canvas
        if (lineCount == 8) 
            addLinesToTexture()
            lineCount = 0
        
        lastDrawTime = lineDrawTime
        ++timeScan
    

    func addLinesToTexture () 
        // Convert the contents of the line container to an SKTexture
        let texture = self.view!.textureFromNode(lineContainer)
        // Display the texture
        lineCanvas!.texture = texture
        // Start a new line
        lineNode.removeFromParent()
        path = CGPathCreateMutable()
    

【讨论】:

如果您希望该行“淡出”,以使每个部分段的 alpha 值比它前面的部分多一点,该怎么办?这个答案在那种情况下仍然适用吗?你暗示的难以捉摸的替代技术怎么样? @Andrew 这种方法适用于绘制大量持久线的游戏。我建议您创建一个可变的 SKShapeNode 行数组,这些行会随着时间的推移而淡出(通过更改 strokeColor 属性)。

以上是关于创建没有丢帧的无尽 cgpath的主要内容,如果未能解决你的问题,请参考以下文章

使用 CSS3 动画确定丢帧的原因

dp83640丢帧

如何创建一个随机封闭平滑CGPath?

如何从文件创建 CGPath - 即 SVG

视频丢帧(详解)

从多个 CGpath 构建 CAShapeLayer