I'm using SpriteKit
and wonder if there is open sourced joystick
somewhere which allows to move only in four directions
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.
Any help really appreciated!!!
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)
}
}