This is pretty basic UI stuff, whether it be for games or other applications. "Easier said than done" is more of a state of mind. When it comes to complexity in building games, this is the simple stuff.
Anyways, the key to this is you need to know how to get the width of you number. This problem is the same for any text for that matter. So if you understand how to do this, you can actually make your own custom fonts.
Below is some "code". It won't compile and I have not filled out methods. The reason for this is manifold. First and foremost, you will get more benefit out of learning by doing. Additionally, I have no idea how you are doing your stuff and it's tedious to type out all the details for a project that is not mine.
For this I'm creating a fictitious class NumberLabel
. This is a combo SKNode
/"custom font" class. In reality, I would separate the two. But for now, this was done for simplicity (albeit very messy as a byproduct that I would not build it in a singular class).
class NumberLabel {
// Root for all digits. This is also the "center"
var root:SKNode = SKNode()
private var underlyingValue:UInt = 0
// These will hold each digit, for example 1234 will be [1, 2, 3, 4]
private var digits: Array<UInt>
var value: UInt {
get {
return underlyingValue
}
set {
// There is no optimization checks (ie. if underlyingValue != newValue, you can do that later WHEN you get this working properly)
underlyingValue = newValue
// Release old nodes
// You will build your other nodes here
buildDigits()
let width = getWidth()
let x = -width / 2
let y = ??? // Determine what your y should be based on your anchoring policy for sprites
// I now know that -width/2
for (digit in digits) {
// This is all pseudo code
var digitNode = createDigitNodeForDigit(digit, x, y)
// You need to determine any trailing spacing, this would be also accounted for in getWidth
x += getWidthOfDigit(digit)
root.addChild(digitNode)
}
}
}
// Initialization omitted
// Build the digits array
// For laziness this uses underlyingValue. Safer would be to take it as an argument, but this is just example code
private func buildDigits() {
// Populate array based on the number value
// For example 1234 will be the array [1, 2, 3, 4]
}
// Get's width of underlyingValue (use float if you want)
// For laziness of writing this, it assumes BOTH underlyingValue and digits are properly assigned
public func getWidth() -> UInt {
// Get the width of the number
// Traverse your digits array, determine the width based on number, and insert any kerning (if appropropiate)
}
// Use float if you want
public func getHeight() -> UInt {
// Return height. While width may not be uniform, for digits, there should be uniform height
}
}
So some key takeaways:
I have not derived from SKNode
. You are free to do so. I never derive from SKNode
myself for classes like this. I use a "has a" versus "is a" approach for these cases. Subclass if you want, it won't change things too much.
getWidth
is your friend. Any font system you are building needs this as a corner stone. It is by which you render but also the means for placement within the UI.
There are assumptions you know your widths or will derive them. You'll need them
There is an assumption you know how to go from number to individual digits. You will use this to populate digits
This is all meant to be a fast example, so my interfaces are sloppy. They don't take arguments whereas a cleaner interface for this would
createDigitNodeForDigit
would be your method to take a digit value (0-9) and create your SKSpriteNode
or "digit node" object
getWidthOfDigit
would be your method to get the width of that digit. Presumably you can just query the width of the texture. It's up to you on how you want to deal with spacing between digits
Note that we only do this on the setting of the value. Thus you are not incurring deconstruction and construction of the nodes every frame.
I answered a similar question here: Add custom "number" images to SKLabelNode in as a Score in SpriteKit with Swift
So maybe that has some other info for you to absorb.