简体   繁体   中英

How do I display a string of digits with custom design?

I don't want to use SKLabelNode because I want to have special effects like a gradient fill, a shadow, and a custom font, so I created 10 png images, one for each digit, and I added them as an image atlas. Then, when setting up the number to display, I create for each digit a new SKSpriteNode with the texture of the digit from the atlas, and add it to a parent SKSpriteNode with no texture. The problem I'm having with this is that I'm trying to set the anchor point to the middle of all the nodes added, but when setting the anchor point, it's only setting the anchor point relative to its own frame, which is not updated when child nodes are added.

I've worked on it some more and I can't quite get the sprites centered. Here's a subclass of SKNode where I'm doing this: Gist . When I create an instance of this node, set the position to 0,0, set currentValue , and add the node to the game scene, it looks like this . The box under it is a SKShapeNode with a frame of the number node's accumulated frame and the same position as the number node. The black dot is 0,0 on the game scene.

Is there a better way to do it? Or what can I do to fix my problem?

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM