Swift with Spritekit - Джойстик с ручкой Учебное пособие - PullRequest
0 голосов
/ 04 мая 2018

В эти дни я работаю над учебником по SpriteKit со Swift.

Я запрограммировал джойстик с помощью ручки, которую можно перемещать в любом месте внутри круга джойстика.

Вот соответствующий фрагмент кода:

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)
}}

Вот изображение из учебника:

enter image description here

Но теперь я хочу сделать это немного по-другому.

Я хочу, чтобы ручку можно было перемещать только по осям X и Y, как крест - как вверх, вниз, влево, вправо на направляющей.

enter image description here

Я действительно понимаю код - я думаю, что это больше похоже на математическую задачу:)

Может кто-нибудь сказать мне, как двигать ручку внутри креста на рельсах?

Ответы [ 2 ]

0 голосов
/ 04 мая 2018

Чтобы сохранить угол на перекрестии, вам нужно округлить угол до интервала пи / 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)
}
0 голосов
/ 04 мая 2018

Итак, вы хотите ограничить angle кратным .pi / 2 (90 градусов). Функция atan2 возвращает значение в диапазоне -.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
}
...