forked from newlinedotco/FlappySwift
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGameScene.swift
267 lines (212 loc) · 11.2 KB
/
GameScene.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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
//
// GameScene.swift
// FlappyBird
//
// Created by Nate Murray on 6/2/14.
// Copyright (c) 2014 Fullstack.io. All rights reserved.
//
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate{
let verticalPipeGap = 150.0
var bird:SKSpriteNode!
var skyColor:SKColor!
var pipeTextureUp:SKTexture!
var pipeTextureDown:SKTexture!
var movePipesAndRemove:SKAction!
var moving:SKNode!
var pipes:SKNode!
var canRestart = Bool()
var scoreLabelNode:SKLabelNode!
var score = NSInteger()
let birdCategory: UInt32 = 1 << 0
let worldCategory: UInt32 = 1 << 1
let pipeCategory: UInt32 = 1 << 2
let scoreCategory: UInt32 = 1 << 3
override func didMoveToView(view: SKView) {
canRestart = false
// setup physics
self.physicsWorld.gravity = CGVectorMake( 0.0, -5.0 )
self.physicsWorld.contactDelegate = self
// setup background color
skyColor = SKColor(red: 81.0/255.0, green: 192.0/255.0, blue: 201.0/255.0, alpha: 1.0)
self.backgroundColor = skyColor
moving = SKNode()
self.addChild(moving)
pipes = SKNode()
moving.addChild(pipes)
// ground
let groundTexture = SKTexture(imageNamed: "land")
groundTexture.filteringMode = SKTextureFilteringMode.Nearest
let moveGroundSprite = SKAction.moveByX(-groundTexture.size().width * 2.0, y: 0, duration: NSTimeInterval(0.02 * groundTexture.size().width * 2.0))
let resetGroundSprite = SKAction.moveByX(groundTexture.size().width * 2.0, y: 0, duration: 0.0)
let moveGroundSpritesForever = SKAction.repeatActionForever(SKAction.sequence([moveGroundSprite,resetGroundSprite]))
for var i:CGFloat = 0; i < 2.0 + self.frame.size.width / ( groundTexture.size().width * 2.0 ); ++i {
let sprite = SKSpriteNode(texture: groundTexture)
sprite.setScale(2.0)
sprite.position = CGPointMake(i * sprite.size.width, sprite.size.height / 2.0)
sprite.runAction(moveGroundSpritesForever)
moving.addChild(sprite)
}
// skyline
let skyTexture = SKTexture(imageNamed: "sky")
skyTexture.filteringMode = SKTextureFilteringMode.Nearest
let moveSkySprite = SKAction.moveByX(-skyTexture.size().width * 2.0, y: 0, duration: NSTimeInterval(0.1 * skyTexture.size().width * 2.0))
let resetSkySprite = SKAction.moveByX(skyTexture.size().width * 2.0, y: 0, duration: 0.0)
let moveSkySpritesForever = SKAction.repeatActionForever(SKAction.sequence([moveSkySprite,resetSkySprite]))
for var i:CGFloat = 0; i < 2.0 + self.frame.size.width / ( skyTexture.size().width * 2.0 ); ++i {
let sprite = SKSpriteNode(texture: skyTexture)
sprite.setScale(2.0)
sprite.zPosition = -20
sprite.position = CGPointMake(i * sprite.size.width, sprite.size.height / 2.0 + groundTexture.size().height * 2.0)
sprite.runAction(moveSkySpritesForever)
moving.addChild(sprite)
}
// create the pipes textures
pipeTextureUp = SKTexture(imageNamed: "PipeUp")
pipeTextureUp.filteringMode = SKTextureFilteringMode.Nearest
pipeTextureDown = SKTexture(imageNamed: "PipeDown")
pipeTextureDown.filteringMode = SKTextureFilteringMode.Nearest
// create the pipes movement actions
let distanceToMove = CGFloat(self.frame.size.width + 2.0 * pipeTextureUp.size().width)
let movePipes = SKAction.moveByX(-distanceToMove, y:0.0, duration:NSTimeInterval(0.01 * distanceToMove))
let removePipes = SKAction.removeFromParent()
movePipesAndRemove = SKAction.sequence([movePipes, removePipes])
// spawn the pipes
let spawn = SKAction.runBlock({() in self.spawnPipes()})
let delay = SKAction.waitForDuration(NSTimeInterval(2.0))
let spawnThenDelay = SKAction.sequence([spawn, delay])
let spawnThenDelayForever = SKAction.repeatActionForever(spawnThenDelay)
self.runAction(spawnThenDelayForever)
// setup our bird
let birdTexture1 = SKTexture(imageNamed: "bird-01")
birdTexture1.filteringMode = SKTextureFilteringMode.Nearest
let birdTexture2 = SKTexture(imageNamed: "bird-02")
birdTexture2.filteringMode = SKTextureFilteringMode.Nearest
let anim = SKAction.animateWithTextures([birdTexture1, birdTexture2], timePerFrame: 0.2)
let flap = SKAction.repeatActionForever(anim)
bird = SKSpriteNode(texture: birdTexture1)
bird.setScale(2.0)
bird.position = CGPoint(x: self.frame.size.width * 0.35, y:self.frame.size.height * 0.6)
bird.runAction(flap)
bird.physicsBody = SKPhysicsBody(circleOfRadius: bird.size.height / 2.0)
bird.physicsBody.dynamic = true
bird.physicsBody.allowsRotation = false
bird.physicsBody.categoryBitMask = birdCategory
bird.physicsBody.collisionBitMask = worldCategory | pipeCategory
bird.physicsBody.contactTestBitMask = worldCategory | pipeCategory
self.addChild(bird)
// create the ground
var ground = SKNode()
ground.position = CGPointMake(0, groundTexture.size().height)
ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width, groundTexture.size().height * 2.0))
ground.physicsBody.dynamic = false
ground.physicsBody.categoryBitMask = worldCategory
self.addChild(ground)
// Initialize label and create a label which holds the score
score = 0
scoreLabelNode = SKLabelNode(fontNamed:"MarkerFelt-Wide")
scoreLabelNode.position = CGPointMake( CGRectGetMidX( self.frame ), 3 * self.frame.size.height / 4 )
scoreLabelNode.zPosition = 100
scoreLabelNode.text = String(score)
self.addChild(scoreLabelNode)
}
func spawnPipes() {
let pipePair = SKNode()
pipePair.position = CGPointMake( self.frame.size.width + pipeTextureUp.size().width * 2, 0 )
pipePair.zPosition = -10
let height = UInt32( self.frame.size.height / 4 )
let y = arc4random() % height + height
let pipeDown = SKSpriteNode(texture: pipeTextureDown)
pipeDown.setScale(2.0)
pipeDown.position = CGPointMake(0.0, CGFloat(y) + pipeDown.size.height + CGFloat(verticalPipeGap))
pipeDown.physicsBody = SKPhysicsBody(rectangleOfSize: pipeDown.size)
pipeDown.physicsBody.dynamic = false
pipeDown.physicsBody.categoryBitMask = pipeCategory
pipeDown.physicsBody.contactTestBitMask = birdCategory
pipePair.addChild(pipeDown)
let pipeUp = SKSpriteNode(texture: pipeTextureUp)
pipeUp.setScale(2.0)
pipeUp.position = CGPointMake(0.0, CGFloat(y))
pipeUp.physicsBody = SKPhysicsBody(rectangleOfSize: pipeUp.size)
pipeUp.physicsBody.dynamic = false
pipeUp.physicsBody.categoryBitMask = pipeCategory
pipeUp.physicsBody.contactTestBitMask = birdCategory
pipePair.addChild(pipeUp)
var contactNode = SKNode()
contactNode.position = CGPointMake( pipeDown.size.width + bird.size.width / 2, CGRectGetMidY( self.frame ) )
contactNode.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake( pipeUp.size.width, self.frame.size.height ))
contactNode.physicsBody.dynamic = false
contactNode.physicsBody.categoryBitMask = scoreCategory
contactNode.physicsBody.contactTestBitMask = birdCategory
pipePair.addChild(contactNode)
pipePair.runAction(movePipesAndRemove)
pipes.addChild(pipePair)
}
func resetScene (){
// Move bird to original position and reset velocity
bird.position = CGPointMake(self.frame.size.width / 2.5, CGRectGetMidY(self.frame))
bird.physicsBody.velocity = CGVectorMake( 0, 0 )
bird.physicsBody.collisionBitMask = worldCategory | pipeCategory
bird.speed = 1.0
bird.zRotation = 0.0
// Remove all existing pipes
pipes.removeAllChildren()
// Reset _canRestart
canRestart = false
// Reset score
score = 0
scoreLabelNode.text = String(score)
// Restart animation
moving.speed = 1
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
if moving.speed > 0 {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
bird.physicsBody.velocity = CGVectorMake(0, 0)
bird.physicsBody.applyImpulse(CGVectorMake(0, 30))
}
}else if canRestart {
self.resetScene()
}
}
// TODO: Move to utilities somewhere. There's no reason this should be a member function
func clamp(min: CGFloat, max: CGFloat, value: CGFloat) -> CGFloat {
if( value > max ) {
return max
} else if( value < min ) {
return min
} else {
return value
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
bird.zRotation = self.clamp( -1, max: 0.5, value: bird.physicsBody.velocity.dy * ( bird.physicsBody.velocity.dy < 0 ? 0.003 : 0.001 ) )
}
func didBeginContact(contact: SKPhysicsContact) {
if moving.speed > 0 {
if ( contact.bodyA.categoryBitMask & scoreCategory ) == scoreCategory || ( contact.bodyB.categoryBitMask & scoreCategory ) == scoreCategory {
// Bird has contact with score entity
score++
scoreLabelNode.text = String(score)
// Add a little visual feedback for the score increment
scoreLabelNode.runAction(SKAction.sequence([SKAction.scaleTo(1.5, duration:NSTimeInterval(0.1)), SKAction.scaleTo(1.0, duration:NSTimeInterval(0.1))]))
} else {
moving.speed = 0
bird.physicsBody.collisionBitMask = worldCategory
bird.runAction( SKAction.rotateByAngle(CGFloat(M_PI) * CGFloat(bird.position.y) * 0.01, duration:1), completion:{self.bird.speed = 0 })
// Flash background if contact is detected
self.removeActionForKey("flash")
self.runAction(SKAction.sequence([SKAction.repeatAction(SKAction.sequence([SKAction.runBlock({
self.backgroundColor = SKColor(red: 1, green: 0, blue: 0, alpha: 1.0)
}),SKAction.waitForDuration(NSTimeInterval(0.05)), SKAction.runBlock({
self.backgroundColor = self.skyColor
}), SKAction.waitForDuration(NSTimeInterval(0.05))]), count:4), SKAction.runBlock({
self.canRestart = true
})]), withKey: "flash")
}
}
}
}