Swift with Spritekit - 带旋钮的操纵杆教程

Swift with Spritekit - Joystick with knob Tutorial

这些天我正在与 Swift 一起编写 SpriteKit 教程。

我用一个旋钮给操纵杆编程,它可以在操纵杆圆圈内的任何地方移动。

这里是相关的代码片段:

for touch in touches {
let position = touch.location(in: joystick)

let length = sqrt(pow(position.y, 2) + pow(position.x, 2))
let angle = atan2(position.y, position.x)

if knobRadius > length {
    joystickKnob.position = position
} else {
    joystickKnob.position = CGPoint(x: cos(angle) * knobRadius, y: sin(angle) * knobRadius)
}}

这是教程中的图片:

但现在我想做一些不同的事情。

我希望旋钮只能像十字一样在 X 轴和 Y 轴上移动 - 就像在导轨上向上、向下、向左、向右移动。

我确实理解代码 - 我认为它更像是一道数学题:)

有人能告诉我如何在 rails 上移动十字内的旋钮吗?

因此您希望将 angle 限制为 .pi / 2 的倍数(90 度)。 atan2 函数 returns 范围 -.pi ... +.pi 中的一个值。将其从弧度转换为四分之一圈(直角)单位,四舍五入为整数,然后再转换回弧度。

let touchAngle = atan2(position.y, position.x) // -.pi ... +.pi
let quarterTurns = touchAngle * 2 / .pi // -2 ... +2
let integralQuarterTurns = quarterTurns.rounded() // -2, -1, 0, +1, or +2
let angle = integralQuarters * .pi / 2

或者,查看 abs(position.x)abs(position.y) 中哪个较大,并用它来确定 angle:

let angle: CGFloat
switch abs(position.x) > abs(position.y) {
case true where position.x > 0: angle = 0
case true where position.x < 0: angle = .pi
case false where position.y > 0: angle = .pi / 2
case false where position.y < 0: angle = .pi / -2
default: angle = 0 // position == .zero so joystick is actually centered
}

为了保持你的角度在十字线上,你需要将角度四舍五入到 pi/2

的间隔

要做到这一点,只需:

angle = (angle * 2 / CGFloat.pi).rounded() * (CGFloat.pi / 2)

下面我修改了您的代码以使其工作得更好,并允许您避免分支。

我使用的是底部半径而不是旋钮半径,这样你就永远不会超过底部。

for touch in touches {
    let position = touch.location(in: joystick)

    let radius = min(baseRadius,sqrt(pow(position.y, 2) + pow(position.x, 2))
    // a nicer way to get radius
    //let radius = min(baseRadius,position.distance(to:CGPoint.zero)) 
    let angle = atan2(position.y, position.x)
    if crossHairStick
    {
        angle = (angle * 2 / CGFloat.pi).rounded() * (CGFloat.pi / 2)
    }
    joystickKnob.position = CGPoint(x: cos(angle) * radius, y: sin(angle) * radius)
}

如果要将旋钮保留在底座内部,请减去旋钮半径。 (这假设两个半径都是正的,否则使用半径的绝对值)

for touch in touches {
    let position = touch.location(in: joystick)

    let radius = min(baseRadius - knobRadius,sqrt(pow(position.y, 2) + pow(position.x, 2))
    let angle = atan2(position.y, position.x)
    if crossHairStick
    {
        angle = (angle * 2 / CGFloat.pi).rounded() * (CGFloat.pi / 2)
    }
    joystickKnob.position = CGPoint(x: cos(angle) * radius, y: sin(angle) * radius)
}