如何创建一个波形路径Swift

时间:2015-12-26 18:58:56

标签: sprite-kit swift2 cgpath sin

我希望我的节点以正弦曲线波形运行,我尝试将其用于CGPath。 如何创建遵循正弦曲线的CGPath?除了手动找到曲线上的点之外还有其他方法,还是我可以传入一个正弦函数?

let action = SKAction.followPath(<the sine path>, asOffset: true, orientToPath: true, duration: 5)

这可以通过Bezier Paths完成,然后转换为CGPaths吗? 感谢。

1 个答案:

答案 0 :(得分:8)

不,没有内置方法来从函数构建路径,但您可以轻松编写自己的路径。在Swift 3中:

/// Build path within rectangle
///
/// Given a `function` that converts values between zero and one to another values between zero and one, this method will create `UIBezierPath` within `rect` using that `function`.
///
/// - parameter rect:      The `CGRect` of points on the screen.
///
/// - parameter count:     How many points should be rendered. Defaults to `rect.size.width`.
///
/// - parameter function:  A closure that will be passed an floating point number between zero and one and should return a return value between zero and one as well.

private func path(in rect: CGRect, count: Int? = nil, function: (CGFloat) -> (CGFloat)) -> UIBezierPath {
    let numberOfPoints = count ?? Int(rect.size.width)

    let path = UIBezierPath()
    path.move(to: convert(point: CGPoint(x: 0, y: function(0)), in: rect))
    for i in 1 ..< numberOfPoints {
        let x = CGFloat(i) / CGFloat(numberOfPoints - 1)
        path.addLine(to: convert(point: CGPoint(x: x, y: function(x)), in: rect))
    }
    return path
}

/// Convert point with x and y values between 0 and 1 within the `CGRect`.
///
/// - parameter point:  A `CGPoint` value with x and y values between 0 and 1.
/// - parameter rect:   The `CGRect` within which that point should be converted.

private func convert(point: CGPoint, in rect: CGRect) -> CGPoint {
    return CGPoint(
        x: rect.origin.x + point.x * rect.size.width,
        y: rect.origin.y + rect.size.height - point.y * rect.size.height
    )
}

所以,让我们传递一个函数,当它在width的{​​{1}}中前进时,它会执行一条正弦曲线:

rect

注意,上面假设你想要从左到右遍历,构建由函数定义的路径。您还可以执行更多参数化再现:

func sinePath(in rect: CGRect, count: Int? = nil) -> UIBezierPath {
    // note, since sine returns values between -1 and 1, let's add 1 and divide by two to get it between 0 and 1
    return path(in: rect, count: count) { (sin($0 * .pi * 2.0) + 1.0) / 2.0 }
}

然后,您可以使用正弦曲线修改/// Build path within rectangle /// /// Given a `function` that converts values between zero and one to another values between zero and one, this method will create `UIBezierPath` within `rect` using that `function`. /// /// - parameter rect: The `CGRect` of points on the screen. /// /// - parameter count: How many points should be rendered. Defaults to `rect.size.width` or `rect.size.width`, whichever is larger. /// /// - parameter function: A closure that will be passed an floating point number between zero and one and should return a `CGPoint` with `x` and `y` values between 0 and 1. private func parametricPath(in rect: CGRect, count: Int? = nil, function: (CGFloat) -> (CGPoint)) -> UIBezierPath { let numberOfPoints = count ?? max(Int(rect.size.width), Int(rect.size.height)) let path = UIBezierPath() let result = function(0) path.move(to: convert(point: CGPoint(x: result.x, y: result.y), in: rect)) for i in 1 ..< numberOfPoints { let t = CGFloat(i) / CGFloat(numberOfPoints - 1) let result = function(t) path.addLine(to: convert(point: CGPoint(x: result.x, y: result.y), in: rect)) } return path } 坐标,只需递增x

y

这样做的好处是你现在也可以定义你想要的任何路径,例如:一个螺旋:

func verticalSinePath(in rect: CGRect, count: Int? = nil) -> UIBezierPath {
    // note, since sine returns values between -1 and 1, let's add 1 and divide by two to get it between 0 and 1
    return parametricPath(in: rect, count: count) { CGPoint(
        x: (sin($0 * .pi * 2.0) + 1.0) / 2.0,
        y: $0
    ) }
}

以下是上面的Swift 2演绎:

func spiralPath(in rect: CGRect, count: Int? = nil) -> UIBezierPath {
    return parametricPath(in: rect, count: count) { t in
        let r = 1.0 - sin(t * .pi / 2.0)
        return CGPoint(
            x: (r * sin(t * 10.0 * .pi * 2.0) + 1.0) / 2.0,
            y: (r * cos(t * 10.0 * .pi * 2.0) + 1.0) / 2.0
        )
    }
}