Class

SKShapeNode

A node that renders a shape defined by a Core Graphics path.

Overview

The graphics path is a collection of straight lines and curves that can define either open or closed subpaths. You can specify separate rendering behavior for the filled and stroked portion of the path. Each part can be rendered using either a solid color or a texture; if you need to render more sophisticated effects, you can also use a custom shader.

Shape nodes are useful for content that cannot be easily decomposed into simple textured sprites. Shape nodes are also very useful for building and displaying debugging information on top of your game content. However, the SKSpriteNode class offers higher performance than this class, so use shape nodes sparingly.

Listing 1 shows an example of how to create a shape node. The example creates a circle with a blue interior and a white outline. The path is created and attached to the shape node’s path property.

Listing 1

Creating a shape node from a path

let path = CGMutablePath()
path.addArc(center: CGPoint.zero,
            radius: 15,
            startAngle: 0,
            endAngle: CGFloat.pi * 2,
            clockwise: true)
let ball = SKShapeNode(path: path)
ball.lineWidth = 1
ball.fillColor = .blue
ball.strokeColor = .white
ball.glowWidth = 0.5

You can see from the code that the shape has three essential elements:

  • The interior of the shape is filled. The fillColor property specifies the color used to fill the interior.

  • The outline of the shape is rendered as a line. The strokeColor and lineWidth properties define how the line is stroked.

  • A glow extends from the outline. The glowWidth and strokeColor properties define the glow.

You can disable any of these elements by setting its color to [SKColor clearColor].

The shape node provides properties that let you control how the shape is blended into the framebuffer. You use these properties the same way as the properties of the SKSpriteNode class. See SKShapeNode.

Creating Shape Nodes from Points

An SKShapeNode object can be initialized with an array of points describing a path. The init(splinePoints:count:) method can smoothly interpolate between these points to create a curve rather than the series of straight lines created by init(points:count:). Listing 2 shows how to create two shape nodes using the same array of points with the two initializers.

var points = [CGPoint(x: 0, y: 0),               
              CGPoint(x: 100, y: 100),               
              CGPoint(x: 200, y: -50),               
              CGPoint(x: 300, y: 30),               
              CGPoint(x: 400, y: 20)]         
let linearShapeNode = SKShapeNode(points: &points,                                   
                                  count: points.count)          
let splineShapeNode = SKShapeNode(splinePoints: &points,                                   
                                  count: points.count)

Figure 1 shows linearShapeNode in blue and splineShapeNode in red.

Figure 1

Shape nodes created from points

Shape nodes created from points

Applying Shaders to Shape Nodes

You can use the strokeShader and fillShader properties of a shape node to change the appearance of its stroke and fill with a custom OpenGL ES fragment shader embedded within a SKShader object. Custom shaders offer almost limitless possibilities, from creating dashed and graduated strokes to custom fills such as checkerboards and random noise.

Stroke Shaders

Custom shaders applied to a shape node's strokeShader have two additional symbols available to them which extend the functionality of stroke shaders (see SKShader for the full list of predefined custom shader symbols):

Table 1

Stroke shader specific predefined custom shader symbols

Symbol declaration

Type

Description

float u_path_length;

Uniform

The total length of the path in points.

float v_path_distance;

Varying

The distance along the path in points.

By dividing the distance along the path by the total length of the path, you can get the normalized position (i.e. between 0 and 1) of each point along a shape node's path and use this to construct the color of each pixel along its stroke. The following code shows how you could create a custom GLSL shader to do this:

let gradientShader = SKShader(source: "void main() {" +
    "float normalisedPosition = v_path_distance / u_path_length;" +
    "gl_FragColor = vec4(normalisedPosition, normalisedPosition, 0.0, 1.0);" +
    "}")
let squareShapeNode = SKShapeNode(rectOf: CGSize(width: 610, height: 200),
                                  cornerRadius: 25)
squareShapeNode.fillColor = .clear
squareShapeNode.lineWidth = 20
squareShapeNode.strokeShader = gradientShader

The shape node generated by this code looks like:

Shape node with graduated stroke

Alternatively, by casting both symbols to integers and using the modulo operator, the following code shows the same shape node with a shader that generates a dashed line:

let dashedShader = SKShader(source: "void main() {" +
    "int stripe = int(u_path_length) / 150;" +
    "int h = int(v_path_distance) / stripe % 2;" +
    "gl_FragColor = float4(h);" +
    "}")

The shape node generated by this code looks like:

Shape node with dashed stroke

Fill Shaders

You can use the shader property of a shape node to either change the appearance of an existing fillTexture or generate a new, procedural texture. Shaders are passed to a shape node with a custom OpenGL ES fragment shader embedded within a SKShader object.

The following code shows a custom shader that generates and fills a shape node with a simple checkerboard texture. Inside the shader, the variables h and v would, on their own, form horizontal and vertical stripes. The exclusive or operator, ^, creates the checkerboard pattern from those stripes.

let checkerboardShader = SKShader(source: "void main() {" +
    "int size = 20;" +
    "int h = int(v_tex_coord.x * u_texture_size.x) / size % 2;" +
    "int v = int(v_tex_coord.y * u_texture_size.y) / size % 2;" +
    "gl_FragColor = float4(v ^ h, v ^ h, v ^ h, 1.0);" +
    "}")
   
let size = CGSize(width: 610, height: 200)
   
checkerboardShader.uniforms = [
    SKUniform(name: "u_texture_size",
              vectorFloat2: vector_float2(Float(size.width), Float(size.height)))
]
   
let squareShapeNode = SKShapeNode(rectOf: size,
                                  cornerRadius: 25)
squareShapeNode.fillShader = checkerboardShader

The shape node generated by this code looks like:

Shape node with checkerboard fill

Topics

Creating a Shape Path

init(path: CGPath)

Creates a shape node from a Core Graphics path.

init(path: CGPath, centered: Bool)

Creates a shape node from a Core Graphics path.

init(rect: CGRect)

Creates a shape node with a rectangular path.

init(rectOf: CGSize)

Creates a shape node with a rectangular path centered on the node’s origin.

init(rect: CGRect, cornerRadius: CGFloat)

Creates a shape with a rectangular path with rounded corners.

init(rectOf: CGSize, cornerRadius: CGFloat)

Creates a shape with a rectangular path with rounded corners centered on the node’s origin.

init(circleOfRadius: CGFloat)

Creates a shape node with a circular path centered on the node’s origin.

init(ellipseOf: CGSize)

Creates a shape node with an elliptical path centered on the node’s origin.

init(ellipseIn: CGRect)

Creates a shape node with an elliptical path that fills the specified rectangle.

init(points: UnsafeMutablePointer<CGPoint>, count: Int)

Creates a shape node from a series of points.

init(splinePoints: UnsafeMutablePointer<CGPoint>, count: Int)

Creates a shape node from a series of spline points.

Inspecting the Shape Node’s Path

var path: CGPath?

The path that defines the shape.

Setting the Fill Properties

var fillColor: UIColor

The color used to fill the shape.

var fillTexture: SKTexture?

The texture used to fill the shape.

var fillShader: SKShader?

A custom shader used to determine the color of the filled portion of the shape node.

Setting the Stroke Properties

var lineWidth: CGFloat

The width used to stroke the path.

var glowWidth: CGFloat

The glow that extends outward from the stroked line.

var isAntialiased: Bool

A Boolean value that determines whether the stroked path is smoothed when drawn.

var strokeColor: UIColor

The color used to stroke the shape.

var strokeTexture: SKTexture?

The texture used to stroke the shape.

var strokeShader: SKShader?

A custom shader used to determine the color of the stroked portion of the shape node.

var lineCap: CGLineCap

The style used to render the endpoints of the stroked portion of the shape node.

var lineJoin: CGLineJoin

The junction type used when the stroked portion of the shape node is rendered.

var miterLimit: CGFloat

The miter limit to use when the line is stroked using a miter join style.

Blending the Shape with the Framebuffer

var blendMode: SKBlendMode

The blend mode used to blend the shape into the parent’s framebuffer.

Reading the Shape Node’s Properties

var lineLength: CGFloat

The length of the line defined by the shape node.

Working with Custom Shaders

You can apply custom shaders separately to a shape node's fill, by setting its fillShader, or stroke, by setting its strokeShader.

var attributeValues: [String : SKAttributeValue]

The values of each attribute associated with the node's attached shader.

func setValue(SKAttributeValue, forAttribute: String)

Sets an attribute value for an attached shader.

func value(forAttributeNamed: String)

The value of a shader attribute.

Instance Properties

var customPlaygroundQuickLook: PlaygroundQuickLook

A custom playground quick look for this instance.

See Also

Nodes That Draw Content

class SKSpriteNode

A node that draws a rectangular texture, image or color.

class SKLabelNode

A node that displays a text label.

class SKVideoNode

A node that displays video content.

class SKCropNode

A node that masks pixels drawn by its children so that only some are rendered to the parent’s frame buffer.

class SKReferenceNode

A node that creates its children from an archived collection of other nodes.