Rethinking a Classic Directional Control Paradigm using Swift

Over the past few weeks I’ve been talking with friend and ultra-talented designer Logan Faerber about building an RPG game for iOS. Navigation in the game world would revolve around vertical and horizontal axes, so the D-Pad paradigm naturally occurred to me (think Legend of Zelda Oracle of Seasons, or Pokemon). Building the game would involve a level of time commitment complexity beyond your typical e-commerce or social app, but I thought hey, why not get started anyway? So I wrote a DPad. Here it is:



My DPad is open source. I’m hoping that others will find it useful in their own games, and that people will file issues and submit pull requests to improve it. Check out the code here.

How to Use it:

First, set up a SpriteKit project using the standard “Game” template.

Then, in your SKScene instance, import the DPad module: import DPad.

Initialize your DPad using the new constructor, optionally setting its position. A recommended pattern is to initialize your DPad in touchesBegan:

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
   /* Called when a touch begins */
    
    for touch in touches {
        let location = touch.locationInNode(self)

        if dPad == nil {
            dPad = DPad.new(supportedDirections: [.Up, .Down, .Left, .Right])

            guard let dPad = dPad else {
                print("Failed to create D-Pad")
                return
            }
            self.addChild(dPad)
        }

        dPad?.position = location
    }
}

Then, to keep track of the DPad’s position, override your SKScene’s update function and switch on the current Direction of your DPad:

override func update(currentTime: CFTimeInterval) {
    super.update(currentTime)
    motion?.invalidate()
    motion = nil
    let direction = dPad?.direction ?? DPad.Direction.None
    switch direction {
        case .None:
            break
        case .Up:
            motion = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "moveUp", userInfo: nil, repeats: true)
        case .Down:
            motion = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "moveDown", userInfo: nil, repeats: true)
        case .Left:
            motion = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "moveLeft", userInfo: nil, repeats: true)
        case .Right:
            motion = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "moveRight", userInfo: nil, repeats: true)
    }
}

func moveUp() {
    character?.position = CGPointMake(character!.position.x, character!.position.y + 1)
}

func moveDown() {
    character?.position = CGPointMake(character!.position.x, character!.position.y - 1)
}

func moveLeft() {
    character?.position = CGPointMake(character!.position.x - 1, character!.position.y)
}

func moveRight() {
    character?.position = CGPointMake(character!.position.x + 1, character!.position.y)
}

It’s as simple as that. Help me improve this DPad’s usability by filing issues at the GitHub repository or reaching out to me on Twitter