Les Paul's 96th Birthday Google Doodle과 비슷한 기타 모양의 문자열 효과를 만들고 싶습니다. Javascript와 CSS를 사용하여 이와 비슷한 접근법을 몇 가지 보았습니다. 순전히 Swift 및 SpriteKit 사용에 관심이 있습니다. 내가 이걸 어떻게 성취 할 수 있는지 조언 해 주시면 감사하겠습니다.Swift 및 SpriteKit을 사용하여 기타와 유사한 문자열을 만드는 방법
2
A
답변
2
SKShapeNode
을 사용하여 기타 문자열을 모델링 할 수 있습니다. 아래 샘플 코드에서는 자식 문자열 노드로 전달하는 터치 이벤트에 응답하는 장면을 만들었습니다. stringiness는 코사인 파 함수와 적용되는 진폭의 감쇠를 사용하여 모델링됩니다.
샘플의 경우 문자열의 시작, 뽑기 및 끝 위치 사이에 줄을 추가하여 간단한 경로를 만들었습니다. 스플라인 커브를 사용하여 매끄러운 선을 만들 수 있습니다 (그 중 하나는 Catmull Rom 스플라인을 구성하여 해당 점을 통과시키고이를 SKShapeNode
으로 직접 렌더링 할 수있는 베 지어 스플라인으로 변환하는 것입니다).
또한 진폭을 계산할 때 문자열을 뽑을 수있는 최대 양을 제어합니다. 실제 문자열에서 문자열의 끝으로 향하는 플렉스의 양은 문자열 중간의 플렉스 양보다 훨씬 적습니다. 진폭에 적용되는 일부 램핑 함수를 사용하여이를 모델링 할 수 있습니다.
cos
함수에 전달 된 매개 변수를 사용하여 update
의 문자열 진동 빈도를 제어 할 수 있습니다. 이 예제에서는 시간 델타를 하드 코딩했습니다.
위의 매개 변수를 조정하면 원하는 구성으로 뽑아 낼 수 있습니다.
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)