I'm adding an old-school high-score entry screen to my game, in which the users tap each letter to enter their name.
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.
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
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()
}
}