Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there really no way to style SKLabelNode?

Is there really no way to style SKLabelNode? I mean beyond changing the font, and the color. I know I can do something like adding a drop shadow by creating a second SKLabelNode behind the first (already sort've hacky).. Creating images for my font would be terrible too for obvious reasons. It seems odd theres nothing you can do to break out of boring flat text.

I mean.. even the spritekit node count has a cool gradient style... how is THAT done?!

enter image description here

okay so here i am trying to recreate a gradient effect. I've tried every combination of blend mode, and multiple combinations of color, and colorBlendFactor.

alpha

alpha

multiply

enter image description here

add

enter image description here

here is the code

let testNode = SKLabelNode(text: "hey there")
testNode.fontSize = 30
testNode.color = SKColor.blueColor()
testNode.colorBlendFactor = 1
testNode.fontName = "Helvetica-Bold"
testNode.blendMode = SKBlendMode.Multiply
testNode.colorBlendFactor = 0.6
testNode.position = CGPoint(x: self.size.width/2, y: self.size.height/2)

self.addChild(testNode)
like image 283
hamobi Avatar asked Jan 09 '15 00:01

hamobi


3 Answers

It's pretty easy with a shader. Add a text file to your project called "TheShader.fsh":

void main()
{
    float yPos = gl_FragCoord.y - labelBase;
    float gradient = yPos / u_sprite_size.y; // ranges from 0 at base to 1 at top

    vec4 color = SKDefaultShading(); // the current label color
    color = vec4(gradient + color.r, gradient + color.g, gradient + color.b, color.a);
    color.rgb *= color.a; // set background to alpha 0

    gl_FragColor = color;
}

Now create an SKEffectNode in SKScene, set its shader to "TheShader", and add your label as a child of the effect node. The label will be drawn using a vertical gradient ranging from the label's fontColor at bottom to white at top.

override func didMoveToView(view: SKView)
{
    makeGradientLabel("318 nodes 60.0 fps", labelPosition: CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame)))
}

func makeGradientLabel(labelText: String, labelPosition: CGPoint)
{
    let myShader = SKShader(fileNamed: "TheShader")
    let labelBase = SKUniform(name: "labelBase", float: Float(labelPosition.y))
    myShader.addUniform(labelBase)

    let effectNode = SKEffectNode()
    effectNode.shader = myShader
    effectNode.shouldEnableEffects = true
    addChild(effectNode)

    let theLabel = SKLabelNode(fontNamed: "Courier")
    theLabel.fontSize = 36
    theLabel.fontColor = SKColor.blueColor()
    theLabel.text = labelText
    theLabel.position = labelPosition

    effectNode.addChild(theLabel)
}

If you move the label you'll need to update the labelBase uniform so the shader will know where it is:

labelBase.floatValue = Float(theLabel.position.y)
like image 135
Loengard Avatar answered Nov 02 '22 00:11

Loengard


After a lot of trial and error, I got this working:

{
        const CGFloat LABELFONTSIZE = 100;
        SKColor* fontcolor = [SKColor redColor];

        UIFont* font = [AppDelegate fontOfType:Fonttype_main size:LABELFONTSIZE]; // LABELFONTSIZE not used here but whatever.
        SKLabelNode* label = [SKLabelNode labelNodeWithFontNamed:font.fontName];
        label.fontColor = fontcolor;
        label.verticalAlignmentMode = SKLabelVerticalAlignmentModeTop;
        self.labelScore = label;
        //label.zPosition = Z_HUD;
        label.fontSize = LABELFONTSIZE;

        self.score = 0;

        const CGPoint LABELPOSITION = CGPointMake(self.size.width * 0.5, self.size.height);
        label.position = LABELPOSITION;

#define USESHADER
#ifdef USESHADER
        SKShader* myShader = [SKShader shaderWithFileNamed:@"TheShader"];
        NSAssert(myShader, @"myShader was nil!");

        SKEffectNode* effectNode = [SKEffectNode node];
        effectNode.zPosition = Z_HUD;
        effectNode.shader = myShader;
        effectNode.shouldEnableEffects = YES;
        [self addChild:effectNode];

        [effectNode addChild:label];
#else
        label.zPosition = Z_HUD;
        [self addChild:label];
#endif

        [label runAction:[SKAction repeatActionForever:[SKAction sequence:@[
                                                                            [SKAction moveByX:0 y:-300 duration:3.0],
                                                                            [SKAction moveByX:0 y:300 duration:3.0],
                                                                            ]]]];
    }

TheShader.fsh:

void main()
{
    float gradient = v_tex_coord.y;
    //vec4 color = SKDefaultShading(); // the current label color THIS DOES NOT WORK ON IOS 9.2 AND EARLIERISH (MAYBE IT DOES ON IOS 8) WORKS ONLY ON IOS 9.3 BETA AT THIS MOMENT!
    vec4 color = texture2D(u_texture, v_tex_coord);
    color = vec4(color.r + gradient, color.g + gradient, color.b + gradient, color.a);
    color.rgb *= color.a; // set background to alpha 0
    gl_FragColor = color;
}

It's based on Loengard's answer, but does not use any uniforms. A few of the stuff inside the shader did/does not work on a number of iOS 9 versions, including the latest iOS 9.3 beta as of now (20160115). This code above works well, it doesn't even care about the position of the label on screen; I animate the label up/and down to confirm this.

like image 39
Jonny Avatar answered Nov 02 '22 01:11

Jonny


There is no method or property of SKLabelNode that will enable you to achieve the gradient style. As noted in the link in the comments, SpriteKit does have a SKBitmapFont private class, but that's not going to help you.

You can either roll your own solution or try glyph designer : https://71squared.com/glyphdesigner

like image 2
prototypical Avatar answered Nov 02 '22 02:11

prototypical