iossprite-kitskspritenodetouchesmoved

Swift with Spritekit - Joystick with knob Tutorial


Im working on an SpriteKit Tutorial with Swift these days.

I programmed Joystick with a knob, that can be moved everywhere within the joystick circle.

Heres the relevant code snippet:

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

Here is a picture from the tutorial:

enter image description here

But now I want to do this a little different.

I want that the Knob only can be moved on the X and Y axis like a cross - like Up, Down, Left, right on a rail.

enter image description here

I really do understand the code - I think its more like a math problem :)

Can somebody tell me how to move the knob within a cross on rails?


Solution

  • To keep your angle on the cross hairs, you need to round the angle to an interval of pi/2

    to do that, just do:

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

    Below I have modified your code to work a lot nicer, and allows you to avoid the branching.

    I am using the base radius instead of the knob radius, so that you can never exceed the base.

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

    If you want to keep the knob inside of the base, subtract the knob radius. (This assumes both radius are positive, otherwise use absolute value of the 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)
    }