├── .DS_Store ├── README.md ├── SkyNinja.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings │ └── xcuserdata │ │ └── apiapia.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── WorkspaceSettings.xcsettings └── xcuserdata │ └── apiapia.xcuserdatad │ └── xcschemes │ ├── SkyNinja.xcscheme │ └── xcschememanagement.plist └── SkyNinja ├── .DS_Store ├── AppDelegate.swift ├── Assets.xcassets ├── .DS_Store ├── AppIcon.appiconset │ ├── Contents.json │ ├── icon-1024.png │ ├── icon-20-ipad.png │ ├── icon-20@2x-ipad.png │ ├── icon-20@2x.png │ ├── icon-20@3x.png │ ├── icon-29-ipad.png │ ├── icon-29.png │ ├── icon-29@2x-ipad.png │ ├── icon-29@2x.png │ ├── icon-29@3x.png │ ├── icon-40.png │ ├── icon-40@2x.png │ ├── icon-40@3x.png │ ├── icon-50.png │ ├── icon-50@2x.png │ ├── icon-57.png │ ├── icon-57@2x.png │ ├── icon-60@2x.png │ ├── icon-60@3x.png │ ├── icon-72.png │ ├── icon-72@2x.png │ ├── icon-76.png │ ├── icon-76@2x.png │ └── icon-83.5@2x.png ├── Contents.json ├── Learning.imageset │ ├── Contents.json │ └── Learning.png ├── Star.imageset │ ├── Contents.json │ └── Star.png ├── bg.imageset │ ├── Contents.json │ └── bg.png ├── bomb.imageset │ ├── Contents.json │ └── bomb.png ├── coin.imageset │ ├── Contents.json │ └── coin.png ├── ground.imageset │ ├── Contents.json │ └── ground.png ├── ifiero-logo.imageset │ ├── Contents.json │ └── ifiero-logo.png ├── play.imageset │ ├── Contents.json │ └── play.png ├── t10001.imageset │ ├── Contents.json │ └── t10001.png ├── t1Mask.imageset │ ├── Contents.json │ └── t1Mask.png ├── tapToPlay.imageset │ ├── Contents.json │ └── tapToPlay.png ├── top.imageset │ ├── Contents.json │ └── top.png ├── tree.imageset │ ├── Contents.json │ └── cloud.png ├── youlose.imageset │ ├── Contents.json │ └── youlose.png └── youwon.imageset │ ├── Contents.json │ └── youwon.png ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Constant.swift ├── Elements Class ├── LineNode.swift ├── PlayerNodeClass.swift └── SpawnElements.swift ├── GameScene.sks ├── GameScene.swift ├── GameStates ├── GameOverState.swift ├── PlayState.swift └── WaitingState.swift ├── GameViewController.swift ├── Info.plist ├── Resources ├── .DS_Store ├── Images │ ├── .DS_Store │ └── Ninja.atlas │ │ ├── .DS_Store │ │ ├── t10001.png │ │ ├── t10002.png │ │ ├── t10003.png │ │ ├── t10004.png │ │ ├── t10005.png │ │ ├── t10006.png │ │ ├── t10007.png │ │ └── t10008.png ├── Particles │ ├── CollectNormal.sks │ ├── Trail.sks │ └── death.sks ├── SoundsFiles │ ├── .DS_Store │ ├── Bomb.wav │ ├── background.mp3 │ ├── coin.wav │ └── ninjaHit.wav └── Utils │ ├── CGFloat+Extension.swift │ └── CGVector+Extensions.swift └── death.caf /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SKY NINJA SpriteKit Game Tutorial 2 | 3 | GIF动画演示:https://upload-images.jianshu.io/upload_images/3896436-8488f7fa265bdeb1.gif?imageMogr2/auto-orient/strip 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | // 18 | // GameScene.swift 19 | // SkyNinja 20 | /* 21 | * *** 游戏元素使用条款及注意事项 *** 22 | * 23 | * 游戏中的所有元素全部由iFIERO所原创(除注明引用之外),包括人物、音乐、场景等; 24 | * 创作的初衷就是让更多的游戏爱好者可以在开发游戏中获得自豪感 -- 让手机游戏开发变得简单; 25 | * 秉着开源分享的原则,iFIERO发布的游戏都尽可能的易懂实用,并开放所有源码; 26 | * 任何使用者都可以使用游戏中的代码块,也可以进行拷贝、修改、更新、升级,无须再经过iFIERO的同意; 27 | * 但这并不表示可以任意复制、拆分其中的游戏元素: 28 | * 用于[商业目的]而不注明出处; 29 | * 用于[任何教学]而不注明出处; 30 | * 用于[游戏上架]而不注明出处; 31 | * 另外,iFIERO有商用授权游戏元素,获得iFIERO官方授权后,即无任何限制; 32 | * 请尊重帮助过你的iFIERO的知识产权,非常感谢; 33 | * 34 | * Created by VANGO杨 && ANDREW陈 35 | * Copyright © 2018 iFiero. All rights reserved. 36 | * www.iFIERO.com 37 | * iFIERO -- 让手机游戏开发变得简单 38 | * 39 | * SkyNinja 天猪之城 在此游戏中您将获得如下技能: 40 | * 41 | * 1、LaunchScreen 学习如何设置游戏启动画面 42 | * 2、Scene 学习如何切换游戏的游戏场景 43 | * 3、Scene Edit 学习直接使用可见即所得操作编辑游戏场景 44 | * 4、Random 利用可复用的随机函数生成Enemy 45 | * 5、SpriteNode class 学习建立独立的class精灵并引入场景scene 46 | * 6、Collision 学习有节点与节点之间的碰撞的原理及处理方法 47 | * 7、Animation&Atlas 学习如何导入动画帧及何为Atlas 48 | * 8、Camera 使用Camera实现endless背景滚动 49 | * 9、Grarity 学习如何点击屏幕时反转重力 50 | * 10、StateMachine GameplayKit 运用之场景切换;(**** 中级技能) 51 | * 11、Partilces 学习如何做特效及把特效发生碰撞时移出场景;(**** 中级技能) 52 | * 53 | */ 54 | 55 | ``` 56 | import SpriteKit 57 | import GameplayKit 58 | 59 | class GameScene: SKScene ,SKPhysicsContactDelegate{ 60 | 61 | var moveAllowed = false /// 场景是否可以移动了; 62 | 63 | //MARK: - StateMachine 场景中各个舞台State 64 | lazy var stateMachine:GKStateMachine = GKStateMachine(states: [ 65 | WaitingState(scene: self), //self 为 GameScene ,把GameScene专入State 66 | PlayState(scene: self), 67 | GameOverState(scene: self) 68 | ]) 69 | 70 | //MARK: - 场景中的所有SpriteNode 71 | /* 72 | * 1.调用 Elements Class 的节点,须在GameScene的把节点的Custom Class设为PlayerNode 73 | * 2.Module 设为项目名称 SkyNinja 74 | * 3.为何要设置独立的class精灵,可以为GameScene减少代码,并有利于代码的复用; 75 | */ 76 | var playerNode:PlayerNodeClass! 77 | var coinTempNode:SKSpriteNode! 78 | var bombTempNode:SKSpriteNode! 79 | var mainCamera:SKCameraNode! 80 | var groundNode:SKSpriteNode! /// 地面 81 | var skyNode:SKSpriteNode! /// 天空 82 | var spawnElements = SpawnElements() /// 生成节点工具 特别注意,这里非 spawnElements = SpawnElements! 83 | 84 | private var dt:TimeInterval = 0 /// 每一frame的时间差 85 | private var lastUpdateTimeInterval:TimeInterval = 0 86 | 87 | override func didMove(to view: SKView) { 88 | super.didMove(to: view) 89 | self.physicsWorld.gravity = CGVector(dx: 0, dy: -9.8) /// 物理世界的重力 90 | self.physicsWorld.contactDelegate = self /// 碰撞代理 91 | 92 | initCamera() /// Camera 93 | initBgMusic() /// 背景音乐 94 | initPlayer() /// 初始化玩家 95 | initCoinBomb() /// 临时的Coin+Bomb 96 | initSkyGroundLine() // 建立物理天空+地面 97 | stateMachine.enter(WaitingState.self) /// 初始化以上的各个精灵SpriteNode后,再进入WaitingState 场景舞台State 98 | } 99 | //MARK: - 加入Camera 100 | func initCamera(){ 101 | mainCamera = childNode(withName: "MainCamera") as! SKCameraNode 102 | } 103 | //MARK: - 移动Camera 104 | func moveCamera(){ 105 | self.mainCamera.position.x += CAMERA_MOVE_XPOS ///向右移动 106 | } 107 | //MARK: - 停止Camera 108 | func stopCamera(){ 109 | self.mainCamera.removeAllActions() 110 | } 111 | // MARK:-初始化玩家 112 | func initPlayer(){ 113 | 114 | playerNode = childNode(withName: "Player") as! PlayerNodeClass 115 | playerNode.physicsBody?.affectedByGravity = true 116 | playerNode.initPlayer() 117 | } 118 | // MARK:-背景音乐 119 | func initBgMusic(){ 120 | 121 | let bgMusic = SKAudioNode(fileNamed: "background.mp3") 122 | bgMusic.autoplayLooped = true 123 | addChild(bgMusic) 124 | } 125 | 126 | func initCoinBomb(){ 127 | coinTempNode = childNode(withName: "CoinTemp") as! SKSpriteNode 128 | bombTempNode = childNode(withName: "BombTemp") as! SKSpriteNode 129 | } 130 | //MARK: - 物理线 131 | func initSkyGroundLine(){ 132 | 133 | skyNode = childNode(withName: "Sky") as! SKSpriteNode 134 | let sykLine = LineNode() /// 生成新的节点 比如 let newNode = SKNode() 135 | sykLine.initSkyLine(size: size, yPos: skyNode.position.y + 10) 136 | addChild(sykLine) 137 | 138 | 139 | groundNode = childNode(withName: "Ground") as! SKSpriteNode 140 | let groundLine = LineNode() 141 | groundLine.initGroundLine(size: size, yPos: groundNode.position.y + groundNode.size.height - 10) 142 | addChild(groundLine) 143 | 144 | } 145 | 146 | // MARK: - 反转物理世界; 147 | func reverseGravity(){ 148 | physicsWorld.gravity *= -1 149 | } 150 | 151 | // MARK: - 根据 camera.position.x 移动所有页面元素; 152 | ///因为节点anchorPoint为(0,0),且相机的初始位置为 1024,所以要把相机的位置扣除1024 即(camera.position.x - self.size.width / 2) 153 | func moveSprites(camera:SKCameraNode){ 154 | /// 所有的天空精灵 155 | enumerateChildNodes(withName: "Sky") { (node, error) in 156 | if node.position.x + self.size.width < (camera.position.x - self.size.width / 2) { 157 | node.position.x += self.size.width * SCENE_NUMBERS 158 | } 159 | } 160 | /// 所有的地面精灵; 161 | enumerateChildNodes(withName: "Ground") { (node, error) in 162 | if node.position.x + self.size.width < (camera.position.x - self.size.width / 2 ) { 163 | node.position.x += self.size.width * SCENE_NUMBERS 164 | } 165 | /// print("所有的地面精灵",node.position.x,(camera.position.x - self.size.width / 2 )) 166 | } 167 | /// 所有线和Camera同步 168 | enumerateChildNodes(withName: "Line") { (node, error) in 169 | // let node = node as! SKNode 170 | node.position.x += CAMERA_MOVE_XPOS 171 | if node.position.x < -self.size.width { 172 | node.position.x += self.size.width * SCENE_NUMBERS 173 | } 174 | } 175 | /// 所有树 176 | /// 树为何不:(camera.position.x - self.size.width / 2 ),请注意树的 anchorPoint(0.5,0.5) 177 | enumerateChildNodes(withName: "Tree") { (node, error) in 178 | if node.position.x + self.size.width < (camera.position.x ) { 179 | node.position.x += self.size.width * SCENE_NUMBERS 180 | } 181 | } 182 | /// 所有背景 183 | enumerateChildNodes(withName: "Bg") { (node, error) in 184 | 185 | if node.position.x + self.size.width < (camera.position.x - self.size.width / 2 ) { 186 | node.position.x += self.size.width * SCENE_NUMBERS 187 | } 188 | } 189 | } 190 | // MARK: - 生成节点工具 class 191 | @objc func spawnCoins(){ 192 | /// print(spawnElements.spawnCoin(camera: mainCamera)) 193 | if moveAllowed { 194 | self.addChild(spawnElements.spawnCoin(camera: mainCamera)) /// 传入主相机位置 195 | } 196 | 197 | } 198 | 199 | @objc func spawnBombs(){ 200 | if moveAllowed { 201 | addChild(spawnElements.spawnBomb(camera: mainCamera,scene: self)) /// 传入主相机位置 202 | } 203 | } 204 | 205 | @objc func removeCoins(){ 206 | enumerateChildNodes(withName: "coin") { (node, error) in 207 | if node.position.x < self.mainCamera.position.x - self.size.width { 208 | /// print("移除coin") 209 | node.removeFromParent() 210 | } 211 | } 212 | } 213 | // MARK: - 不再生成了; 214 | func stopSpawning(){ 215 | 216 | playerNode.removeAction(forKey: "jogging") /// 移除人物的运动; 217 | 218 | enumerateChildNodes(withName: "coin") { (node, error) in 219 | node.removeAllActions() 220 | } 221 | enumerateChildNodes(withName: "bomb") { (node, error) in 222 | node.removeAllActions() 223 | } 224 | } 225 | 226 | //MARK: - 重新开始游戏; 227 | func restartGame(){ 228 | 229 | let newScene = GameScene(fileNamed: "GameScene")! 230 | newScene.size = CGSize(width: SCENE_WIDTH, height: SCENE_HEIGHT) 231 | newScene.anchorPoint = CGPoint(x: 0, y: 0) 232 | newScene.scaleMode = .aspectFill 233 | let transition = SKTransition.flipHorizontal(withDuration: 0.5) 234 | view?.presentScene(newScene, transition:transition) 235 | } 236 | 237 | // MARK: - 监测屏幕点击事件 238 | override func touchesBegan(_ touches: Set, with event: UIEvent?) { 239 | guard let touch = touches.first else { 240 | return 241 | } 242 | let touchLocation = touch.location(in: self) ///获得点击的位置 243 | /// 判断目前的GameScene场景舞台是哪个state 244 | switch stateMachine.currentState { 245 | case is WaitingState: 246 | /// 获得按钮的点击位置 247 | guard let body = physicsWorld.body(at: touchLocation) else { 248 | return 249 | } 250 | /// 判断是否是点击了PlayButton 251 | guard let playButton = body.node?.childNode(withName: "PlayButton") as? SKSpriteNode else { 252 | return 253 | } 254 | /// 如果点击位置是在PlayButton 255 | if (playButton.contains(touchLocation)){ 256 | playButton.isHidden = true 257 | stateMachine.enter(PlayState.self) /// 进入开始游戏; 258 | } 259 | 260 | case is PlayState: 261 | reverseGravity() /// 反转物理世界; 262 | 263 | case is GameOverState: 264 | 265 | guard let body = physicsWorld.body(at: touchLocation) else { 266 | return 267 | } 268 | // TapToPlay按钮; 269 | if let tapToPlay = body.node?.childNode(withName: "tapToPlay"){ 270 | 271 | if tapToPlay.contains(touchLocation){ 272 | print("重新开始游戏!") 273 | restartGame() 274 | } 275 | } 276 | 277 | default: 278 | break; 279 | 280 | 281 | } 282 | } 283 | // MARK: - 监测碰撞 284 | func didBegin(_ contact: SKPhysicsContact) { 285 | 286 | let bodyA:SKPhysicsBody 287 | let bodyB:SKPhysicsBody 288 | if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask { 289 | bodyA = contact.bodyA 290 | bodyB = contact.bodyB 291 | }else{ 292 | bodyA = contact.bodyB 293 | bodyB = contact.bodyA 294 | } 295 | ///检测碰到中间线 296 | if bodyA.categoryBitMask == PhysicsCategory.Player && bodyB.categoryBitMask == PhysicsCategory.MiddleLine { 297 | /// print("碰到屏幕线人物反转") 298 | playerNode.reversePlayer() 299 | } 300 | 301 | ///检测碰到coin 302 | if bodyA.categoryBitMask == PhysicsCategory.Player && bodyB.categoryBitMask == PhysicsCategory.Coin { 303 | /// print("碰到屏幕线人物反转") 304 | let coinAction = SKAction.playSoundFileNamed("coin.wav", waitForCompletion: false) 305 | run(coinAction) 306 | bodyB.node?.removeFromParent() 307 | } 308 | 309 | ///检测碰到Bomb 310 | if bodyA.categoryBitMask == PhysicsCategory.Player && bodyB.categoryBitMask == PhysicsCategory.Bomb { 311 | /// 播放音乐 312 | let bombAction = SKAction.playSoundFileNamed("ninjaHit.wav", waitForCompletion: false) 313 | run(bombAction) 314 | /// 移除BOMB 315 | /// bodyB.node?.removeFromParent() 316 | stateMachine.enter(GameOverState.self) 317 | } 318 | 319 | } 320 | // MARK: - 时时更新update 321 | override func update(_ currentTime: TimeInterval) { 322 | 323 | /// 获取时间差 324 | if lastUpdateTimeInterval == 0 { 325 | lastUpdateTimeInterval = currentTime 326 | } 327 | dt = currentTime - lastUpdateTimeInterval 328 | lastUpdateTimeInterval = currentTime 329 | 330 | stateMachine.update(deltaTime: dt) /// 把update传进各个State里; 331 | } 332 | } 333 | ``` 334 | 335 |
源码传送门:https://github.com/apiapia/SkyNinjaGameSpriteKitTutorial 336 |
百度源码备份:https://pan.baidu.com/s/1_1rt4fZR2OuDSg-lFDAUQw 337 |
更多手机游戏教程:http://www.iFIERO.com 338 | -------------------------------------------------------------------------------- /SkyNinja.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2103A5F620CE02C20061DA6A /* PlayerNodeClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2103A5F520CE02C20061DA6A /* PlayerNodeClass.swift */; }; 11 | 2103A5F820CE0A590061DA6A /* Constant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2103A5F720CE0A590061DA6A /* Constant.swift */; }; 12 | 2103A5FE20CE712F0061DA6A /* LineNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2103A5FD20CE712F0061DA6A /* LineNode.swift */; }; 13 | 21302A6F20C8066E0014029B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21302A6E20C8066E0014029B /* AppDelegate.swift */; }; 14 | 21302A7120C8066E0014029B /* GameScene.sks in Resources */ = {isa = PBXBuildFile; fileRef = 21302A7020C8066E0014029B /* GameScene.sks */; }; 15 | 21302A7520C8066E0014029B /* GameScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21302A7420C8066E0014029B /* GameScene.swift */; }; 16 | 21302A7720C8066E0014029B /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21302A7620C8066E0014029B /* GameViewController.swift */; }; 17 | 21302A7A20C8066E0014029B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 21302A7820C8066E0014029B /* Main.storyboard */; }; 18 | 21302A7C20C8066F0014029B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 21302A7B20C8066F0014029B /* Assets.xcassets */; }; 19 | 21302A7F20C8066F0014029B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 21302A7D20C8066F0014029B /* LaunchScreen.storyboard */; }; 20 | 21302A8720C807DD0014029B /* Ninja.atlas in Resources */ = {isa = PBXBuildFile; fileRef = 21302A8620C807DD0014029B /* Ninja.atlas */; }; 21 | 21302A8B20C809410014029B /* background.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 21302A8A20C809410014029B /* background.mp3 */; }; 22 | 21302A8F20C809CD0014029B /* ninjaHit.wav in Resources */ = {isa = PBXBuildFile; fileRef = 21302A8E20C809CD0014029B /* ninjaHit.wav */; }; 23 | 21302A9120C809D80014029B /* Bomb.wav in Resources */ = {isa = PBXBuildFile; fileRef = 21302A9020C809D80014029B /* Bomb.wav */; }; 24 | 21302A9320C80A900014029B /* coin.wav in Resources */ = {isa = PBXBuildFile; fileRef = 21302A9220C80A900014029B /* coin.wav */; }; 25 | 21374EAC20F4A4E60018FD54 /* CollectNormal.sks in Resources */ = {isa = PBXBuildFile; fileRef = 21374EAB20F4A4E60018FD54 /* CollectNormal.sks */; }; 26 | 21374EB320F4B1BF0018FD54 /* death.sks in Resources */ = {isa = PBXBuildFile; fileRef = 21374EB220F4B1BF0018FD54 /* death.sks */; }; 27 | 21374EB620F4B5E70018FD54 /* death.caf in Resources */ = {isa = PBXBuildFile; fileRef = 21374EB520F4B58F0018FD54 /* death.caf */; }; 28 | 21568E8020CD302800800669 /* CGVector+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21568E7F20CD302800800669 /* CGVector+Extensions.swift */; }; 29 | 21568E8220CD302E00800669 /* CGFloat+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21568E8120CD302E00800669 /* CGFloat+Extension.swift */; }; 30 | 21568E8720CD559600800669 /* PlayState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21568E8620CD559600800669 /* PlayState.swift */; }; 31 | 21568E8920CD567500800669 /* GameOverState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21568E8820CD567500800669 /* GameOverState.swift */; }; 32 | 21568E8D20CD592C00800669 /* WaitingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21568E8C20CD592C00800669 /* WaitingState.swift */; }; 33 | 21FE2DD720CF4C4D0032CE81 /* SpawnElements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21FE2DD620CF4C4D0032CE81 /* SpawnElements.swift */; }; 34 | 21FE2DDC20CF6F3D0032CE81 /* Trail.sks in Resources */ = {isa = PBXBuildFile; fileRef = 21FE2DDB20CF6F3D0032CE81 /* Trail.sks */; }; 35 | /* End PBXBuildFile section */ 36 | 37 | /* Begin PBXFileReference section */ 38 | 2103A5F520CE02C20061DA6A /* PlayerNodeClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerNodeClass.swift; sourceTree = ""; }; 39 | 2103A5F720CE0A590061DA6A /* Constant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constant.swift; sourceTree = ""; }; 40 | 2103A5FD20CE712F0061DA6A /* LineNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineNode.swift; sourceTree = ""; }; 41 | 21302A6B20C8066E0014029B /* SkyNinja.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SkyNinja.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 21302A6E20C8066E0014029B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 43 | 21302A7020C8066E0014029B /* GameScene.sks */ = {isa = PBXFileReference; lastKnownFileType = file.sks; path = GameScene.sks; sourceTree = ""; }; 44 | 21302A7420C8066E0014029B /* GameScene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameScene.swift; sourceTree = ""; }; 45 | 21302A7620C8066E0014029B /* GameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameViewController.swift; sourceTree = ""; }; 46 | 21302A7920C8066E0014029B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 21302A7B20C8066F0014029B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 21302A7E20C8066F0014029B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 21302A8020C8066F0014029B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | 21302A8620C807DD0014029B /* Ninja.atlas */ = {isa = PBXFileReference; lastKnownFileType = folder.skatlas; path = Ninja.atlas; sourceTree = ""; }; 51 | 21302A8A20C809410014029B /* background.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = background.mp3; sourceTree = ""; }; 52 | 21302A8E20C809CD0014029B /* ninjaHit.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = ninjaHit.wav; sourceTree = ""; }; 53 | 21302A9020C809D80014029B /* Bomb.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = Bomb.wav; sourceTree = ""; }; 54 | 21302A9220C80A900014029B /* coin.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = coin.wav; sourceTree = ""; }; 55 | 21374EAB20F4A4E60018FD54 /* CollectNormal.sks */ = {isa = PBXFileReference; lastKnownFileType = file.sks; path = CollectNormal.sks; sourceTree = ""; }; 56 | 21374EB220F4B1BF0018FD54 /* death.sks */ = {isa = PBXFileReference; lastKnownFileType = file.sks; path = death.sks; sourceTree = ""; }; 57 | 21374EB520F4B58F0018FD54 /* death.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = death.caf; path = SkyNinja/death.caf; sourceTree = SOURCE_ROOT; }; 58 | 21568E7F20CD302800800669 /* CGVector+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGVector+Extensions.swift"; sourceTree = ""; }; 59 | 21568E8120CD302E00800669 /* CGFloat+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGFloat+Extension.swift"; sourceTree = ""; }; 60 | 21568E8620CD559600800669 /* PlayState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayState.swift; sourceTree = ""; }; 61 | 21568E8820CD567500800669 /* GameOverState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameOverState.swift; sourceTree = ""; }; 62 | 21568E8C20CD592C00800669 /* WaitingState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitingState.swift; sourceTree = ""; }; 63 | 21FE2DD620CF4C4D0032CE81 /* SpawnElements.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpawnElements.swift; sourceTree = ""; }; 64 | 21FE2DDB20CF6F3D0032CE81 /* Trail.sks */ = {isa = PBXFileReference; lastKnownFileType = file.sks; path = Trail.sks; sourceTree = ""; }; 65 | /* End PBXFileReference section */ 66 | 67 | /* Begin PBXFrameworksBuildPhase section */ 68 | 21302A6820C8066E0014029B /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXFrameworksBuildPhase section */ 76 | 77 | /* Begin PBXGroup section */ 78 | 2103A5F420CE02870061DA6A /* Elements Class */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 2103A5F520CE02C20061DA6A /* PlayerNodeClass.swift */, 82 | 2103A5FD20CE712F0061DA6A /* LineNode.swift */, 83 | 21FE2DD620CF4C4D0032CE81 /* SpawnElements.swift */, 84 | ); 85 | path = "Elements Class"; 86 | sourceTree = ""; 87 | }; 88 | 21302A6220C8066E0014029B = { 89 | isa = PBXGroup; 90 | children = ( 91 | 21302A6D20C8066E0014029B /* SkyNinja */, 92 | 21302A6C20C8066E0014029B /* Products */, 93 | ); 94 | sourceTree = ""; 95 | }; 96 | 21302A6C20C8066E0014029B /* Products */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 21302A6B20C8066E0014029B /* SkyNinja.app */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | 21302A6D20C8066E0014029B /* SkyNinja */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 21568E8320CD554300800669 /* GameStates */, 108 | 2103A5F420CE02870061DA6A /* Elements Class */, 109 | 21302A8820C809290014029B /* Resources */, 110 | 21302A6E20C8066E0014029B /* AppDelegate.swift */, 111 | 21302A7020C8066E0014029B /* GameScene.sks */, 112 | 21302A7420C8066E0014029B /* GameScene.swift */, 113 | 21302A7620C8066E0014029B /* GameViewController.swift */, 114 | 21302A7820C8066E0014029B /* Main.storyboard */, 115 | 2103A5F720CE0A590061DA6A /* Constant.swift */, 116 | 21302A7B20C8066F0014029B /* Assets.xcassets */, 117 | 21302A7D20C8066F0014029B /* LaunchScreen.storyboard */, 118 | 21302A8020C8066F0014029B /* Info.plist */, 119 | ); 120 | path = SkyNinja; 121 | sourceTree = ""; 122 | }; 123 | 21302A8820C809290014029B /* Resources */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 21FE2DD820CF6D210032CE81 /* Particles */, 127 | 21302A9520C821B60014029B /* Utils */, 128 | 21302A9420C821960014029B /* Images */, 129 | 21302A8920C809300014029B /* SoundsFiles */, 130 | ); 131 | path = Resources; 132 | sourceTree = ""; 133 | }; 134 | 21302A8920C809300014029B /* SoundsFiles */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 21374EB520F4B58F0018FD54 /* death.caf */, 138 | 21302A9220C80A900014029B /* coin.wav */, 139 | 21302A8A20C809410014029B /* background.mp3 */, 140 | 21302A8E20C809CD0014029B /* ninjaHit.wav */, 141 | 21302A9020C809D80014029B /* Bomb.wav */, 142 | ); 143 | path = SoundsFiles; 144 | sourceTree = ""; 145 | }; 146 | 21302A9420C821960014029B /* Images */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | 21302A8620C807DD0014029B /* Ninja.atlas */, 150 | ); 151 | path = Images; 152 | sourceTree = ""; 153 | }; 154 | 21302A9520C821B60014029B /* Utils */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 21568E8120CD302E00800669 /* CGFloat+Extension.swift */, 158 | 21568E7F20CD302800800669 /* CGVector+Extensions.swift */, 159 | ); 160 | path = Utils; 161 | sourceTree = ""; 162 | }; 163 | 21568E8320CD554300800669 /* GameStates */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | 21568E8C20CD592C00800669 /* WaitingState.swift */, 167 | 21568E8620CD559600800669 /* PlayState.swift */, 168 | 21568E8820CD567500800669 /* GameOverState.swift */, 169 | ); 170 | path = GameStates; 171 | sourceTree = ""; 172 | }; 173 | 21FE2DD820CF6D210032CE81 /* Particles */ = { 174 | isa = PBXGroup; 175 | children = ( 176 | 21374EB220F4B1BF0018FD54 /* death.sks */, 177 | 21374EAB20F4A4E60018FD54 /* CollectNormal.sks */, 178 | 21FE2DDB20CF6F3D0032CE81 /* Trail.sks */, 179 | ); 180 | path = Particles; 181 | sourceTree = ""; 182 | }; 183 | /* End PBXGroup section */ 184 | 185 | /* Begin PBXNativeTarget section */ 186 | 21302A6A20C8066E0014029B /* SkyNinja */ = { 187 | isa = PBXNativeTarget; 188 | buildConfigurationList = 21302A8320C8066F0014029B /* Build configuration list for PBXNativeTarget "SkyNinja" */; 189 | buildPhases = ( 190 | 21302A6720C8066E0014029B /* Sources */, 191 | 21302A6820C8066E0014029B /* Frameworks */, 192 | 21302A6920C8066E0014029B /* Resources */, 193 | ); 194 | buildRules = ( 195 | ); 196 | dependencies = ( 197 | ); 198 | name = SkyNinja; 199 | productName = SkyNinja; 200 | productReference = 21302A6B20C8066E0014029B /* SkyNinja.app */; 201 | productType = "com.apple.product-type.application"; 202 | }; 203 | /* End PBXNativeTarget section */ 204 | 205 | /* Begin PBXProject section */ 206 | 21302A6320C8066E0014029B /* Project object */ = { 207 | isa = PBXProject; 208 | attributes = { 209 | LastSwiftUpdateCheck = 0940; 210 | LastUpgradeCheck = 0940; 211 | ORGANIZATIONNAME = iFiero; 212 | TargetAttributes = { 213 | 21302A6A20C8066E0014029B = { 214 | CreatedOnToolsVersion = 9.4; 215 | }; 216 | }; 217 | }; 218 | buildConfigurationList = 21302A6620C8066E0014029B /* Build configuration list for PBXProject "SkyNinja" */; 219 | compatibilityVersion = "Xcode 9.3"; 220 | developmentRegion = en; 221 | hasScannedForEncodings = 0; 222 | knownRegions = ( 223 | en, 224 | Base, 225 | ); 226 | mainGroup = 21302A6220C8066E0014029B; 227 | productRefGroup = 21302A6C20C8066E0014029B /* Products */; 228 | projectDirPath = ""; 229 | projectRoot = ""; 230 | targets = ( 231 | 21302A6A20C8066E0014029B /* SkyNinja */, 232 | ); 233 | }; 234 | /* End PBXProject section */ 235 | 236 | /* Begin PBXResourcesBuildPhase section */ 237 | 21302A6920C8066E0014029B /* Resources */ = { 238 | isa = PBXResourcesBuildPhase; 239 | buildActionMask = 2147483647; 240 | files = ( 241 | 21374EB620F4B5E70018FD54 /* death.caf in Resources */, 242 | 21302A8B20C809410014029B /* background.mp3 in Resources */, 243 | 21374EAC20F4A4E60018FD54 /* CollectNormal.sks in Resources */, 244 | 21302A7A20C8066E0014029B /* Main.storyboard in Resources */, 245 | 21302A7120C8066E0014029B /* GameScene.sks in Resources */, 246 | 21302A8F20C809CD0014029B /* ninjaHit.wav in Resources */, 247 | 21302A7C20C8066F0014029B /* Assets.xcassets in Resources */, 248 | 21302A9120C809D80014029B /* Bomb.wav in Resources */, 249 | 21FE2DDC20CF6F3D0032CE81 /* Trail.sks in Resources */, 250 | 21302A9320C80A900014029B /* coin.wav in Resources */, 251 | 21302A7F20C8066F0014029B /* LaunchScreen.storyboard in Resources */, 252 | 21374EB320F4B1BF0018FD54 /* death.sks in Resources */, 253 | 21302A8720C807DD0014029B /* Ninja.atlas in Resources */, 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | }; 257 | /* End PBXResourcesBuildPhase section */ 258 | 259 | /* Begin PBXSourcesBuildPhase section */ 260 | 21302A6720C8066E0014029B /* Sources */ = { 261 | isa = PBXSourcesBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | 21568E8020CD302800800669 /* CGVector+Extensions.swift in Sources */, 265 | 2103A5FE20CE712F0061DA6A /* LineNode.swift in Sources */, 266 | 21568E8D20CD592C00800669 /* WaitingState.swift in Sources */, 267 | 2103A5F620CE02C20061DA6A /* PlayerNodeClass.swift in Sources */, 268 | 21302A7520C8066E0014029B /* GameScene.swift in Sources */, 269 | 21568E8920CD567500800669 /* GameOverState.swift in Sources */, 270 | 21302A7720C8066E0014029B /* GameViewController.swift in Sources */, 271 | 21302A6F20C8066E0014029B /* AppDelegate.swift in Sources */, 272 | 21FE2DD720CF4C4D0032CE81 /* SpawnElements.swift in Sources */, 273 | 2103A5F820CE0A590061DA6A /* Constant.swift in Sources */, 274 | 21568E8220CD302E00800669 /* CGFloat+Extension.swift in Sources */, 275 | 21568E8720CD559600800669 /* PlayState.swift in Sources */, 276 | ); 277 | runOnlyForDeploymentPostprocessing = 0; 278 | }; 279 | /* End PBXSourcesBuildPhase section */ 280 | 281 | /* Begin PBXVariantGroup section */ 282 | 21302A7820C8066E0014029B /* Main.storyboard */ = { 283 | isa = PBXVariantGroup; 284 | children = ( 285 | 21302A7920C8066E0014029B /* Base */, 286 | ); 287 | name = Main.storyboard; 288 | sourceTree = ""; 289 | }; 290 | 21302A7D20C8066F0014029B /* LaunchScreen.storyboard */ = { 291 | isa = PBXVariantGroup; 292 | children = ( 293 | 21302A7E20C8066F0014029B /* Base */, 294 | ); 295 | name = LaunchScreen.storyboard; 296 | sourceTree = ""; 297 | }; 298 | /* End PBXVariantGroup section */ 299 | 300 | /* Begin XCBuildConfiguration section */ 301 | 21302A8120C8066F0014029B /* Debug */ = { 302 | isa = XCBuildConfiguration; 303 | buildSettings = { 304 | ALWAYS_SEARCH_USER_PATHS = NO; 305 | CLANG_ANALYZER_NONNULL = YES; 306 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 307 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 308 | CLANG_CXX_LIBRARY = "libc++"; 309 | CLANG_ENABLE_MODULES = YES; 310 | CLANG_ENABLE_OBJC_ARC = YES; 311 | CLANG_ENABLE_OBJC_WEAK = YES; 312 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 313 | CLANG_WARN_BOOL_CONVERSION = YES; 314 | CLANG_WARN_COMMA = YES; 315 | CLANG_WARN_CONSTANT_CONVERSION = YES; 316 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 317 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 318 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 319 | CLANG_WARN_EMPTY_BODY = YES; 320 | CLANG_WARN_ENUM_CONVERSION = YES; 321 | CLANG_WARN_INFINITE_RECURSION = YES; 322 | CLANG_WARN_INT_CONVERSION = YES; 323 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 324 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 325 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 326 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 327 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 328 | CLANG_WARN_STRICT_PROTOTYPES = YES; 329 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 330 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 331 | CLANG_WARN_UNREACHABLE_CODE = YES; 332 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 333 | CODE_SIGN_IDENTITY = "iPhone Developer"; 334 | COPY_PHASE_STRIP = NO; 335 | DEBUG_INFORMATION_FORMAT = dwarf; 336 | ENABLE_STRICT_OBJC_MSGSEND = YES; 337 | ENABLE_TESTABILITY = YES; 338 | GCC_C_LANGUAGE_STANDARD = gnu11; 339 | GCC_DYNAMIC_NO_PIC = NO; 340 | GCC_NO_COMMON_BLOCKS = YES; 341 | GCC_OPTIMIZATION_LEVEL = 0; 342 | GCC_PREPROCESSOR_DEFINITIONS = ( 343 | "DEBUG=1", 344 | "$(inherited)", 345 | ); 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 353 | MTL_ENABLE_DEBUG_INFO = YES; 354 | ONLY_ACTIVE_ARCH = YES; 355 | SDKROOT = iphoneos; 356 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 357 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 358 | }; 359 | name = Debug; 360 | }; 361 | 21302A8220C8066F0014029B /* Release */ = { 362 | isa = XCBuildConfiguration; 363 | buildSettings = { 364 | ALWAYS_SEARCH_USER_PATHS = NO; 365 | CLANG_ANALYZER_NONNULL = YES; 366 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 367 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 368 | CLANG_CXX_LIBRARY = "libc++"; 369 | CLANG_ENABLE_MODULES = YES; 370 | CLANG_ENABLE_OBJC_ARC = YES; 371 | CLANG_ENABLE_OBJC_WEAK = YES; 372 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 373 | CLANG_WARN_BOOL_CONVERSION = YES; 374 | CLANG_WARN_COMMA = YES; 375 | CLANG_WARN_CONSTANT_CONVERSION = YES; 376 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 377 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 378 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 379 | CLANG_WARN_EMPTY_BODY = YES; 380 | CLANG_WARN_ENUM_CONVERSION = YES; 381 | CLANG_WARN_INFINITE_RECURSION = YES; 382 | CLANG_WARN_INT_CONVERSION = YES; 383 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 384 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 385 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 386 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 387 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 388 | CLANG_WARN_STRICT_PROTOTYPES = YES; 389 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 390 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 391 | CLANG_WARN_UNREACHABLE_CODE = YES; 392 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 393 | CODE_SIGN_IDENTITY = "iPhone Developer"; 394 | COPY_PHASE_STRIP = NO; 395 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 396 | ENABLE_NS_ASSERTIONS = NO; 397 | ENABLE_STRICT_OBJC_MSGSEND = YES; 398 | GCC_C_LANGUAGE_STANDARD = gnu11; 399 | GCC_NO_COMMON_BLOCKS = YES; 400 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 401 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 402 | GCC_WARN_UNDECLARED_SELECTOR = YES; 403 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 404 | GCC_WARN_UNUSED_FUNCTION = YES; 405 | GCC_WARN_UNUSED_VARIABLE = YES; 406 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 407 | MTL_ENABLE_DEBUG_INFO = NO; 408 | SDKROOT = iphoneos; 409 | SWIFT_COMPILATION_MODE = wholemodule; 410 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 411 | VALIDATE_PRODUCT = YES; 412 | }; 413 | name = Release; 414 | }; 415 | 21302A8420C8066F0014029B /* Debug */ = { 416 | isa = XCBuildConfiguration; 417 | buildSettings = { 418 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 419 | CODE_SIGN_IDENTITY = "iPhone Developer"; 420 | CODE_SIGN_STYLE = Automatic; 421 | DEVELOPMENT_TEAM = 48X349N2P7; 422 | INFOPLIST_FILE = SkyNinja/Info.plist; 423 | LD_RUNPATH_SEARCH_PATHS = ( 424 | "$(inherited)", 425 | "@executable_path/Frameworks", 426 | ); 427 | PRODUCT_BUNDLE_IDENTIFIER = com.iFiero.SkyNinjaGameTutorial; 428 | PRODUCT_NAME = "$(TARGET_NAME)"; 429 | PROVISIONING_PROFILE = ""; 430 | PROVISIONING_PROFILE_SPECIFIER = ""; 431 | SWIFT_VERSION = 4.0; 432 | TARGETED_DEVICE_FAMILY = "1,2"; 433 | }; 434 | name = Debug; 435 | }; 436 | 21302A8520C8066F0014029B /* Release */ = { 437 | isa = XCBuildConfiguration; 438 | buildSettings = { 439 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 440 | CODE_SIGN_IDENTITY = "iPhone Developer"; 441 | CODE_SIGN_STYLE = Automatic; 442 | DEVELOPMENT_TEAM = 48X349N2P7; 443 | INFOPLIST_FILE = SkyNinja/Info.plist; 444 | LD_RUNPATH_SEARCH_PATHS = ( 445 | "$(inherited)", 446 | "@executable_path/Frameworks", 447 | ); 448 | PRODUCT_BUNDLE_IDENTIFIER = com.iFiero.SkyNinjaGameTutorial; 449 | PRODUCT_NAME = "$(TARGET_NAME)"; 450 | PROVISIONING_PROFILE = ""; 451 | PROVISIONING_PROFILE_SPECIFIER = ""; 452 | SWIFT_VERSION = 4.0; 453 | TARGETED_DEVICE_FAMILY = "1,2"; 454 | }; 455 | name = Release; 456 | }; 457 | /* End XCBuildConfiguration section */ 458 | 459 | /* Begin XCConfigurationList section */ 460 | 21302A6620C8066E0014029B /* Build configuration list for PBXProject "SkyNinja" */ = { 461 | isa = XCConfigurationList; 462 | buildConfigurations = ( 463 | 21302A8120C8066F0014029B /* Debug */, 464 | 21302A8220C8066F0014029B /* Release */, 465 | ); 466 | defaultConfigurationIsVisible = 0; 467 | defaultConfigurationName = Release; 468 | }; 469 | 21302A8320C8066F0014029B /* Build configuration list for PBXNativeTarget "SkyNinja" */ = { 470 | isa = XCConfigurationList; 471 | buildConfigurations = ( 472 | 21302A8420C8066F0014029B /* Debug */, 473 | 21302A8520C8066F0014029B /* Release */, 474 | ); 475 | defaultConfigurationIsVisible = 0; 476 | defaultConfigurationName = Release; 477 | }; 478 | /* End XCConfigurationList section */ 479 | }; 480 | rootObject = 21302A6320C8066E0014029B /* Project object */; 481 | } 482 | -------------------------------------------------------------------------------- /SkyNinja.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SkyNinja.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SkyNinja.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /SkyNinja.xcodeproj/project.xcworkspace/xcuserdata/apiapia.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja.xcodeproj/project.xcworkspace/xcuserdata/apiapia.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /SkyNinja.xcodeproj/project.xcworkspace/xcuserdata/apiapia.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildLocationStyle 6 | UseAppPreferences 7 | CustomBuildLocationType 8 | RelativeToDerivedData 9 | DerivedDataLocationStyle 10 | Default 11 | EnabledFullIndexStoreVisibility 12 | 13 | IssueFilterStyle 14 | ShowActiveSchemeOnly 15 | LiveSourceIssuesEnabled 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /SkyNinja.xcodeproj/xcuserdata/apiapia.xcuserdatad/xcschemes/SkyNinja.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /SkyNinja.xcodeproj/xcuserdata/apiapia.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SkyNinja.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 21302A6A20C8066E0014029B 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /SkyNinja/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/.DS_Store -------------------------------------------------------------------------------- /SkyNinja/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SkyNinja 4 | // 5 | // Created by Chen on 2018/6/6. 6 | // Copyright © 2018 iFiero. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/.DS_Store -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "size": "20x20", 5 | "idiom": "iphone", 6 | "filename": "icon-20@2x.png", 7 | "scale": "2x" 8 | }, 9 | { 10 | "size": "20x20", 11 | "idiom": "iphone", 12 | "filename": "icon-20@3x.png", 13 | "scale": "3x" 14 | }, 15 | { 16 | "size": "29x29", 17 | "idiom": "iphone", 18 | "filename": "icon-29.png", 19 | "scale": "1x" 20 | }, 21 | { 22 | "size": "29x29", 23 | "idiom": "iphone", 24 | "filename": "icon-29@2x.png", 25 | "scale": "2x" 26 | }, 27 | { 28 | "size": "29x29", 29 | "idiom": "iphone", 30 | "filename": "icon-29@3x.png", 31 | "scale": "3x" 32 | }, 33 | { 34 | "size": "40x40", 35 | "idiom": "iphone", 36 | "filename": "icon-40@2x.png", 37 | "scale": "2x" 38 | }, 39 | { 40 | "size": "40x40", 41 | "idiom": "iphone", 42 | "filename": "icon-40@3x.png", 43 | "scale": "3x" 44 | }, 45 | { 46 | "size": "57x57", 47 | "idiom": "iphone", 48 | "filename": "icon-57.png", 49 | "scale": "1x" 50 | }, 51 | { 52 | "size": "57x57", 53 | "idiom": "iphone", 54 | "filename": "icon-57@2x.png", 55 | "scale": "2x" 56 | }, 57 | { 58 | "size": "60x60", 59 | "idiom": "iphone", 60 | "filename": "icon-60@2x.png", 61 | "scale": "2x" 62 | }, 63 | { 64 | "size": "60x60", 65 | "idiom": "iphone", 66 | "filename": "icon-60@3x.png", 67 | "scale": "3x" 68 | }, 69 | { 70 | "size": "20x20", 71 | "idiom": "ipad", 72 | "filename": "icon-20-ipad.png", 73 | "scale": "1x" 74 | }, 75 | { 76 | "size": "20x20", 77 | "idiom": "ipad", 78 | "filename": "icon-20@2x-ipad.png", 79 | "scale": "2x" 80 | }, 81 | { 82 | "size": "29x29", 83 | "idiom": "ipad", 84 | "filename": "icon-29-ipad.png", 85 | "scale": "1x" 86 | }, 87 | { 88 | "size": "29x29", 89 | "idiom": "ipad", 90 | "filename": "icon-29@2x-ipad.png", 91 | "scale": "2x" 92 | }, 93 | { 94 | "size": "40x40", 95 | "idiom": "ipad", 96 | "filename": "icon-40.png", 97 | "scale": "1x" 98 | }, 99 | { 100 | "size": "40x40", 101 | "idiom": "ipad", 102 | "filename": "icon-40@2x.png", 103 | "scale": "2x" 104 | }, 105 | { 106 | "size": "50x50", 107 | "idiom": "ipad", 108 | "filename": "icon-50.png", 109 | "scale": "1x" 110 | }, 111 | { 112 | "size": "50x50", 113 | "idiom": "ipad", 114 | "filename": "icon-50@2x.png", 115 | "scale": "2x" 116 | }, 117 | { 118 | "size": "72x72", 119 | "idiom": "ipad", 120 | "filename": "icon-72.png", 121 | "scale": "1x" 122 | }, 123 | { 124 | "size": "72x72", 125 | "idiom": "ipad", 126 | "filename": "icon-72@2x.png", 127 | "scale": "2x" 128 | }, 129 | { 130 | "size": "76x76", 131 | "idiom": "ipad", 132 | "filename": "icon-76.png", 133 | "scale": "1x" 134 | }, 135 | { 136 | "size": "76x76", 137 | "idiom": "ipad", 138 | "filename": "icon-76@2x.png", 139 | "scale": "2x" 140 | }, 141 | { 142 | "size": "83.5x83.5", 143 | "idiom": "ipad", 144 | "filename": "icon-83.5@2x.png", 145 | "scale": "2x" 146 | }, 147 | { 148 | "size": "1024x1024", 149 | "idiom": "ios-marketing", 150 | "filename": "icon-1024.png", 151 | "scale": "1x" 152 | } 153 | ], 154 | "info": { 155 | "version": 1, 156 | "author": "icon.wuruihong.com" 157 | } 158 | } -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-1024.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-20-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-20-ipad.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-20@2x-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-20@2x-ipad.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-29-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-29-ipad.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-29.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-29@2x-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-29@2x-ipad.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-40.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-50.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-50@2x.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-57.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-57@2x.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-72.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-76.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/Learning.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Learning.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/Learning.imageset/Learning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/Learning.imageset/Learning.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/Star.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Star.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/Star.imageset/Star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/Star.imageset/Star.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/bg.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "bg.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/bg.imageset/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/bg.imageset/bg.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/bomb.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "bomb.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/bomb.imageset/bomb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/bomb.imageset/bomb.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/coin.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "coin.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/coin.imageset/coin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/coin.imageset/coin.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/ground.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ground.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/ground.imageset/ground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/ground.imageset/ground.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/ifiero-logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ifiero-logo.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/ifiero-logo.imageset/ifiero-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/ifiero-logo.imageset/ifiero-logo.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/play.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "play.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/play.imageset/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/play.imageset/play.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/t10001.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "t10001.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/t10001.imageset/t10001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/t10001.imageset/t10001.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/t1Mask.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "t1Mask.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/t1Mask.imageset/t1Mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/t1Mask.imageset/t1Mask.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/tapToPlay.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "tapToPlay.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/tapToPlay.imageset/tapToPlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/tapToPlay.imageset/tapToPlay.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/top.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "top.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/top.imageset/top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/top.imageset/top.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/tree.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "cloud.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/tree.imageset/cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/tree.imageset/cloud.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/youlose.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "youlose.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/youlose.imageset/youlose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/youlose.imageset/youlose.png -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/youwon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "youwon.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SkyNinja/Assets.xcassets/youwon.imageset/youwon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Assets.xcassets/youwon.imageset/youwon.png -------------------------------------------------------------------------------- /SkyNinja/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 33 | 39 | 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 | -------------------------------------------------------------------------------- /SkyNinja/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /SkyNinja/Constant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhysicsCategory.swift 3 | // SkyNinja 4 | // 5 | // Created by Chen on 2018/6/11. 6 | // Copyright © 2018 iFiero. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SpriteKit 11 | import CoreGraphics 12 | 13 | public let SCENE_WIDTH:CGFloat = 2048 14 | public let SCENE_HEIGHT:CGFloat = 1536 15 | public let SCENE_NUMBERS:CGFloat = 2 // 二个场景 HardCode 16 | public let CAMERA_MOVE_XPOS:CGFloat = 18 /// 相机X-Axis移动的尺寸; 17 | 18 | // 物理标识; 19 | struct PhysicsCategory { 20 | 21 | static let None: UInt32 = 0x1 << 1 22 | static let Player: UInt32 = 0x1 << 2 23 | static let Bomb: UInt32 = 0x1 << 3 24 | static let Coin:UInt32 = 0x1 << 4 25 | static let GroudLine: UInt32 = 0x1 << 5 26 | static let SkyLine: UInt32 = 0x1 << 6 27 | static let MiddleLine:UInt32 = 0x1 << 7 // 中间物理碰撞线 28 | 29 | } 30 | -------------------------------------------------------------------------------- /SkyNinja/Elements Class/LineNode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MiddleNodeClass.swift 3 | // SkyNinja 4 | // 5 | // Created by Chen on 2018/6/11. 6 | // Copyright © 2018 iFiero. All rights reserved. 7 | // 8 | 9 | import SpriteKit 10 | 11 | class LineNode :SKNode { 12 | 13 | func initLine(size:CGSize,yPos:CGFloat){ // 传入scene的尺寸 14 | /// let yPos : CGFloat = size.height * 0.65 15 | let startPoint = CGPoint(x: 0, y: yPos) 16 | let endPoint = CGPoint(x: size.width * SCENE_NUMBERS, y: yPos) 17 | self.name = "Line" 18 | physicsBody = SKPhysicsBody(edgeFrom: startPoint, to: endPoint) 19 | physicsBody?.affectedByGravity = false 20 | physicsBody?.categoryBitMask = PhysicsCategory.MiddleLine 21 | physicsBody?.contactTestBitMask = PhysicsCategory.Player 22 | physicsBody?.collisionBitMask = PhysicsCategory.None /// 不会弹开 23 | } 24 | 25 | func initSkyLine(size:CGSize,yPos:CGFloat){ // 传入scene的尺寸 26 | /// let yPos : CGFloat = size.height * 0.65 27 | let startPoint = CGPoint(x: 0, y: yPos) 28 | let endPoint = CGPoint(x: size.width * SCENE_NUMBERS, y: yPos) 29 | self.name = "Line" 30 | physicsBody = SKPhysicsBody(edgeFrom: startPoint, to: endPoint) 31 | physicsBody?.affectedByGravity = false 32 | physicsBody?.categoryBitMask = PhysicsCategory.SkyLine 33 | physicsBody?.contactTestBitMask = PhysicsCategory.Player 34 | physicsBody?.collisionBitMask = PhysicsCategory.None /// 不会弹开 35 | } 36 | 37 | func initGroundLine(size:CGSize,yPos:CGFloat){ // 传入scene的尺寸 38 | /// let yPos : CGFloat = size.height * 0.65 39 | let startPoint = CGPoint(x: 0, y: yPos) 40 | let endPoint = CGPoint(x: size.width * SCENE_NUMBERS, y: yPos) 41 | self.name = "Line" 42 | physicsBody = SKPhysicsBody(edgeFrom: startPoint, to: endPoint) 43 | physicsBody?.affectedByGravity = false 44 | physicsBody?.categoryBitMask = PhysicsCategory.GroudLine 45 | physicsBody?.contactTestBitMask = PhysicsCategory.Player 46 | physicsBody?.collisionBitMask = PhysicsCategory.None /// 不会弹开 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /SkyNinja/Elements Class/PlayerNodeClass.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlayerNode.swift 3 | // SkyNinja 4 | // 5 | // Created by Chen on 2018/6/11. 6 | // Copyright © 2018 iFiero. All rights reserved. 7 | // 8 | 9 | 10 | import SpriteKit 11 | 12 | class PlayerNodeClass:SKSpriteNode { 13 | 14 | private var playerTextureAtlas = SKTextureAtlas() 15 | private var playerTextures = [SKTexture]() 16 | private var playerUpDown = false 17 | 18 | // MARK:-玩家 19 | func initPlayer(){ 20 | /// Ninja.atlas名称 (位于目录Resources->Images) 21 | playerTextureAtlas = SKTextureAtlas(named: "Ninja") 22 | /// 把atlas里的图片append到PlayerTextures数组内 23 | for i in 1...playerTextureAtlas.textureNames.count { 24 | let imageName = "t1000\(i)" 25 | playerTextures.append(SKTexture(imageNamed: imageName)) 26 | } 27 | /// 节点的物理特性 28 | /// 1.调用rectangleOf矩形物理外框 29 | /// self.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.size.width, height: self.size.height)) 30 | ///2.调用物理体本身的图片 因为头重脚轻 上升到顶部时导致站不稳; 31 | // self.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "t10001"), size:CGSize(width: SKTexture(imageNamed: "t10001").size().width * 0.7, height: SKTexture(imageNamed: "t10001").size().height * 0.7)) 32 | ///3.精确定制物理体t1Mask 33 | let playerMask = SKTexture(imageNamed: "t1Mask") 34 | self.physicsBody = SKPhysicsBody(texture: playerMask, size: CGSize(width: playerMask.size().width * 0.7, height: playerMask.size().height * 0.7)) /// 0.7 的意思是缩小0.7倍 35 | self.physicsBody?.restitution = 0 // bounciness物体落下时反弹跳力不会消减 36 | self.physicsBody?.categoryBitMask = PhysicsCategory.Player 37 | /// 和谁发生碰撞并发出通知 didBegin 会监测到碰撞通知 38 | self.physicsBody?.contactTestBitMask = PhysicsCategory.Bomb | PhysicsCategory.Coin 39 | /// 相撞后会相互作用吗?比如站立、弹开 40 | self.physicsBody?.collisionBitMask = PhysicsCategory.SkyLine | PhysicsCategory.GroudLine 41 | self.physicsBody?.isDynamic = true 42 | 43 | } 44 | 45 | func startPlayer(){ 46 | /// 执行textures动画 47 | let playerAnimation = SKAction.animate(with: playerTextures, timePerFrame: 0.1) 48 | /// repeatForever执行 49 | let repeatAction = SKAction.repeatForever(playerAnimation) 50 | self.run(repeatAction, withKey: "jogging") 51 | 52 | 53 | } 54 | // MARK: - 停止动画 55 | func stopPlayer(){ 56 | self.removeAction(forKey: "jogging") 57 | } 58 | 59 | // MARK: - 移动玩家 - 和Camera的步调一致 60 | func movePlayer(){ 61 | self.position.x += CAMERA_MOVE_XPOS 62 | } 63 | 64 | // MARK: - 反转Player 65 | func reversePlayer(){ 66 | // print(self.position.y,(scene?.size.height)! * 0.4) 67 | 68 | if self.position.y > (scene?.size.height)! * 0.6 { 69 | self.yScale = -1 * 0.7 70 | } 71 | 72 | if self.position.y < (scene?.size.height)! * 0.4 { 73 | self.yScale = 1 * 0.7 74 | } 75 | 76 | 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /SkyNinja/Elements Class/SpawnElements.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpawnElements.swift 3 | // SkyNinja 4 | // 5 | // Created by Chen on 2018/6/12. 6 | // Copyright © 2018 iFiero. All rights reserved. 7 | // 8 | 9 | import SpriteKit 10 | import CoreGraphics 11 | 12 | /// 生成class 不用继承任何 SKSpriteNode 13 | class SpawnElements { 14 | 15 | private let minY = CGFloat(360) /// 地板 16 | private let maxY = CGFloat(1100) /// 天空的底线 17 | private var randomYPos :CGFloat = 0.0 18 | 19 | // MARK:- 生成Bomb+随机移动Bomb 20 | func spawnBomb(camera:SKCameraNode,scene:SKScene) -> SKSpriteNode{ 21 | 22 | let item :SKSpriteNode! 23 | item = SKSpriteNode(imageNamed: "bomb") /// Assests.xcassets存放的图片名称,非场景中的Sprite的Node名称; 24 | item.name = "bomb" 25 | item.setScale(0.6) /// 原始图片太大了 26 | /// 新建节点 27 | let trailNode = SKNode() 28 | trailNode.zPosition = 4 /// 此处的位置要 < Bomb的Z-Axis轴 29 | scene.addChild(trailNode) 30 | /// 新建粒子效果 31 | let emitterNode = SKEmitterNode(fileNamed: "Trail")! 32 | emitterNode.targetNode = trailNode 33 | item.addChild(emitterNode) 34 | 35 | /// 物理引擎 SKPhysicsBody(rectangleOf: item.size) 36 | let bombTexture = SKTexture(imageNamed: "bomb") 37 | item.physicsBody = SKPhysicsBody(texture: bombTexture, size: CGSize(width: bombTexture.size().width * 0.7 * 0.8 , height: bombTexture.size().height * 0.7 * 0.8)) /// 大小缩小 0.7 * 0.8(透明区域) 38 | item.physicsBody?.affectedByGravity = false 39 | item.physicsBody?.allowsRotation = false 40 | item.physicsBody?.isDynamic = false /// 碰撞后物理体不进行动态反馈 41 | item.physicsBody?.categoryBitMask = PhysicsCategory.Bomb 42 | item.physicsBody?.contactTestBitMask = PhysicsCategory.Player 43 | item.physicsBody?.collisionBitMask = PhysicsCategory.None 44 | 45 | /// 随机位置 46 | randomYPos = CGFloat.random(minY, max: maxY) /// 扩展的CGFloat函数 47 | item.zPosition = 5 48 | item.position.x = camera.position.x + SCENE_WIDTH * 0.7 /// 位于Camera.position的位置右侧 49 | item.position.y = randomYPos 50 | 51 | /// 从右往左的随机速度 52 | let duration = CGFloat.random(CGFloat(6.0), max: CGFloat(12.0)) 53 | let moveToLeft = SKAction.move(to: CGPoint(x: CGFloat(-item.size.width), y: randomYPos), duration: TimeInterval(duration)) 54 | let remove = SKAction.removeFromParent() 55 | item.run(SKAction.sequence([moveToLeft,remove, 56 | SKAction.run { 57 | trailNode.removeFromParent() /// 记得也把emitter节点移除; 58 | } 59 | ])) /// 移出屏幕了; 60 | 61 | return item 62 | } 63 | 64 | // MARK:- 生成随机 coin 65 | func spawnCoin(camera:SKCameraNode) -> SKSpriteNode { 66 | 67 | let item:SKSpriteNode 68 | item = SKSpriteNode(imageNamed: "coin") 69 | item.name = "coin" 70 | item.setScale(0.7) 71 | /// 物理引擎 72 | item.physicsBody = SKPhysicsBody(circleOfRadius: item.size.width / 3) 73 | item.physicsBody?.affectedByGravity = false 74 | item.physicsBody?.categoryBitMask = PhysicsCategory.Coin 75 | item.physicsBody?.contactTestBitMask = PhysicsCategory.Player 76 | item.physicsBody?.collisionBitMask = PhysicsCategory.None 77 | /// 随机yPos位置 78 | randomYPos = CGFloat.random(minY, max: maxY) /// 扩展的CGFloat函数 79 | /// 随机xPos位置 80 | let minX = SCENE_WIDTH + SCENE_WIDTH * 0.1 81 | let maxX = SCENE_WIDTH + SCENE_WIDTH * 1.2 82 | let randomX = CGFloat.random(minX, max: maxX) 83 | 84 | item.zPosition = 5 85 | item.position.x = camera.position.x + randomX /// 位于Camera.position的位置右侧 86 | item.position.y = randomYPos 87 | 88 | /// item上下浮动 89 | let duration:CGFloat = CGFloat.random(1.5, max: 3.0) 90 | let moveUp = SKAction.moveBy(x: 0, y: 50, duration: TimeInterval(duration)) 91 | moveUp.timingMode = .easeInEaseOut 92 | let moveDown = moveUp.reversed() 93 | let sequence = SKAction.sequence([moveUp,moveDown]) 94 | let repeatWobble = SKAction.repeatForever(sequence) 95 | item.run(repeatWobble, withKey: "Wobble") 96 | 97 | return item 98 | } 99 | 100 | } 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /SkyNinja/GameScene.sks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/GameScene.sks -------------------------------------------------------------------------------- /SkyNinja/GameScene.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GameScene.swift 3 | // SkyNinja 4 | /* 5 | * *** 游戏元素使用条款及注意事项 *** 6 | * 7 | * 游戏中的所有元素全部由iFIERO所原创(除注明引用之外),包括人物、音乐、场景等; 8 | * 创作的初衷就是让更多的游戏爱好者可以在开发游戏中获得自豪感 -- 让手机游戏开发变得简单; 9 | * 秉着开源分享的原则,iFIERO发布的游戏都尽可能的易懂实用,并开放所有源码; 10 | * 任何使用者都可以使用游戏中的代码块,也可以进行拷贝、修改、更新、升级,无须再经过iFIERO的同意; 11 | * 但这并不表示可以任意复制、拆分其中的游戏元素: 12 | * 用于[商业目的]而不注明出处; 13 | * 用于[任何教学]而不注明出处; 14 | * 用于[游戏上架]而不注明出处; 15 | * 另外,iFIERO有商用授权游戏元素,获得iFIERO官方授权后,即无任何限制; 16 | * 请尊重帮助过你的iFIERO的知识产权,非常感谢; 17 | * 18 | * Created by VANGO杨 && ANDREW陈 19 | * Copyright © 2018 iFiero. All rights reserved. 20 | * www.iFIERO.com 21 | * iFIERO -- 让手机游戏开发变得简单 22 | * 23 | * SkyNinja 天猪之城 在此游戏中您将获得如下技能: 24 | * 25 | * 1、LaunchScreen 学习如何设置游戏启动画面 26 | * 2、Scene 学习如何切换游戏的游戏场景 27 | * 3、Scene Edit 学习直接使用可见即所得操作编辑游戏场景 28 | * 4、Random 利用可复用的随机函数生成Enemy 29 | * 5、SpriteNode class 学习建立独立的class精灵并引入场景scene 30 | * 6、Collision 学习有节点与节点之间的碰撞的原理及处理方法 31 | * 7、Animation&Atlas 学习如何导入动画帧及何为Atlas 32 | * 8、Camera 使用Camera实现endless背景滚动 33 | * 9、Grarity 学习如何点击屏幕时反转重力 34 | * 10、StateMachine GameplayKit 运用之场景切换;(**** 中级技能) 35 | * 11、Partilces 学习如何做特效及把特效发生碰撞时移出场景;(**** 中级技能) 36 | * 37 | */ 38 | 39 | import SpriteKit 40 | import GameplayKit 41 | 42 | class GameScene: SKScene ,SKPhysicsContactDelegate{ 43 | 44 | var moveAllowed = false /// 场景是否可以移动了; 45 | 46 | //MARK: - StateMachine 场景中各个舞台State 47 | lazy var stateMachine:GKStateMachine = GKStateMachine(states: [ 48 | WaitingState(scene: self), //self 为 GameScene ,把GameScene专入State 49 | PlayState(scene: self), 50 | GameOverState(scene: self) 51 | ]) 52 | 53 | //MARK: - 场景中的所有SpriteNode 54 | /* 55 | * 1.调用 Elements Class 的节点,须在GameScene的把节点的Custom Class设为PlayerNode 56 | * 2.Module 设为项目名称 SkyNinja 57 | * 3.为何要设置独立的class精灵,可以为GameScene减少代码,并有利于代码的复用; 58 | */ 59 | var playerNode:PlayerNodeClass! 60 | var learnTemp:SKSpriteNode! 61 | var coinTempNode:SKSpriteNode! 62 | var bombTempNode:SKSpriteNode! 63 | var mainCamera:SKCameraNode! 64 | var groundNode:SKSpriteNode! /// 地面 65 | var skyNode:SKSpriteNode! /// 天空 66 | var spawnElements = SpawnElements() /// 生成节点工具 特别注意,这里非 spawnElements = SpawnElements! 67 | 68 | private var dt:TimeInterval = 0 /// 每一frame的时间差 69 | private var lastUpdateTimeInterval:TimeInterval = 0 70 | 71 | override func didMove(to view: SKView) { 72 | super.didMove(to: view) 73 | self.physicsWorld.gravity = CGVector(dx: 0, dy: -9.8) /// 物理世界的重力 74 | self.physicsWorld.contactDelegate = self /// 碰撞代理 75 | 76 | initCamera() /// Camera 77 | initBgMusic() /// 背景音乐 78 | initPlayer() /// 初始化玩家 79 | initCoinBomb() /// 临时的Coin+Bomb 80 | initSkyGroundLine() // 建立物理天空+地面 81 | stateMachine.enter(WaitingState.self) /// 初始化以上的各个精灵SpriteNode后,再进入WaitingState 场景舞台State 82 | } 83 | //MARK: - 加入Camera 84 | func initCamera(){ 85 | mainCamera = childNode(withName: "MainCamera") as! SKCameraNode 86 | } 87 | //MARK: - 移动Camera 88 | func moveCamera(){ 89 | self.mainCamera.position.x += CAMERA_MOVE_XPOS ///向右移动 90 | } 91 | //MARK: - 停止Camera 92 | func stopCamera(){ 93 | self.mainCamera.removeAllActions() 94 | } 95 | // MARK:-初始化玩家 96 | func initPlayer(){ 97 | 98 | playerNode = childNode(withName: "Player") as! PlayerNodeClass 99 | playerNode.physicsBody?.affectedByGravity = true 100 | playerNode.initPlayer() 101 | } 102 | // MARK:-背景音乐 103 | func initBgMusic(){ 104 | 105 | let bgMusic = SKAudioNode(fileNamed: "background.mp3") 106 | bgMusic.autoplayLooped = true 107 | addChild(bgMusic) 108 | } 109 | 110 | func initCoinBomb(){ 111 | coinTempNode = childNode(withName: "CoinTemp") as! SKSpriteNode 112 | bombTempNode = childNode(withName: "BombTemp") as! SKSpriteNode 113 | learnTemp = childNode(withName: "learnTemp") as! SKSpriteNode 114 | } 115 | //MARK: - 物理线 116 | func initSkyGroundLine(){ 117 | 118 | skyNode = childNode(withName: "Sky") as! SKSpriteNode 119 | let sykLine = LineNode() /// 生成新的节点 比如 let newNode = SKNode() 120 | sykLine.initSkyLine(size: size, yPos: skyNode.position.y + 10) 121 | addChild(sykLine) 122 | 123 | 124 | groundNode = childNode(withName: "Ground") as! SKSpriteNode 125 | let groundLine = LineNode() 126 | groundLine.initGroundLine(size: size, yPos: groundNode.position.y + groundNode.size.height - 10) 127 | addChild(groundLine) 128 | 129 | } 130 | 131 | // MARK: - 反转物理世界; 132 | func reverseGravity(){ 133 | physicsWorld.gravity *= -1 134 | } 135 | 136 | // MARK: - 根据 camera.position.x 移动所有页面元素; 137 | ///因为节点anchorPoint为(0,0),且相机的初始位置为 1024,所以要把相机的位置扣除1024 即(camera.position.x - self.size.width / 2) 138 | func moveSprites(camera:SKCameraNode){ 139 | /// 所有的天空精灵 140 | enumerateChildNodes(withName: "Sky") { (node, error) in 141 | if node.position.x + self.size.width < (camera.position.x - self.size.width / 2) { 142 | node.position.x += self.size.width * SCENE_NUMBERS 143 | } 144 | } 145 | /// 所有的地面精灵; 146 | enumerateChildNodes(withName: "Ground") { (node, error) in 147 | if node.position.x + self.size.width < (camera.position.x - self.size.width / 2 ) { 148 | node.position.x += self.size.width * SCENE_NUMBERS 149 | } 150 | /// print("所有的地面精灵",node.position.x,(camera.position.x - self.size.width / 2 )) 151 | } 152 | /// 所有线和Camera同步 153 | enumerateChildNodes(withName: "Line") { (node, error) in 154 | // let node = node as! SKNode 155 | node.position.x += CAMERA_MOVE_XPOS 156 | if node.position.x < -self.size.width { 157 | node.position.x += self.size.width * SCENE_NUMBERS 158 | } 159 | } 160 | /// 所有树 161 | /// 树为何不:(camera.position.x - self.size.width / 2 ),请注意树的 anchorPoint(0.5,0.5) 162 | enumerateChildNodes(withName: "Tree") { (node, error) in 163 | if node.position.x + self.size.width < (camera.position.x ) { 164 | node.position.x += self.size.width * SCENE_NUMBERS 165 | } 166 | } 167 | /// 所有背景 168 | enumerateChildNodes(withName: "Bg") { (node, error) in 169 | 170 | if node.position.x + self.size.width < (camera.position.x - self.size.width / 2 ) { 171 | node.position.x += self.size.width * SCENE_NUMBERS 172 | } 173 | } 174 | } 175 | // MARK: - 生成节点工具 class 176 | @objc func spawnCoins(){ 177 | /// print(spawnElements.spawnCoin(camera: mainCamera)) 178 | if moveAllowed { 179 | self.addChild(spawnElements.spawnCoin(camera: mainCamera)) /// 传入主相机位置 180 | } 181 | 182 | } 183 | 184 | @objc func spawnBombs(){ 185 | if moveAllowed { 186 | addChild(spawnElements.spawnBomb(camera: mainCamera,scene: self)) /// 传入主相机位置 187 | } 188 | } 189 | 190 | @objc func removeCoins(){ 191 | enumerateChildNodes(withName: "coin") { (node, error) in 192 | if node.position.x < self.mainCamera.position.x - self.size.width { 193 | /// print("移除coin") 194 | node.removeFromParent() 195 | } 196 | } 197 | } 198 | // MARK: - 不再生成了; 199 | func stopSpawning(){ 200 | 201 | playerNode.removeAction(forKey: "jogging") /// 移除人物的运动; 202 | 203 | enumerateChildNodes(withName: "coin") { (node, error) in 204 | node.removeAllActions() 205 | } 206 | enumerateChildNodes(withName: "bomb") { (node, error) in 207 | node.removeAllActions() 208 | } 209 | } 210 | 211 | //MARK: - 重新开始游戏; 212 | func restartGame(){ 213 | 214 | let newScene = GameScene(fileNamed: "GameScene")! 215 | newScene.size = CGSize(width: SCENE_WIDTH, height: SCENE_HEIGHT) 216 | newScene.anchorPoint = CGPoint(x: 0, y: 0) 217 | newScene.scaleMode = .aspectFill 218 | let transition = SKTransition.flipHorizontal(withDuration: 0.5) 219 | view?.presentScene(newScene, transition:transition) 220 | } 221 | 222 | // MARK: - 监测屏幕点击事件 223 | override func touchesBegan(_ touches: Set, with event: UIEvent?) { 224 | guard let touch = touches.first else { 225 | return 226 | } 227 | let touchLocation = touch.location(in: self) ///获得点击的位置 228 | /// 判断目前的GameScene场景舞台是哪个state 229 | switch stateMachine.currentState { 230 | case is WaitingState: 231 | /// 获得按钮的点击位置 232 | guard let body = physicsWorld.body(at: touchLocation) else { 233 | return 234 | } 235 | /// 判断是否是点击了PlayButton 236 | guard let playButton = body.node?.childNode(withName: "PlayButton") as? SKSpriteNode else { 237 | return 238 | } 239 | /// 如果点击位置是在PlayButton 240 | if (playButton.contains(touchLocation)){ 241 | playButton.isHidden = true 242 | stateMachine.enter(PlayState.self) /// 进入开始游戏; 243 | } 244 | 245 | guard let learnTempNode = body.node?.childNode(withName: "learnTemp") as? SKSpriteNode else { 246 | return 247 | } 248 | if (learnTempNode.contains(touchLocation)){ 249 | UIApplication.shared.open(URL(string: "http://www.iFIERO.com")!, options: [:], completionHandler: { (error) in 250 | print("jump to http://www.iFiero.com") 251 | }) 252 | } 253 | 254 | case is PlayState: 255 | reverseGravity() /// 反转物理世界; 256 | 257 | case is GameOverState: 258 | 259 | guard let body = physicsWorld.body(at: touchLocation) else { 260 | return 261 | } 262 | // TapToPlay按钮; 263 | if let tapToPlay = body.node?.childNode(withName: "tapToPlay"){ 264 | 265 | if tapToPlay.contains(touchLocation){ 266 | print("重新开始游戏!") 267 | restartGame() 268 | } 269 | } 270 | default: 271 | break; 272 | } 273 | } 274 | // MARK: - 监测碰撞 275 | func didBegin(_ contact: SKPhysicsContact) { 276 | 277 | let bodyA:SKPhysicsBody 278 | let bodyB:SKPhysicsBody 279 | if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask { 280 | bodyA = contact.bodyA 281 | bodyB = contact.bodyB 282 | }else{ 283 | bodyA = contact.bodyB 284 | bodyB = contact.bodyA 285 | } 286 | ///检测碰到中间线 287 | /* 288 | if bodyA.categoryBitMask == PhysicsCategory.Player && bodyB.categoryBitMask == PhysicsCategory.MiddleLine { 289 | /// print("碰到屏幕线人物反转") 290 | playerNode.reversePlayer() 291 | } 292 | */ 293 | 294 | ///检测碰到coin 295 | if bodyA.categoryBitMask == PhysicsCategory.Player && bodyB.categoryBitMask == PhysicsCategory.Coin { 296 | /// print("碰到屏幕线人物反转") 297 | let coinAction = SKAction.playSoundFileNamed("coin.wav", waitForCompletion: false) 298 | run(coinAction) 299 | bodyB.categoryBitMask = PhysicsCategory.None /// 去除双响; 300 | 301 | /// 加入收集coin特效 position位置; 302 | let coinEmitter = SKEmitterNode(fileNamed: "CollectNormal") 303 | coinEmitter?.position = (bodyB.node?.position)! 304 | coinEmitter?.zPosition = 3 305 | self.addChild(coinEmitter!) 306 | //移除coin 307 | bodyB.node?.run(SKAction.sequence([ 308 | SKAction.scale(to: 0.01, duration: 0.05), 309 | SKAction.removeFromParent()])) 310 | //移除coin特效节点 311 | coinEmitter?.run(SKAction.sequence([ 312 | SKAction.wait(forDuration: TimeInterval(0.7)), 313 | SKAction.removeFromParent(), 314 | 315 | ])) 316 | } 317 | 318 | /// 检测碰到Bomb 319 | if bodyA.categoryBitMask == PhysicsCategory.Player && bodyB.categoryBitMask == PhysicsCategory.Bomb { 320 | /// 播放音乐 321 | /// let bombAction = SKAction.playSoundFileNamed("ninjaHit.wav", waitForCompletion: false) 322 | /// 无法播放音乐 在Build Phases -> Copy Bundle Resources 导入即可; 323 | let bombAction = SKAction.playSoundFileNamed("death.caf", waitForCompletion: false) 324 | run(bombAction) 325 | bodyA.categoryBitMask = PhysicsCategory.None /// 防止一直和Bomb碰撞 产生多重声音; 326 | /// 加入death 特效; 327 | let deathNode = SKNode() 328 | deathNode.position = (bodyA.node?.position)! 329 | self.addChild(deathNode) 330 | 331 | let deathEmitter = SKEmitterNode(fileNamed: "death") 332 | deathEmitter?.targetNode = deathNode 333 | deathNode.addChild(deathEmitter!) 334 | bodyA.node?.removeFromParent() 335 | deathEmitter?.run(SKAction.sequence([ 336 | SKAction.wait(forDuration: TimeInterval(2.0)), 337 | SKAction.removeFromParent(), 338 | ])) 339 | bodyB.node?.removeFromParent() /// 移除Bomb 340 | 341 | stateMachine.enter(GameOverState.self) 342 | } 343 | } 344 | // MARK: - 时时更新update 345 | override func update(_ currentTime: TimeInterval) { 346 | 347 | /// 获取时间差 348 | if lastUpdateTimeInterval == 0 { 349 | lastUpdateTimeInterval = currentTime 350 | } 351 | dt = currentTime - lastUpdateTimeInterval 352 | lastUpdateTimeInterval = currentTime 353 | 354 | stateMachine.update(deltaTime: dt) /// 把update传进各个State里; 355 | } 356 | } 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | -------------------------------------------------------------------------------- /SkyNinja/GameStates/GameOverState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GameOverState.swift 3 | // SkyNinja 4 | // 5 | // Copyright © 2018 iFiero. All rights reserved. 6 | // 7 | 8 | import SpriteKit 9 | import GameplayKit 10 | 11 | class GameOverState:GKState { 12 | 13 | unowned let scene:GameScene 14 | init(scene:SKScene){ 15 | self.scene = scene as! GameScene 16 | super.init() 17 | } 18 | 19 | override func didEnter(from previousState: GKState?) { 20 | scene.moveAllowed = false 21 | scene.stopSpawning() 22 | print("GameOver State 游戏结束") 23 | initGameOver() 24 | 25 | } 26 | 27 | func initGameOver(){ 28 | ///CAMERA有移动了; 29 | let xPos = scene.mainCamera.position.x 30 | let yPos = scene.mainCamera.position.y 31 | 32 | let loseNode = SKSpriteNode(imageNamed: "youlose") 33 | loseNode.setScale(0.7) 34 | loseNode.zPosition = 6 35 | loseNode.position = CGPoint(x: xPos, y: yPos + 60) 36 | scene.addChild(loseNode) 37 | 38 | let tapNode = SKSpriteNode(imageNamed: "tapToPlay") 39 | tapNode.name = "tapToPlay" 40 | tapNode.position = CGPoint(x: xPos, y: yPos - 60) 41 | tapNode.setScale(0.7) 42 | tapNode.zPosition = 6 43 | scene.addChild(tapNode) 44 | 45 | } 46 | 47 | override func isValidNextState(_ stateClass: AnyClass) -> Bool { 48 | return stateClass is PlayState.Type 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /SkyNinja/GameStates/PlayState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlayState.swift 3 | // SkyNinja 4 | // Copyright © 2018 iFiero. All rights reserved. 5 | // 6 | 7 | import SpriteKit 8 | import GameplayKit 9 | 10 | class PlayState:GKState { 11 | unowned let scene:GameScene 12 | 13 | init(scene:SKScene){ 14 | self.scene = scene as! GameScene 15 | super.init() 16 | print("Play State") 17 | } 18 | 19 | /// 前一State是WaitState 20 | override func didEnter(from previousState: GKState?) { 21 | scene.moveAllowed = true /// 场景可以移动了 22 | scene.bombTempNode.isHidden = true 23 | scene.coinTempNode.isHidden = true 24 | scene.learnTemp.isHidden = true 25 | scene.playerNode.startPlayer() 26 | 27 | initTopBottomLineNode() /// 加入上线二条碰撞线 28 | // 用Timer每隔1s调用 spawnBomb 注意此处是在State里,则target要为scene.self而不是self 29 | Timer.scheduledTimer(timeInterval: TimeInterval(3.0), target: scene.self, selector: #selector(scene.spawnBombs), userInfo: nil, repeats: true) 30 | 31 | Timer.scheduledTimer(timeInterval: TimeInterval(2.0), target: scene.self, selector: #selector(scene.spawnCoins), userInfo: nil, repeats: true) 32 | 33 | 34 | } 35 | // MARK:- 加入上线二条碰撞线 36 | func initTopBottomLineNode(){ 37 | 38 | // let topLine = LineNode() 39 | // topLine.initLine(size: scene.size, yPos: scene.size.height * 0.7) 40 | // scene.addChild(topLine) 41 | // 42 | // let bottomLine = LineNode() 43 | // bottomLine.initLine(size: scene.size, yPos: scene.size.height * 0.3) 44 | // scene.addChild(bottomLine) 45 | 46 | } 47 | /// 下一State是GameOver 48 | override func isValidNextState(_ stateClass: AnyClass) -> Bool { 49 | return stateClass is GameOverState.Type 50 | } 51 | /// update 52 | 53 | override func update(deltaTime seconds: TimeInterval) { 54 | 55 | if scene.moveAllowed { 56 | scene.moveCamera() /// 移动camera 57 | scene.playerNode.movePlayer() /// 移动玩家 58 | scene.moveSprites(camera: scene.mainCamera!) /// 移动天空及地面; 59 | scene.playerNode.reversePlayer() 60 | }else { 61 | scene.stopCamera() /// 停止camera 62 | } 63 | 64 | scene.removeCoins() 65 | 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /SkyNinja/GameStates/WaitingState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WaitingState.swift 3 | // SkyNinja 4 | // Copyright © 2018 iFiero. All rights reserved. 5 | // 6 | 7 | import SpriteKit 8 | import GameplayKit 9 | 10 | class WaitingState:GKState { 11 | 12 | unowned let scene:GameScene 13 | var playButton:SKSpriteNode! 14 | 15 | init(scene:SKScene){ 16 | self.scene = scene as! GameScene 17 | super.init() 18 | print("Waiting State") 19 | } 20 | //MARK: - 初始化PlayButton,但获得点击事件touchesBegan是在GameScene中进行判断; 21 | func initPlayButton() { 22 | /// 取得playButton节点 23 | playButton = scene.childNode(withName: "PlayButton") as! SKSpriteNode 24 | /// 播放按钮的动画; 25 | let zoomOut = SKAction.scale(by: 0.8, duration: 0.75) 26 | zoomOut.timingMode = .easeInEaseOut 27 | let zoomIn = zoomOut.reversed() 28 | zoomIn.timingMode = .easeInEaseOut 29 | /// 播放顺序 30 | let sequence = SKAction.sequence([zoomOut,zoomIn]) 31 | let repeatAction = SKAction.repeatForever(sequence) 32 | /// 执行Action 33 | playButton.run(repeatAction) 34 | 35 | } 36 | 37 | override func didEnter(from previousState: GKState?) { 38 | scene.moveAllowed = false /// 初始进入WaitState人物+场景是不可以运动的; 39 | scene.coinTempNode.isHidden = false 40 | scene.bombTempNode.isHidden = false 41 | scene.learnTemp.isHidden = false 42 | initPlayButton() 43 | } 44 | // 正确无误 进入一下个State PlayState 45 | override func isValidNextState(_ stateClass: AnyClass) -> Bool { 46 | return stateClass is PlayState.Type 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /SkyNinja/GameViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GameViewController.swift 3 | // SkyNinja 4 | /* 5 | * *** 游戏元素使用条款及注意事项 *** 6 | * 7 | * 游戏中的所有元素全部由iFIERO所原创(除注明引用之外),包括人物、音乐、场景等; 8 | * 创作的初衷就是让更多的游戏爱好者可以在开发游戏中获得自豪感 -- 让手机游戏开发变得简单; 9 | * 秉着开源分享的原则,iFIERO发布的游戏都尽可能的易懂实用,并开放所有源码; 10 | * 任何使用者都可以使用游戏中的代码块,也可以进行拷贝、修改、更新、升级,无须再经过iFIERO的同意; 11 | * 但这并不表示可以任意复制、拆分其中的游戏元素: 12 | * 用于[商业目的]而不注明出处; 13 | * 用于[任何教学]而不注明出处; 14 | * 用于[游戏上架]而不注明出处; 15 | * 另外,iFIERO有商用授权游戏元素,获得iFIERO官方授权后,即无任何限制; 16 | * 请尊重帮助过你的iFIERO的知识产权,非常感谢; 17 | * 18 | * Created by VANGO杨 && ANDREW陈 19 | * Copyright © 2018 iFiero. All rights reserved. 20 | * www.iFIERO.com 21 | * iFIERO -- 让手机游戏开发变得简单 22 | * 23 | * SkyNinja 天猪之城 在此游戏中您将获得如下技能: 24 | * 25 | * 1、LaunchScreen 学习如何设置游戏启动画面 26 | * 2、Scene 学习如何切换游戏的游戏场景 27 | * 3、Scene Edit 学习直接使用可见即所得操作编辑游戏场景 28 | * 4、Random 利用可复用的随机函数生成Enemy 29 | * 5、SpriteNode class 学习建立独立的class精灵并引入场景scene 30 | * 6、Collision 学习有节点与节点之间的碰撞的原理及处理方法 31 | * 7、Animation&Atlas 学习如何导入动画帧及何为Atlas 32 | * 8、Camera 使用Camera实现endless背景滚动 33 | * 9、Grarity 学习如何点击屏幕时反转重力 34 | * 10、StateMachine GameplayKit 运用之场景切换;(**** 中级技能) 35 | * 11、Partilces 学习如何做特效及把特效发生碰撞时移出场景;(**** 中级技能) 36 | * 37 | */ 38 | 39 | import UIKit 40 | import SpriteKit 41 | import GameplayKit 42 | 43 | 44 | class GameViewController: UIViewController { 45 | 46 | override func viewDidLoad() { 47 | super.viewDidLoad() 48 | 49 | if let view = self.view as! SKView? { 50 | 51 | if let scene = GameScene(fileNamed: "GameScene") { 52 | scene.scaleMode = .aspectFill 53 | scene.size = CGSize(width: SCENE_WIDTH, height: SCENE_HEIGHT) 54 | scene.anchorPoint = CGPoint(x: 0, y: 0) /// 设置anchorPoint为(0,0) 55 | view.presentScene(scene) 56 | } 57 | /* ***** 千万嫑嫑嫑小看了这一句的强大功效(监测物理体外框) ***** */ 58 | // view.showsPhysics = true 59 | view.ignoresSiblingOrder = true /// 是否忽略图层自动排列 true 60 | view.showsFPS = true 61 | view.showsNodeCount = true 62 | } 63 | } 64 | 65 | override var shouldAutorotate: Bool { 66 | return true 67 | } 68 | 69 | override var supportedInterfaceOrientations: UIInterfaceOrientationMask { 70 | if UIDevice.current.userInterfaceIdiom == .phone { 71 | return .allButUpsideDown 72 | } else { 73 | return .all 74 | } 75 | } 76 | 77 | override func didReceiveMemoryWarning() { 78 | super.didReceiveMemoryWarning() 79 | // Release any cached data, images, etc that aren't in use. 80 | } 81 | 82 | override var prefersStatusBarHidden: Bool { 83 | return true 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /SkyNinja/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | 天猪之城 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0.1 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIRequiresFullScreen 34 | 35 | UIStatusBarHidden 36 | 37 | UISupportedInterfaceOrientations 38 | 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /SkyNinja/Resources/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/.DS_Store -------------------------------------------------------------------------------- /SkyNinja/Resources/Images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/Images/.DS_Store -------------------------------------------------------------------------------- /SkyNinja/Resources/Images/Ninja.atlas/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/Images/Ninja.atlas/.DS_Store -------------------------------------------------------------------------------- /SkyNinja/Resources/Images/Ninja.atlas/t10001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/Images/Ninja.atlas/t10001.png -------------------------------------------------------------------------------- /SkyNinja/Resources/Images/Ninja.atlas/t10002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/Images/Ninja.atlas/t10002.png -------------------------------------------------------------------------------- /SkyNinja/Resources/Images/Ninja.atlas/t10003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/Images/Ninja.atlas/t10003.png -------------------------------------------------------------------------------- /SkyNinja/Resources/Images/Ninja.atlas/t10004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/Images/Ninja.atlas/t10004.png -------------------------------------------------------------------------------- /SkyNinja/Resources/Images/Ninja.atlas/t10005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/Images/Ninja.atlas/t10005.png -------------------------------------------------------------------------------- /SkyNinja/Resources/Images/Ninja.atlas/t10006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/Images/Ninja.atlas/t10006.png -------------------------------------------------------------------------------- /SkyNinja/Resources/Images/Ninja.atlas/t10007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/Images/Ninja.atlas/t10007.png -------------------------------------------------------------------------------- /SkyNinja/Resources/Images/Ninja.atlas/t10008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/Images/Ninja.atlas/t10008.png -------------------------------------------------------------------------------- /SkyNinja/Resources/Particles/CollectNormal.sks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/Particles/CollectNormal.sks -------------------------------------------------------------------------------- /SkyNinja/Resources/Particles/Trail.sks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/Particles/Trail.sks -------------------------------------------------------------------------------- /SkyNinja/Resources/Particles/death.sks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/Particles/death.sks -------------------------------------------------------------------------------- /SkyNinja/Resources/SoundsFiles/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/SoundsFiles/.DS_Store -------------------------------------------------------------------------------- /SkyNinja/Resources/SoundsFiles/Bomb.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/SoundsFiles/Bomb.wav -------------------------------------------------------------------------------- /SkyNinja/Resources/SoundsFiles/background.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/SoundsFiles/background.mp3 -------------------------------------------------------------------------------- /SkyNinja/Resources/SoundsFiles/coin.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/SoundsFiles/coin.wav -------------------------------------------------------------------------------- /SkyNinja/Resources/SoundsFiles/ninjaHit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/Resources/SoundsFiles/ninjaHit.wav -------------------------------------------------------------------------------- /SkyNinja/Resources/Utils/CGFloat+Extension.swift: -------------------------------------------------------------------------------- 1 | /* 注:此文件中的代码为iFIERO所引用,即原创版权非iFIERO所持有 */ 2 | 3 | import CoreGraphics 4 | import SpriteKit 5 | /** π as a CGFloat 180度 */ 6 | let π = CGFloat(Double.pi) 7 | 8 | public extension CGFloat { 9 | /** 10 | * 角度转化为弧度. 11 | */ 12 | public func degreesToRadians() -> CGFloat { 13 | return π * self / 180.0 14 | } 15 | 16 | /** 17 | * 弧度转化为角度. 18 | */ 19 | public func radiansToDegrees() -> CGFloat { 20 | return self * 180.0 / π 21 | } 22 | 23 | /** 24 | * 0.0 and 1.0 随机数. 25 | */ 26 | public static func random() -> CGFloat { 27 | return CGFloat(Float(arc4random()) / 0xFFFFFFFF) 28 | } 29 | 30 | /** 31 | * min...max 的随机数. 32 | */ 33 | public static func random(_ min: CGFloat, max: CGFloat) -> CGFloat { 34 | assert(min < max) 35 | return CGFloat.random() * (max - min) + min 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /SkyNinja/Resources/Utils/CGVector+Extensions.swift: -------------------------------------------------------------------------------- 1 | /* 注:此文件中的代码为iFIERO所引用,即原创版权非iFIERO所持有 */ 2 | 3 | import CoreGraphics 4 | import SpriteKit 5 | 6 | public extension CGVector { 7 | /** 8 | * Creates a new CGVector given a CGPoint. 9 | */ 10 | public init(point: CGPoint) { 11 | self.init(dx: point.x, dy: point.y) 12 | } 13 | 14 | /** 15 | * Given an angle in radians, creates a vector of length 1.0 and returns the 16 | * result as a new CGVector. An angle of 0 is assumed to point to the right. 17 | */ 18 | public init(angle: CGFloat) { 19 | self.init(dx: cos(angle), dy: sin(angle)) 20 | } 21 | 22 | /** 23 | * Adds (dx, dy) to the vector. 24 | */ 25 | public mutating func offset(_ dx: CGFloat, dy: CGFloat) -> CGVector { 26 | self.dx += dx 27 | self.dy += dy 28 | return self 29 | } 30 | 31 | /** 32 | * Returns the length (magnitude) of the vector described by the CGVector. 33 | */ 34 | public func length() -> CGFloat { 35 | return sqrt(dx*dx + dy*dy) 36 | } 37 | 38 | /** 39 | * Returns the squared length of the vector described by the CGVector. 40 | */ 41 | public func lengthSquared() -> CGFloat { 42 | return dx*dx + dy*dy 43 | } 44 | 45 | /** 46 | * Normalizes the vector described by the CGVector to length 1.0 and returns 47 | * the result as a new CGVector. 48 | public */ 49 | func normalized() -> CGVector { 50 | let len = length() 51 | return len>0 ? self / len : CGVector.zero 52 | } 53 | 54 | /** 55 | * Normalizes the vector described by the CGVector to length 1.0. 56 | */ 57 | public mutating func normalize() -> CGVector { 58 | self = normalized() 59 | return self 60 | } 61 | 62 | /** 63 | * Calculates the distance between two CGVectors. Pythagoras! 64 | */ 65 | public func distanceTo(_ vector: CGVector) -> CGFloat { 66 | return (self - vector).length() 67 | } 68 | 69 | /** 70 | * Returns the angle in radians of the vector described by the CGVector. 71 | * The range of the angle is -π to π; an angle of 0 points to the right. 72 | */ 73 | public var angle: CGFloat { 74 | return atan2(dy, dx) 75 | } 76 | } 77 | 78 | /** 79 | * Adds two CGVector values and returns the result as a new CGVector. 80 | */ 81 | public func + (left: CGVector, right: CGVector) -> CGVector { 82 | return CGVector(dx: left.dx + right.dx, dy: left.dy + right.dy) 83 | } 84 | 85 | /** 86 | * Increments a CGVector with the value of another. 87 | */ 88 | public func += (left: inout CGVector, right: CGVector) { 89 | left = left + right 90 | } 91 | 92 | /** 93 | * Subtracts two CGVector values and returns the result as a new CGVector. 94 | */ 95 | public func - (left: CGVector, right: CGVector) -> CGVector { 96 | return CGVector(dx: left.dx - right.dx, dy: left.dy - right.dy) 97 | } 98 | 99 | /** 100 | * Decrements a CGVector with the value of another. 101 | */ 102 | public func -= (left: inout CGVector, right: CGVector) { 103 | left = left - right 104 | } 105 | 106 | /** 107 | * Multiplies two CGVector values and returns the result as a new CGVector. 108 | */ 109 | public func * (left: CGVector, right: CGVector) -> CGVector { 110 | return CGVector(dx: left.dx * right.dx, dy: left.dy * right.dy) 111 | } 112 | 113 | /** 114 | * Multiplies a CGVector with another. 115 | */ 116 | public func *= (left: inout CGVector, right: CGVector) { 117 | left = left * right 118 | } 119 | 120 | /** 121 | * Multiplies the x and y fields of a CGVector with the same scalar value and 122 | * returns the result as a new CGVector. 123 | */ 124 | public func * (vector: CGVector, scalar: CGFloat) -> CGVector { 125 | return CGVector(dx: vector.dx * scalar, dy: vector.dy * scalar) 126 | } 127 | 128 | /** 129 | * Multiplies the x and y fields of a CGVector with the same scalar value. 130 | */ 131 | public func *= (vector: inout CGVector, scalar: CGFloat) { 132 | vector = vector * scalar 133 | } 134 | 135 | /** 136 | * Divides two CGVector values and returns the result as a new CGVector. 137 | */ 138 | public func / (left: CGVector, right: CGVector) -> CGVector { 139 | return CGVector(dx: left.dx / right.dx, dy: left.dy / right.dy) 140 | } 141 | 142 | /** 143 | * Divides a CGVector by another. 144 | */ 145 | public func /= (left: inout CGVector, right: CGVector) { 146 | left = left / right 147 | } 148 | 149 | /** 150 | * Divides the dx and dy fields of a CGVector by the same scalar value and 151 | * returns the result as a new CGVector. 152 | */ 153 | public func / (vector: CGVector, scalar: CGFloat) -> CGVector { 154 | return CGVector(dx: vector.dx / scalar, dy: vector.dy / scalar) 155 | } 156 | 157 | /** 158 | * Divides the dx and dy fields of a CGVector by the same scalar value. 159 | */ 160 | public func /= (vector: inout CGVector, scalar: CGFloat) { 161 | vector = vector / scalar 162 | } 163 | 164 | /** 165 | * Performs a linear interpolation between two CGVector values. 166 | */ 167 | public func lerp(_ start: CGVector, end: CGVector, t: CGFloat) -> CGVector { 168 | return start + (end - start) * t 169 | } 170 | -------------------------------------------------------------------------------- /SkyNinja/death.caf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiapia/SkyNinjaGameSpriteKitTutorial/729df1f7209fcefd6317c3da976edb4d8c6bc55d/SkyNinja/death.caf --------------------------------------------------------------------------------