Question

I'm using SpriteKit and wonder if there is open sourced joystick somewhere which allows to move only in four directions

  • left
  • right
  • down
  • up

I understand that there are should be some workaround with angle. I'm wondering if it's possible technically, and found only joysticks which allows circle rotation etc, like this

https://github.com/TheSneakyNarwhal/SpriteKit-Joystick

It looks well enough instead of moving in desired directions... Because user can touch it occasionally and object will move in incorrect direction.

enter image description here

Any help really appreciated!!!

Was it helpful?

Solution

I just created a class that provides exactly what you are looking for. My dpad class can be set up with any amount of directions you like. It inherits from SKNode and is very simple to use. I hope I could help you.

EDIT: The class isn't supported by anymore. Use it adapt it like you want.

//
//  LESKSliderNode.swift
//  LESKClasses
//
//  Created by Cyrill Lippuner on 17.06.14.
//  Copyright (c) 2014 legelite. All rights reserved.
//

import SpriteKit


class LESKSliderNode : SKNode
{
/**
Defines the Size of the LESKSliderNode.
*/
var size : CGSize = CGSize(width: 0, height: 0)
/**
Defines the AnchorPoint of the LESKSliderNode.
*/
//var anchorPoint : CGPoint = CGPoint(x:0.5,y:0.5)
/**
Defines frameInParent with the position of the superclass and the size of the LESKSliderNode.
*/
var frameInParent : CGRect
{
get {return CGRect(origin: CGPoint(x:self.position.x - 0.5 * self.size.width,y:self.position.y - 0.5 * self.size.height), size: self.size)}
set(newValue)
{
    super.position = newValue.origin
    self.size = newValue.size
    //self.value = self.valueRange.startIndex + ((newPositionX + range.endIndex) / (range.endIndex - range.startIndex)) * (self.valueRange.endIndex - self.valueRange.startIndex)
}
}

/**
Enables the LESKSliderNode to interactions.
*/
var isEnabled : Bool = true
/**
Displays whether a touch is in progress.
*/
var isActive : Bool = false
/**
Defines the space between the thumb and the edges of the scale.
*/
var overlayThumb : Bool = false {didSet{calculateNewThumbRange()}}
/**
Displays the value of thumb on the slider.
*/
var value : Float
{
get
{
    return  self.valueRange.startIndex + ((thumbSprite.position.x + self.thumbRange.endIndex) / (self.thumbRange.endIndex - self.thumbRange.startIndex)) * (self.valueRange.endIndex - self.valueRange.startIndex)
}
set(newValue)
{
    var val = newValue
    if newValue < self.valueRange.startIndex {val = self.valueRange.startIndex}
    else if newValue > self.valueRange.endIndex {val = self.valueRange.endIndex}
    let newPositionX = (val - self.valueRange.startIndex) * (self.thumbRange.endIndex - self.thumbRange.startIndex) / (self.valueRange.endIndex - self.valueRange.startIndex) - self.thumbRange.endIndex
    thumbSprite.position = CGPoint(x:newPositionX,y:thumbSprite.position.y)
    if self.thumbSpriteActive {self.thumbSpriteActive!.position = CGPoint(x:newPositionX,y:self.thumbSpriteActive!.position.y)}
}
}
/**
Defines the range of the values.
*/
var valueRange : Range<Float> = Range(start: 0.0, end: 1.0)
/**
The range of the thumb's position.
*/
var thumbRange : Range<Float> = Range(start: 0.0, end: 0.0)
/**
The range of the thumb's position.
*/
var thumbOffset : Float = 0.0
{
didSet
{
    self.thumbSprite.position = CGPoint(x:self.thumbSprite.position.x, y: self.thumbOffset)
    if self.thumbSpriteActive {self.thumbSpriteActive!.position = CGPoint(x:self.thumbSpriteActive!.position.x, y: self.thumbOffset)}
}
}


/**
ScaleSprite is the scale for the LESKSliderNode.
*/
let scaleSprite : SKSpriteNode
/**
ThumbSprite is the thumb for the LESKSliderNode.
*/
let thumbSprite : SKSpriteNode
/**
ScaleSpriteActive is the active scale for the LESKSliderNode.
*/
let scaleSpriteActive : SKSpriteNode?
/**
ThumbSpriteActive is the active thumb for the LESKSliderNode.
*/
let thumbSpriteActive : SKSpriteNode?

/**
Description of the LESKSliderNode
*/
override var description : String
{
get
{
    var string = "<LESKSliderNode> name: \(self.name) "
    string += "scaleSprite: [\(scaleSprite.description)] "
    string += "thumbSprites: [\(thumbSprite.description)] "
    string += "frame: \(self.frameInParent) rotation: \(self.zRotation) "
    string += "isEnabled: \(isEnabled) "
    if isEnabled {string += "isActive: \(isActive) overlayThumb: \(overlayThumb) range: \(valueRange) value: \(value)"}
    return string
}
}

/**
Typealiases
*/
typealias LESKSliderNodeCompletion = ((slider: LESKSliderNode, value: Float) -> ())
/**
Closure, which is executed when a touch begins
*/
var touchDown : LESKSliderNodeCompletion?
/**
Closure, which is executed when the thumb is dragged
*/
var touchMoved : LESKSliderNodeCompletion?
/**
Closure, which is executed when a touch ends
*/
var touchUp : LESKSliderNodeCompletion?
/**
Closure, which is executed when a touch ends inside the frame of the LESKSliderNode
*/
var touchUpInside : LESKSliderNodeCompletion?
/**
Closure, which is executed when a touch is cancelled
*/
var touchCancelled : LESKSliderNodeCompletion?


/**
Initializer
@param the string of the thumbSprite
@param the string of the scaleSprite
*/
convenience init(thumbString: String, scaleString: String)
{
    self.init(thumbSprite: SKSpriteNode(imageNamed: thumbString), scaleSprite: SKSpriteNode(imageNamed: scaleString), thumbSpriteActive: nil, scaleSpriteActive: nil)
}

/**
Initializer
@param the string of the thumbSprite
@param the string of the scaleSprite
@param the string of the thumbSpriteActive
@param the string of the scaleSpriteActive
*/
convenience init(thumbString: String, scaleString: String, thumbStringActive: String?, scaleStringActive: String?)
{
    self.init(thumbSprite: SKSpriteNode(imageNamed: thumbString), scaleSprite: SKSpriteNode(imageNamed: scaleString), thumbSpriteActive: SKSpriteNode(imageNamed: thumbStringActive), scaleSpriteActive: SKSpriteNode(imageNamed: scaleStringActive))
}


/**
Initializer
@param the texture of the thumbSprite
@param the texture of the scaleSprite
*/
convenience init(thumbTexture: SKTexture, scaleTexture: SKTexture)
{
    self.init(thumbSprite: SKSpriteNode(texture: thumbTexture), scaleSprite: SKSpriteNode(texture: scaleTexture), thumbSpriteActive: nil, scaleSpriteActive: nil)
}

/**
Initializer
@param the texture of the thumbSprite
@param the texture of the scaleSprite
@param the texture of the thumbSpriteActive
@param the texture of the scaleSpriteActive
*/
convenience init(thumbTexture: SKTexture, scaleTexture: SKTexture, thumbTextureActive: SKTexture?, scaleTextureActive: SKTexture?)
{
    self.init(thumbSprite: SKSpriteNode(texture: thumbTexture), scaleSprite: SKSpriteNode(texture: scaleTexture), thumbSpriteActive: SKSpriteNode(texture: thumbTextureActive), scaleSpriteActive: SKSpriteNode(texture: scaleTextureActive))
}

/**
Initializer
@param the sprite of the thumbSprite
@param the sprite of the scaleSprite
*/
convenience init(thumbSprite: SKSpriteNode, scaleSprite: SKSpriteNode)
{
    self.init(thumbSprite: thumbSprite, scaleSprite: scaleSprite)
}

/**
Initializer
@param the sprite of the thumbSprite
@param the sprite of the scaleSprite
@param the sprite of the thumbSpriteActive
@param the sprite of the scaleSpriteActive
*/
init(thumbSprite: SKSpriteNode, scaleSprite: SKSpriteNode, thumbSpriteActive: SKSpriteNode?, scaleSpriteActive: SKSpriteNode?)
{
    self.thumbSprite = thumbSprite
    self.scaleSprite = scaleSprite
    self.thumbSpriteActive = thumbSpriteActive
    self.scaleSpriteActive = scaleSpriteActive
    super.init()
    self.userInteractionEnabled = true
    self.addChild(self.scaleSprite)
    self.addChild(self.thumbSprite)
    if self.scaleSpriteActive?
    {
        self.addChild(self.scaleSpriteActive)
        self.scaleSpriteActive!.hidden = true
    }
    if self.thumbSpriteActive?
    {
        self.addChild(self.thumbSpriteActive)
        self.thumbSpriteActive!.hidden = true
    }
    calculateNewThumbRange()
    self.size = scaleSprite.size
}

override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!)
{
    if isEnabled
    {
        isActive = true
        if self.scaleSpriteActive?
        {
            self.scaleSprite.hidden = true
            self.scaleSpriteActive!.hidden = false
        }
        if self.thumbSpriteActive?
        {
            self.thumbSprite.hidden = true
            self.thumbSpriteActive!.hidden = false
        }
        moveThumbToValueAccordingToTouch(touches.anyObject() as UITouch)
        if touchDown? {touchDown!(slider: self, value: self.value)}
    }
}

override func touchesMoved(touches: NSSet!, withEvent event: UIEvent!)
{
    if isEnabled
    {
        let touchPosition = (touches.anyObject() as UITouch).locationInNode(self.parent)
        if CGRectContainsPoint(self.frameInParent, touchPosition)
        {
            if self.scaleSpriteActive?
            {
                self.scaleSprite.hidden = true
                self.scaleSpriteActive!.hidden = false
            }
        }
        else
        {
            if self.scaleSpriteActive?
            {
                self.scaleSprite.hidden = false
                self.scaleSpriteActive!.hidden = true
            }
        }
        moveThumbToValueAccordingToTouch(touches.anyObject() as UITouch)
        if touchMoved? {touchMoved!(slider: self, value: self.value)}
    }
}

override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!)
{
    if isEnabled
    {
        isActive = false
        if self.scaleSpriteActive?
        {
            self.scaleSprite.hidden = false
            self.scaleSpriteActive!.hidden = true
        }
        if self.thumbSpriteActive?
        {
            self.thumbSprite.hidden = false
            self.thumbSpriteActive!.hidden = true
        }
        if touchUp? {touchUp!(slider: self, value: self.value)}
        let touchPosition = (touches.anyObject() as UITouch).locationInNode(self.parent)
        if CGRectContainsPoint(self.frameInParent, touchPosition) {if touchUpInside? {touchUpInside!(slider: self, value: self.value)}}
    }
}

override func touchesCancelled(touches: NSSet!, withEvent event: UIEvent!)
{
    if isEnabled
    {
        isActive = false
        if self.scaleSpriteActive?
        {
            self.scaleSprite.hidden = false
            self.scaleSpriteActive!.hidden = true
        }
        if self.thumbSpriteActive?
        {
            self.thumbSprite.hidden = false
            self.thumbSpriteActive!.hidden = true
        }
        if touchCancelled? {touchCancelled!(slider: self, value: self.value)}
    }
}

func moveThumbToValueAccordingToTouch(touch: UITouch)
{
    let touchPosition = touch.locationInNode(self)
    var newPositionX = touchPosition.x
    if newPositionX < self.thumbRange.startIndex {newPositionX = self.thumbRange.startIndex}
    else if newPositionX > self.thumbRange.endIndex {newPositionX = self.thumbRange.endIndex}
    self.thumbSprite.position = CGPoint(x:newPositionX,y:self.thumbSprite.position.y)
    if self.thumbSpriteActive {self.thumbSpriteActive!.position = CGPoint(x:newPositionX,y:self.thumbSpriteActive!.position.y)}
}

func calculateNewThumbRange()
{
    self.thumbRange = (self.overlayThumb) ? Range(start: -scaleSprite.size.width/2, end: scaleSprite.size.width/2) : Range(start: -(scaleSprite.size.width / 2 - thumbSprite.size.width / 2), end: scaleSprite.size.width / 2 - thumbSprite.size.width / 2)
}
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top