SpriteKit:如何为圆角矩形的绘制设置动画

SpriteKit: How to animate drawing of a rounded rectangle

我想为圆角框的绘图设置动画。我看到一个人用 CGLinePath 画房子形状的答案。老实说,我认为它很整洁。

我对 Core Animation 以及如何在 SKScene 中实现知之甚少。我不知道你会添加什么样的东西。我已经看到了几个不同的解决方案来使用 Core Animation 到达我想去的地方,但这并不能帮助我在场景中将我的圆角矩形绘制为节点。

我看过这个,有人用 Core Animation 画了一个圆,然后把它加载到 UIView 中:Animate drawing of a circle

不过,我希望它成为一个节点,因此我可以对其应用一些 SKAction

在动作中使用 SKShapeNode。

这详细介绍了带有 skshapenode 的圆角矩形:SKSpriteNode - create a round corner node?

现在只需用一个动作来激活变化。

添加到答案中,因为 OP 明确要求随着时间的推移绘制笔划长度动画,并指出没有 SKAction。相反,您可以通过提供基于 SKShader 的 propertiesv_path_distanceu_path_length 中的一些输出的自定义 strokeShader 来设置 SKShapeNode 路径的动画。请注意,在下面提供的着色器中,u_current_percentage 是由我们添加的,指的是我们想要向上描边的路径中的当前点。由此,场景决定了动画抚摸的速度。另请注意,strokeShader 作为片段着色器,在每一步输出一个 RGB,它允许您随时控制颜色,如果您愿意,可以使描边成为渐变颜色。

着色器作为文件添加到 Xcode 项目 animateStroke.fsh:

void main()
{
    if ( u_path_length == 0.0 ) {
        gl_FragColor = vec4( 0.0, 0.0, 1.0, 1.0 ); // draw blue // this is an error
    } else if ( v_path_distance / u_path_length <= u_current_percentage ) {
        gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); // draw red
    } else {
        gl_FragColor = vec4( 0.0, 0.0, 0.0, 0.0 ); // draw nothing
    }
}

以及使用它的示例 SKScene 子类:

import SpriteKit
import GameplayKit


func shaderWithFilename( _ filename: String?, fileExtension: String?, uniforms: [SKUniform] ) -> SKShader {
    let path = Bundle.main.path( forResource: filename, ofType: fileExtension )
    let source = try! NSString( contentsOfFile: path!, encoding: String.Encoding.utf8.rawValue )
    let shader = SKShader( source: source as String, uniforms: uniforms )
    return shader
}

class GameScene: SKScene {

    let strokeSizeFactor = CGFloat( 2.0 )
    var strokeShader: SKShader!
    var strokeLengthUniform: SKUniform!
    var _strokeLengthFloat: Float = 0.0
    var strokeLengthKey: String!
    var strokeLengthFloat: Float {
        get {
            return _strokeLengthFloat
        }
        set( newStrokeLengthFloat ) {
            _strokeLengthFloat = newStrokeLengthFloat
            strokeLengthUniform.floatValue = newStrokeLengthFloat
        }
    }

    override func didMove(to view: SKView) {

        strokeLengthKey = "u_current_percentage"
        strokeLengthUniform = SKUniform( name: strokeLengthKey, float: 0.0 )
        let uniforms: [SKUniform] = [strokeLengthUniform]
        strokeShader = shaderWithFilename( "animateStroke", fileExtension: "fsh", uniforms: uniforms )
        strokeLengthFloat = 0.0

        let cameraNode = SKCameraNode()
        self.camera = cameraNode

        // make your rounded rect
        let path = CGMutablePath()
        path.addRoundedRect(in: CGRect( x: 0, y: 0, width: 200, height: 150 ), cornerWidth: 35, cornerHeight: 35, transform: CGAffineTransform.identity )

        // note that prior to a fix in iOS 10.2, bug #27989113  "SKShader/SKShapeNode: u_path_length is not set unless shouldUseLocalStrokeBuffers() is true" requires that a path be "long" - past a certain number of points - I don't know how many. So if the rounded rect doesn't work for you on iOS 10 - iOS 10.1, try this path instead: 
        // let path1 = CGMutablePath()
        // path1.move( to: CGPoint.zero )
        // path1.addLine( to: CGPoint( x: 0, y: strokeHeight ) )
        // for i in 0...15 {
        //    path.addLine( to: CGPoint( x: 0, y: strokeHeight + CGFloat( 0.001 ) * CGFloat( i ) ) )
        // }            
        // path.closeSubpath()            

        let strokeWidth = 17.0 * strokeSizeFactor
        let shapeNode = SKShapeNode( path: path )
        shapeNode.lineWidth = strokeWidth
        shapeNode.lineCap = .round
        shapeNode.addChild( cameraNode )
        shapeNode.strokeShader = strokeShader
        shapeNode.calculateAccumulatedFrame()
        addChild( shapeNode )

        // center the camera
        cameraNode.position = CGPoint( x: shapeNode.frame.size.width/2.0, y: shapeNode.frame.size.height/2.0 )


    }

    override func update(_ currentTime: TimeInterval) {

        // the increment chosen determines how fast the path is stroked. Note this maps to "u_current_percentage" within animateStroke.fsh
        strokeLengthFloat += 0.01
        if strokeLengthFloat > 1.0 {
            strokeLengthFloat = 0.0
        }

    }
}