2

I'm adding an old-school high-score entry screen to my game, in which the users tap each letter to enter their name.

enter image description here

Each letter, symbol or phrase ('DEL', 'SP' etc) is a single SKLabelNode and it's very difficult to tap on the ',' and '.' characters and some of the symbols though. Each tap is detected via the usual touchesBegan

To make detention easier I plan on placing a larger node behind each letter and update touchesBegan to either detect a touch on the label itself or on this other label, in which case just use the child node for the touch (which will be the letter or phrase).

Before I do this I was wondering if there is an easy way to achieve what I want, possible by detecting the nearest SKLabelNode to a touch etc.

Steve Ives
  • 7,894
  • 3
  • 24
  • 55
  • Just wondering that if you just make the 'frame' of those two nodes same like the other labels, your touch should work. Are the frame sizes smaller here ? – Tushar Feb 03 '17 at 14:57
  • @Tushar - not sure. I'll find out. I assume not (because the small labels are hard to touch) and I though that if I simply increase the size, the letters will get bigger. – Steve Ives Feb 03 '17 at 15:06
  • They won't get bigger unless you set bigger font size :) so give it a try to set a tappable frame to them. – Tushar Feb 03 '17 at 15:13
  • @Tushar SKLabelNodes don;t appear to have either a frame or size property, so I'm going to have to try the bigger parent node. – Steve Ives Feb 03 '17 at 15:22
  • I think it already inherits the frame from superclass SKNode, need to check once – Tushar Feb 03 '17 at 17:15
  • 1
    Steve, place each label as a child of an SKSpriteNode of a fixed size, then enable touch on the sprite instead of the label – Knight0fDragon Feb 03 '17 at 20:00
  • @Tushar, SKNodes do not have frames because they are infinetly large, you need to use calculateAccumulatedFrame – Knight0fDragon Feb 03 '17 at 20:01
  • Aesthetically, @SteveIves, frigging PERFECT!!! – Confused Feb 04 '17 at 10:38
  • Alternative cheaty technique: combine two SKLabelNodes in a subclass of SKNode. One is an invisible pair of `|`'s around each letter. Like **|Q||W||E||R||T||Y|** etc... here's a subclass made of two labels: http://stackoverflow.com/questions/28356256/sklabelnode-text-with-two-different-fonts-and-colour-how-is-this-possible – Confused Feb 04 '17 at 10:48

1 Answers1

1

Just building on what @KnightOfDragon said, I would just create a subclass of SKSpriteNode for each key. That way it keeps your code objectified and pretty!

There is so much more that can be done here, but I whipped this up in a hurry. It doesn't have all the letters and it doesn't know how to handle del, space, return, but I figured your code already knew what to do with those.

The Key class uses a protocol to send down which key was pushed, so you don't have to worry about capturing each key area in the scene

So I entered the rest of the keys and added some blank filler options for spacing. I don't have the same font as you but it looks pretty cool

enter image description here

import SpriteKit

protocol KeyDelegate: NSObjectProtocol {
    func keyWasPressed(sender: Key)
}

enum KeyType: Int {

    case a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, comma, period, del, space, ret, dash, lb, rb, pound, exclaim, quest, filler, halfFiller

    var height: CGFloat {
        return 60
    }

    var width: CGFloat {

        switch self {  
            case .del: return 150
            case .space: return 180
            case .ret: return 150
            case .halfFiller: return 30
            default: return 60
        }
    }

    var keyValue: String {

        switch self {

            case .filler: return ""
            default: return String(describing: self)
        }
    }

    var keyText: String {

        switch self {

            case .del: return "DEL"
            case .space: return "SP"
            case .ret: return "ENT"
            case .lb: return "("
            case .rb: return ")"
            case .exclaim: return "!"
            case .comma: return ","
            case .period: return "."
            case .dash: return "-"
            case .pound: return "#"
            case .quest: return "?"
            case .filler, .halfFiller: return ""

            default: return String(describing: self).uppercased()
        }
    }

    var keyTextColor: SKColor {

        switch self {

            case .del, .space, .ret: return .red
            case .lb, .rb, .exclaim, .dash, .pound, .quest: return .green
            default: return .blue
        }
    }

    var filler: Bool {

        switch self {
            case .filler, .halfFiller: return true
            default: return false
        }
    }
}

class Key: SKSpriteNode {

    var keyType: KeyType = .a
    private var keyValue = ""
    var keyText: String = ""
    weak var keyDelegate: KeyDelegate!

    init(keyType: KeyType) {

        //let backgroundColor: SKColor = keyType.filler ? .clear : .red

        super.init(texture: nil, color: .clear, size: CGSize.zero)

        self.isUserInteractionEnabled = true
        self.size = CGSize(width: keyType.width, height: keyType.height)
        self.anchorPoint = CGPoint(x: 0, y: 0)
        self.keyType = keyType
        self.keyValue = keyType.keyValue
        self.keyText = keyType.keyText

        guard !keyType.filler else { return }

//        let square = SKShapeNode(rectOf: size)
//        square.strokeColor = .white
//        square.position = CGPoint(x: self.size.width / 2, y: self.size.height / 2)
//        square.zPosition = 1
//        addChild(square)

        let titleLabel = SKLabelNode(fontNamed: "04b_19")
        titleLabel.fontColor = keyType.keyTextColor
        titleLabel.fontSize = 56
        titleLabel.horizontalAlignmentMode = .center
        titleLabel.verticalAlignmentMode = SKLabelVerticalAlignmentMode.center
        titleLabel.position = CGPoint(x: self.size.width / 2, y: self.size.height / 2)
        titleLabel.zPosition = 10
        titleLabel.text = self.keyText
        self.addChild(titleLabel)

    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func getKeyValue() -> String {

        return self.keyValue
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {

        guard !keyType.filler else { return }

        self.keyDelegate.keyWasPressed(sender: self)
    }
}



class GameScene: SKScene, KeyDelegate {

    private var titleLabel = SKLabelNode()

    override func didMove(to view: SKView) {

        titleLabel.fontColor = .white
        titleLabel.fontSize = 68
        titleLabel.horizontalAlignmentMode = .left
        titleLabel.verticalAlignmentMode = SKLabelVerticalAlignmentMode.baseline
                titleLabel.position = CGPoint(x: 220, y: 800)
        titleLabel.zPosition = 10
        titleLabel.text = ""
        self.addChild(titleLabel)

        let keys: [[KeyType]] = [[.q, .w, .e, .r, .t, .y, .u, .i, .o, .p],
                             [.halfFiller, .a, .s, .d, .f, .g, .h, .j, .k, .l],
                             [.halfFiller, .filler, .z, .x, .c, .v, .b, .n, .m, .comma, .period],
                             [.del, .filler, .halfFiller, .space, .filler, .halfFiller, .ret],
                             [.filler, .filler, .dash, .lb, .rb, .pound, .exclaim, .quest]]
        let padding: CGFloat = 8
        let startPoint = CGPoint(x: 30, y: 700)
        var xOffset: CGFloat = 0
        var yOffset: CGFloat = 0
        var keyHeight: CGFloat = 0

        for row in keys {

            for keyType in row {

                print("keyType \(keyType)")
                let key = Key(keyType: keyType)
                key.position = CGPoint(x: startPoint.x + xOffset, y: startPoint.y + yOffset)
                key.keyDelegate = self
                addChild(key)

                xOffset += key.keyType.width + padding
                keyHeight = key.keyType.height
            }

            xOffset = 0
            yOffset -= (keyHeight + padding)
        }
    }

    //MARK:- KeyDelegate Func 

    func keyWasPressed(sender: Key) {

        let text = titleLabel.text!
        titleLabel.text = text + sender.getKeyValue()
    }    
}
Ron Myschuk
  • 6,011
  • 2
  • 20
  • 32
  • thanks Ron - I'll check that out and get back to you. – Steve Ives Feb 03 '17 at 20:45
  • No Problem, You probably would figure it out on your own, but the keys don't have to have the border around them. They could basically look like your layout by just remove the shape node from the key init, and changing the super.init color to .clear – Ron Myschuk Feb 03 '17 at 22:45
  • I finished adding the reset of the keys as well as some spacing options for keys, and removed the key background color – Ron Myschuk Feb 04 '17 at 17:10
  • 1
    Sorry - just gotten around to looking at this. Not sure I would have come up with his solution - not delved into protocols yet. Going to take a while for me to assimilate this. – Steve Ives Feb 08 '17 at 22:18