【问题标题】:How to Create Guitar-like string using Swift and SpriteKit如何使用 Swift 和 SpriteKit 创建类似吉他的字符串
【发布时间】:2017-05-11 17:10:44
【问题描述】:

我想创建一个类似吉他的弦乐效果,类似于Les Paul's 96th Birthday Google Doodle 中的效果。我想承认我已经看到了一些使用 Javascript 和 CSS 来解决这个问题的类似问题。我对使用纯粹的 Swift 和 SpriteKit 很感兴趣。我将不胜感激有关如何实现这一目标的任何建议。

【问题讨论】:

    标签: swift swift3 sprite-kit game-physics


    【解决方案1】:

    您可以使用SKShapeNode 为您的吉他弦建模。在下面的示例代码中,我创建了一个响应触摸事件并将其转发到其子字符串节点的场景。使用余弦波函数和应用幅度衰减来模拟拉丝性。

    对于示例,我通过在字符串的开始位置、弹拨位置和结束位置之间添加线条来创建一个简单的路径。您可以使用样条曲线创建一条更平滑的线(一种方法是构建一个穿过这些点的 Catmull Rom 样条,并将其转换为可以直接使用SKShapeNode 渲染的贝塞尔样条)。

    在计算振幅时,我还控制您可以拨弦的最大量。在真正的琴弦中,琴弦末端的弯曲量将远小于琴弦中间的弯曲量。您可以使用一些应用于幅度的斜坡函数来对此进行建模。

    您可以使用在update 中传递给cos 函数的参数来控制琴弦的振动频率。请注意,我为此示例硬编码了时间增量。

    调整上述参数将允许您将弹拨效果固定到您想要的配置。

    import UIKit
    import SpriteKit
    import PlaygroundSupport
    
    
    let view = SKView(frame: .init(origin: .zero, size: .init(width: 300, height: 300)))
    PlaygroundPage.current.liveView = view
    
    class GuitarString : SKShapeNode {
    
        let start: CGPoint
        let end: CGPoint
        var intermediate: CGPoint = .zero
        var amplitude: CGFloat = 0.0
    
        required init(start: CGPoint, end: CGPoint) {
            self.start = start
            self.end = end
            self.intermediate = start
            let path = CGMutablePath()
            path.move(to: start)
            path.addLine(to: end)
            super.init()
            self.path = path
            self.strokeColor = .red
            self.lineWidth = 2.0
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
    
        func pluckStringBegan(at point: CGPoint) {
            intermediate = point
            amplitude = min(abs(point.y - start.y), 100) * (point.y - start.y < 0 ? -1 : 1)
            t = 0
        }
    
    
    
        var t:CGFloat = 0.0
    
        func update(dt: CGFloat) {
            amplitude *= 0.95
            t += dt
            let a = amplitude * cos(t * 10)
            // to get a smoother line, you might 
            // consider creating a catmull rom spline
            // that passes through start, f and end
            // and convert that to a bezier curve
            let path = CGMutablePath()
            path.move(to: start)
            let f = CGPoint(x: intermediate.x, y: a + start.y)
            path.addLine(to: f)
            path.addLine(to: end)
            self.path = path
        }
    
    }
    
    class GuitarScene : SKScene {
        override init(size: CGSize) {
            super.init(size: size)
            self.isUserInteractionEnabled = true
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            guard let t = touches.first else { return }
            let p = t.location(in: self.children[0])
            (self.children[0] as! GuitarString).pluckStringBegan(at: p)
        }
    
        override func update(_ currentTime: TimeInterval) {
            (self.children[0] as! GuitarString).update(dt: 1/20)
        }
    }
    
    let scene = GuitarScene(size: view.frame.size)
    scene.scaleMode = .aspectFit
    view.presentScene(scene)
    
    let string = GuitarString(start: .init(x: 0, y: 150), end: .init(x: 300, y: 150))
    
    string.position = .zero
    string.zPosition = 100
    scene.addChild(string)
    

    【讨论】:

      猜你喜欢
      • 2016-05-07
      • 1970-01-01
      • 2014-08-31
      • 2017-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-23
      相关资源
      最近更新 更多