In this article by Samanyu Chopra, author of the book iOS Game Development By Example, we will study about nodes, which play an important role in understanding the tree structure of a game. Further, we will discuss about types of nodes in the Sprite Kit and their uses in detail.
(For more resources related to this topic, see here.)
We have discussed many things about nodes so far. Almost everything you are making in a game with Sprite Kit is a node. Scenes that we are presenting to view are instances of the SKScene class, which is a subclass of the SKEffectNode class, which is itself a subclass of the SKNode class. Indirectly, SKScene is a subclass of the SKNode class.
As a game follows the node tree formation, a scene acts like a root node and the other nodes are used as its children. It should be remembered that although SKNode is a base class for the node you see in a scene, it itself does not draw anything. It only provides some basic features to its subclass nodes. All the visual content we see in a Sprite Kit made game, is drawn by using the appropriate SKNode subclasses.
Following are some subclasses of SKNode classes, which are used for different behaviors in a Sprite Kit-based game:
So, these are the basic subclasses of SKNode which are used frequently in Sprite Kit. SKNode provides some basic properties to its subclasses, which are used to view a node inside a scene, such as:
We know that the SKNode class does not draw anything by itself. So, what is the use of if? Well, we can use SKNode instances to manage our other nodes in different layers separately, or we can use them to manage different nodes in the same layer. Let's take a look at how we can do this.
Now, we will discover what the various aspects of SKNode are used for. Say you have to make a body from different parts of sprites, like a car. You can make it from sprites of wheels and body. The wheels and body of a car run in synchronization with each other, so that one control their action together, rather than manage each part separately. This can be done by adding them as a child of the SKNode class object and updating this node to control the activity of the car.
The SKNode class object can be used for layering purposes in a game. Suppose we have three layers in our game: the foreground layer, which represents foreground sprites, the middle layer, which represents middle sprites, and the background layer which represents background sprites.
If we want a parallax effect in our game, we will have to update each sprite's position separately or we can make three SKNode objects, referring to each layer, and add the sprites to their respective nodes. Now we have to update only these three nodes' position and the sprites will update their position automatically.
The SKNode class can be used to make some kind of check point in a game, which is hidden but performs or triggers some event when a player crosses them, such as level end, bonus, or death trap.
We can remove or add the whole sub tree inside a node and perform the necessary functions, such as rotating, scaling, positioning, and so on.
Well, as we described that we can use the SKNode object as checkpoints in the game, it is important to recognize them in your scene. So, how we do that? Well the SKNode class provides a property for this. Let's find out more about it.
The SKNode class provides a property with a name, to recognize the correct node. It takes string as a parameter. Either you can search a node by its name or can use one of the two methods provided by SKNode, which are as follows:
So, let's have a quick look at other properties or methods of the SKNode class.
There are two initializers to make an instance of SKNode. Both are available in iOS 8.0 or later.
As we already discussed the positioning of a node, let's discuss some functions and properties that are used to build a node tree.
SKNode provides some functions and properties to work with a node tree. Following are some of the functions:
There are some useful properties used in a node tree, as follows:
In a game, we need some specific task on a node, such as changing its position from one point to another, changing sprites in a sequence, and so on. These tasks are done using actions on node. Let's talk about them now.
Actions are required for some specific tasks in a game. For this, the SKNode class provides some basic functions, which are as follows.
Some useful properties to control these actions are as follows:
Sometimes, we require changing a point coordinate system according to a node inside a scene. The SKNode class provides two functions to interchange a point's coordinate system with respect to a node in a scene. Let's talk about them.
We can convert a point with respect to the coordinate system of any node tree. The functions to do that, are as follows:
We can also determine if a point is inside a node's area or not.
Apart from these, the SKNode class provides some other functions and properties too. Let's talk about them.
Some other functions and properties of the SKNode class are as follows:
Now, we are ready to see some basic SKNode subclasses in action. The classes we are going to discuss are as follows:
To study these classes, we are going to create six different SKScene subclasses in our project, so that we can learn them separately.
Now, having learned in detail about nodes, we can proceed further to utilize the concept of nodes in a game.
With the theoretical understanding of nodes, one wonders how this concept is helpful in developing a game. To understand the development of a game using the concept of Nodes, we now go ahead with writing and executing code for our Platformer game.
Create the subclasses of different nodes in Xcode, following the given steps:
Make sure Platformer is ticked as the target. Now Create and Open and make the NodeMenuScene class by subclassing SKScene.
override func viewDidLoad() {
super.viewDidLoad()
let menuscene = NodeMenuScene()
let skview = view as SKView
skview.showsFPS = true
skview.showsNodeCount = true
skview.ignoresSiblingOrder = true
menuscene.scaleMode = .ResizeFill
menuscene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
menuscene.size = view.bounds.size
skview.presentScene(menuscene)
}
In this code, we just called our NodeMenuScene class from the GameViewController class. Now, it's time to add some code to the NodeMenuScene class.
Open the NodeMenuScene.swift file and type in the code as shown next. Do not worry about the length of the code; as this code is for creating the node menu screen, most of the functions are similar to creating buttons:
import Foundation
import SpriteKit
let BackgroundImage = "BG"
let FontFile = "Mackinaw1"
let sKCropNode = "SKCropNode"
let sKEmitterNode = "SKEmitterNode"
let sKLightNode = "SKLightNode"
let sKShapeNode = "SKShapeNode"
let sKVideoNode = "SKVideoNode"
class NodeMenuScene: SKScene {
let transitionEffect = SKTransition.flipHorizontalWithDuration(1.0)
var labelNode : SKNode?
var backgroundNode : SKNode?
override func didMoveToView(view: SKView) {
backgroundNode = getBackgroundNode()
backgroundNode!.zPosition = 0
self.addChild(backgroundNode!)
labelNode = getLabelNode()
labelNode?.zPosition = 1
self.addChild(labelNode!)
}
func getBackgroundNode() -> SKNode {
var bgnode = SKNode()
var bgSprite = SKSpriteNode(imageNamed: "BG")
bgSprite.xScale = self.size.width/bgSprite.size.width
bgSprite.yScale = self.size.height/bgSprite.size.height
bgnode.addChild(bgSprite)
return bgnode
}
func getLabelNode() -> SKNode {
var labelNode = SKNode()
var cropnode = SKLabelNode(fontNamed: FontFile)
cropnode.fontColor = UIColor.whiteColor()
cropnode.name = sKCropNode
cropnode.text = sKCropNode
cropnode.position =
CGPointMake(CGRectGetMinX(self.frame)+cropnode.frame.width/2,
CGRectGetMaxY(self.frame)-cropnode.frame.height)
labelNode.addChild(cropnode)
var emitternode = SKLabelNode(fontNamed: FontFile)
emitternode.fontColor = UIColor.blueColor()
emitternode.name = sKEmitterNode
emitternode.text = sKEmitterNode
emitternode.position =
CGPointMake(CGRectGetMinX(self.frame) + emitternode.frame.width/2
, CGRectGetMidY(self.frame) - emitternode.frame.height/2)
labelNode.addChild(emitternode)
var lightnode = SKLabelNode(fontNamed: FontFile)
lightnode.fontColor = UIColor.whiteColor()
lightnode.name = sKLightNode
lightnode.text = sKLightNode
lightnode.position = CGPointMake(CGRectGetMaxX(self.frame)
- lightnode.frame.width/2 , CGRectGetMaxY(self.frame) -
lightnode.frame.height)
labelNode.addChild(lightnode)
var shapetnode = SKLabelNode(fontNamed: FontFile)
shapetnode.fontColor = UIColor.greenColor()
shapetnode.name = sKShapeNode
shapetnode.text = sKShapeNode
shapetnode.position =
CGPointMake(CGRectGetMaxX(self.frame) - shapetnode.frame.width/2 ,
CGRectGetMidY(self.frame) - shapetnode.frame.height/2)
labelNode.addChild(shapetnode)
var videonode = SKLabelNode(fontNamed: FontFile)
videonode.fontColor = UIColor.blueColor()
videonode.name = sKVideoNode
videonode.text = sKVideoNode
videonode.position = CGPointMake(CGRectGetMaxX(self.frame)
- videonode.frame.width/2 , CGRectGetMinY(self.frame) )
labelNode.addChild(videonode)
return labelNode
}
var once:Bool = true
override func touchesBegan(touches: NSSet, withEvent event:
UIEvent) {
if !once {
return
}
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let node = self.nodeAtPoint(location)
if node.name == sKCropNode {
once = false
var scene = CropScene()
scene.anchorPoint = CGPointMake(0.5, 0.5)
scene.scaleMode = .ResizeFill
scene.size = self.size
self.view?.presentScene(scene,
transition:transitionEffect)
}
else if node.name == sKEmitterNode {
once = false
var scene = ParticleScene()
scene.anchorPoint = CGPointMake(0.5, 0.5)
scene.scaleMode = .ResizeFill
scene.size = self.size
self.view?.presentScene(scene,
transition:transitionEffect)
}
else if node.name == sKLightNode {
once = false
var scene = LightScene()
scene.scaleMode = .ResizeFill
scene.size = self.size
scene.anchorPoint = CGPointMake(0.5, 0.5)
self.view?.presentScene(scene ,
transition:transitionEffect)
}
else if node.name == sKShapeNode {
once = false
var scene = ShapeScene()
scene.scaleMode = .ResizeFill
scene.size = self.size
scene.anchorPoint = CGPointMake(0.5, 0.5)
self.view?.presentScene(scene,
transition:transitionEffect)
}
else if node.name == sKVideoNode {
once = false
var scene = VideoNodeScene()
scene.scaleMode = .ResizeFill
scene.size = self.size
scene.anchorPoint = CGPointMake(0.5, 0.5)
self.view?.presentScene(scene ,
transition:transitionEffect)
}
}
}
}
We will get the following screen from the previous code:
The screen is obtained when we execute the NodeMenuScene,swift file
In the preceding code, after import statements, we defined some String variables. We are going to use these variables as Label names in scene .We also added our font name as a string variable. Inside this class, we made two node references: one for background and the other for those labels which we are going to use in this scene. We are using these two nodes to make layers in our game. It is best to categorize the nodes in a scene, so that we can optimize the code. We make an SKTransition object reference of the flip horizontal effect. You can use other transition effects too.
Inside the didMoveToView() function, we just get the node and add it to our scene and set their z position.
Now, if we look at the getBackgroundNode() function, we can see that we made a node by the SKNode class instance, a background by the SKSpriteNode class instance, and then added it to node and returned it. If you see the syntax of this function, you will see -> SKNode. It means that this function returns an SKNode object.
The same goes in the function, getLabelNode(). It also returns a node containing all the SKLabelNode class objects. We have given a font and a name to these labels and set the position of them in the screen. The SKLabelNode class is used to make labels in Sprite Kit with many customizable options.
In the touchBegan() function, we get the information on which Label is touched, and we then call the appropriate scene with transitions.
With this, we have created a scene with the transition effect. By tapping on each button, you can see the transition effect.
d shadows will also change themselves according to the source.
In this article, we learned about nodes in detail. We discussed many properties and functions of the SKNode class of Sprite Kit, along with its usage. Also, we discussed about the building of a node tree, and actions on a node tree. Now we are familiar with the major subclasses of SKNode, namely SKLabelNode, SKCropNode, SKShapeNode, SKEmitterNode, SKLightNode, and SKVideoNode, along with their implementation in our game.
Further resources on this subject: