-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathEndlessBackgroundNode.swift
161 lines (122 loc) · 4.99 KB
/
EndlessBackgroundNode.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//
// EndlessBackgroundNode.swift
//
// Created by gitmalong
//
//
import SpriteKit
/*
Node for an endless vertically scrolling background
in an SpriteKit environment that makes use of SKCameraNode
Supports parallax backgrounds as well
Usage:
Designed for this kind of pattern
GameScene -> world -> backgroundNodes -> place your EndlessBackground(s) here
-> e.g. platforms
-> e.g. interactive sprites
-> e.g. characters -> hero
-> enemies
-> camera -> fixed stuff, e.g. control UI
Add your EndlessBackground object to your backgroundNodes or world and call its draw() method in your didMoveToView for initializing.
After that just call triggerDraw() in your GameScenes didFinishUpdate()
or in your camers position -> didSet listener
and this class takes care of drawing and removing your background nodes
*/
public class EndlessBackground:SKNode {
/// Caches your background node
private let node:SKSpriteNode
/// Max number of background nodes
private var drawMaxNodes:Int
private let camera:SKCameraNode
/// New background is drawn when there is no bg at this value+camera.position.x
private let distanceTrigger:CGFloat
init(yourBackgroundNode node:SKSpriteNode,camera:SKCameraNode, triggerDistance:CGFloat?=nil) {
self.node = node
self.camera = camera
if let d = triggerDistance {
self.distanceTrigger = d
} else {
self.distanceTrigger = camera.parent!.scene!.size.width/2
}
// Calc max nodes
// Allow to prerender half a screen
drawMaxNodes = Int(round(camera.scene!.size.width/node.size.width*1.5))
if drawMaxNodes < 2 { // If node size is bigger than screen we want to have 2 at least
drawMaxNodes = 2
}
super.init()
self.position = node.position
self.zPosition = node.zPosition
self.name = node.name
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func getMostRightChildnode()->SKNode? {
var mostRightNode:SKNode? = nil
for node in self.children {
if (mostRightNode == nil || node.position.x > mostRightNode!.position.x) {
mostRightNode = node
}
}
return mostRightNode
}
private func getMostLeftChildnode()->SKNode? {
var mostLeftNode:SKNode? = nil
for node in self.children {
if (mostLeftNode == nil || node.position.x < mostLeftNode!.position.x) {
mostLeftNode = node
}
}
return mostLeftNode
}
func initDraw() {
self.addChild(node)
}
private func draw(position:CGPoint=CGPointZero) {
// Copy Sprite Node and change its position
// Texture of the copy will point to the same ressource -> Good
if let _ = node.parent { // If cache node has parent we need a copy
let nodeCopy = node.copy() as! SKSpriteNode
nodeCopy.position = position
self.addChild(nodeCopy)
} else { // If cache node has no parent we use it
node.position = position
self.addChild(node)
}
}
/* should be called from the scene's didFinishUpdate or cameras position->didSet method */
func triggerDraw() {
// Convert most left/right child position to camera parents (->szene) coordinate system in order to support moving parallex backgrounds
let mostRightNodeEndXInGameScene = convertPoint(getDrawRightPosition(), toNode: camera.parent!).x
let mostLeftChildNodeXInGameScene = convertPoint(getMostLeftChildnode()!.position, toNode: camera.parent!).x
let cameraPlusTriggerDistance = camera.position.x+distanceTrigger
let cameraMinusTriggerDistance = camera.position.x-distanceTrigger
if (cameraPlusTriggerDistance > mostRightNodeEndXInGameScene) {
if children.count > drawMaxNodes {
getMostLeftChildnode()!.position = getDrawRightPosition()
} else {
drawRight()
}
}
if (cameraMinusTriggerDistance < mostLeftChildNodeXInGameScene) {
if children.count > drawMaxNodes {
getMostRightChildnode()!.position = getDrawLeftPosition()
} else {
drawLeft()
}
}
}
private func getDrawLeftPosition()->CGPoint {
return CGPointMake(getMostLeftChildnode()!.position.x-node.size.width,node.position.y)
}
private func getDrawRightPosition()->CGPoint {
return CGPointMake(getMostRightChildnode()!.position.x+node.size.width,node.position.y)
}
private func drawLeft() {
draw(getDrawLeftPosition())
}
private func drawRight() {
draw(getDrawRightPosition())
}
}