美文网首页布袋的世界之Apple苹果家园
iFIERO -- SkyNinja天猪之城 SpriteKit

iFIERO -- SkyNinja天猪之城 SpriteKit

作者: 布袋的世界 | 来源:发表于2018-06-12 12:22 被阅读7次
    SkyNinja.gif Snip20180612_2.png Snip20180612_9.png Snip20180612_3.png

    //
    // GameScene.swift
    // SkyNinja
    /*

    • *** 游戏元素使用条款及注意事项 ***
    • 游戏中的所有元素全部由iFIERO所原创(除注明引用之外),包括人物、音乐、场景等;
    • 创作的初衷就是让更多的游戏爱好者可以在开发游戏中获得自豪感 -- 让手机游戏开发变得简单;
    • 秉着开源分享的原则,iFIERO发布的游戏都尽可能的易懂实用,并开放所有源码;
    • 任何使用者都可以使用游戏中的代码块,也可以进行拷贝、修改、更新、升级,无须再经过iFIERO的同意;
    • 但这并不表示可以任意复制、拆分其中的游戏元素:
    • 用于[商业目的]而不注明出处;
    • 用于[任何教学]而不注明出处;
    • 用于[游戏上架]而不注明出处;
    • 另外,iFIERO有商用授权游戏元素,获得iFIERO官方授权后,即无任何限制;
    • 请尊重帮助过你的iFIERO的知识产权,非常感谢;
    • Created by VANGO杨 && ANDREW陈
    • Copyright © 2018 iFiero. All rights reserved.
    • www.iFIERO.com
    • iFIERO -- 让手机游戏开发变得简单
    • SkyNinja 天猪之城 在此游戏中您将获得如下技能:
    • 1、LaunchScreen 学习如何设置游戏启动画面
    • 2、Scene 学习如何切换游戏的游戏场景
    • 3、Scene Edit 学习直接使用可见即所得操作编辑游戏场景
    • 4、Random 利用可复用的随机函数生成Enemy
    • 5、SpriteNode class 学习建立独立的class精灵并引入场景scene
    • 6、Collision 学习有节点与节点之间的碰撞的原理及处理方法
    • 7、Animation&Atlas 学习如何导入动画帧及何为Atlas
    • 8、Camera 使用Camera实现endless背景滚动
    • 9、Grarity 学习如何点击屏幕时反转重力
    • 10、StateMachine GameplayKit 运用之场景切换;(**** 中级技能)
    • 11、Partilces 学习如何做特效及把特效发生碰撞时移出场景;(**** 中级技能)

    */

    import SpriteKit
    import GameplayKit
    
    class GameScene: SKScene ,SKPhysicsContactDelegate{
        
        var moveAllowed = false /// 场景是否可以移动了;
        
        //MARK: - StateMachine 场景中各个舞台State
        lazy var stateMachine:GKStateMachine = GKStateMachine(states: [
            WaitingState(scene: self), //self 为 GameScene ,把GameScene专入State
            PlayState(scene: self),
            GameOverState(scene: self)
            ])
        
        //MARK: - 场景中的所有SpriteNode
        /*
         * 1.调用 Elements Class 的节点,须在GameScene的把节点的Custom Class设为PlayerNode
         * 2.Module 设为项目名称 SkyNinja
         * 3.为何要设置独立的class精灵,可以为GameScene减少代码,并有利于代码的复用;
         */
        var playerNode:PlayerNodeClass!
        var coinTempNode:SKSpriteNode!
        var bombTempNode:SKSpriteNode!
        var mainCamera:SKCameraNode!
        var groundNode:SKSpriteNode!    /// 地面
        var skyNode:SKSpriteNode!       /// 天空
        var spawnElements = SpawnElements() /// 生成节点工具 特别注意,这里非 spawnElements = SpawnElements!
        
        private var dt:TimeInterval = 0  /// 每一frame的时间差
        private var lastUpdateTimeInterval:TimeInterval = 0
        
        override func didMove(to view: SKView) {
            super.didMove(to: view)
            self.physicsWorld.gravity = CGVector(dx: 0, dy: -9.8)  /// 物理世界的重力
            self.physicsWorld.contactDelegate = self               /// 碰撞代理
            
            initCamera()   /// Camera
            initBgMusic()  /// 背景音乐
            initPlayer()   /// 初始化玩家
            initCoinBomb() /// 临时的Coin+Bomb
            initSkyGroundLine() // 建立物理天空+地面
            stateMachine.enter(WaitingState.self) /// 初始化以上的各个精灵SpriteNode后,再进入WaitingState 场景舞台State
        }
        //MARK: - 加入Camera
        func initCamera(){
            mainCamera = childNode(withName: "MainCamera") as! SKCameraNode
        }
        //MARK: - 移动Camera
        func moveCamera(){
            self.mainCamera.position.x += CAMERA_MOVE_XPOS ///向右移动
        }
        //MARK: - 停止Camera
        func stopCamera(){
            self.mainCamera.removeAllActions()
        }
        // MARK:-初始化玩家
        func initPlayer(){
            
            playerNode = childNode(withName: "Player") as! PlayerNodeClass
            playerNode.physicsBody?.affectedByGravity = true
            playerNode.initPlayer()
        }
        // MARK:-背景音乐
        func initBgMusic(){
            
            let bgMusic = SKAudioNode(fileNamed: "background.mp3")
            bgMusic.autoplayLooped = true
            addChild(bgMusic)
        }
        
        func initCoinBomb(){
            coinTempNode = childNode(withName: "CoinTemp") as! SKSpriteNode
            bombTempNode = childNode(withName: "BombTemp") as! SKSpriteNode
        }
        //MARK: - 物理线
        func initSkyGroundLine(){
            
            skyNode = childNode(withName: "Sky") as! SKSpriteNode
            let sykLine = LineNode()  /// 生成新的节点 比如 let newNode = SKNode()
            sykLine.initSkyLine(size: size, yPos: skyNode.position.y + 10)
            addChild(sykLine)
            
            
            groundNode = childNode(withName: "Ground") as! SKSpriteNode
            let groundLine = LineNode()
            groundLine.initGroundLine(size: size, yPos: groundNode.position.y + groundNode.size.height - 10)
            addChild(groundLine)
            
        }
        
        // MARK: - 反转物理世界;
        func reverseGravity(){
            physicsWorld.gravity *= -1
        }
        
        // MARK: - 根据 camera.position.x 移动所有页面元素;
        ///因为节点anchorPoint为(0,0),且相机的初始位置为 1024,所以要把相机的位置扣除1024 即(camera.position.x - self.size.width / 2)
        func moveSprites(camera:SKCameraNode){
            /// 所有的天空精灵
            enumerateChildNodes(withName: "Sky") { (node, error) in
                if  node.position.x + self.size.width < (camera.position.x - self.size.width / 2) {
                    node.position.x += self.size.width * SCENE_NUMBERS
                }
            }
            /// 所有的地面精灵;
            enumerateChildNodes(withName: "Ground") { (node, error) in
                if  node.position.x + self.size.width < (camera.position.x - self.size.width / 2 ) {
                    node.position.x += self.size.width * SCENE_NUMBERS
                }
                /// print("所有的地面精灵",node.position.x,(camera.position.x - self.size.width / 2 ))
            }
            /// 所有线和Camera同步
            enumerateChildNodes(withName: "Line") { (node, error) in
                // let node = node as! SKNode
                node.position.x += CAMERA_MOVE_XPOS
                if  node.position.x < -self.size.width {
                    node.position.x += self.size.width * SCENE_NUMBERS
                }
            }
            /// 所有树
            /// 树为何不:(camera.position.x - self.size.width / 2 ),请注意树的 anchorPoint(0.5,0.5)
            enumerateChildNodes(withName: "Tree") { (node, error) in
                if  node.position.x + self.size.width < (camera.position.x ) {
                    node.position.x += self.size.width * SCENE_NUMBERS
                }
            }
            /// 所有背景
            enumerateChildNodes(withName: "Bg") { (node, error) in
                
                if  node.position.x + self.size.width < (camera.position.x - self.size.width / 2 ) {
                    node.position.x += self.size.width * SCENE_NUMBERS
                }
            }
        }
        // MARK: - 生成节点工具 class
        @objc func spawnCoins(){
            /// print(spawnElements.spawnCoin(camera: mainCamera))
            if moveAllowed {
                self.addChild(spawnElements.spawnCoin(camera: mainCamera)) /// 传入主相机位置
            }
            
        }
        
        @objc func spawnBombs(){
            if moveAllowed {
                addChild(spawnElements.spawnBomb(camera: mainCamera,scene: self)) /// 传入主相机位置
            }
        }
        
        @objc func removeCoins(){
            enumerateChildNodes(withName: "coin") { (node, error) in
                if node.position.x < self.mainCamera.position.x - self.size.width {
                    /// print("移除coin")
                    node.removeFromParent()
                }
            }
        }
        // MARK: - 不再生成了;
        func stopSpawning(){
            
            playerNode.removeAction(forKey: "jogging")  /// 移除人物的运动;
            
            enumerateChildNodes(withName: "coin") { (node, error) in
                node.removeAllActions()
            }
            enumerateChildNodes(withName: "bomb") { (node, error) in
                node.removeAllActions()
            }
        }
        
        //MARK: - 重新开始游戏;
        func restartGame(){
            
            let newScene = GameScene(fileNamed: "GameScene")!
            newScene.size = CGSize(width: SCENE_WIDTH, height: SCENE_HEIGHT)
            newScene.anchorPoint = CGPoint(x: 0, y: 0)
            newScene.scaleMode   = .aspectFill
            let transition = SKTransition.flipHorizontal(withDuration: 0.5)
            view?.presentScene(newScene, transition:transition)
        }
        
        // MARK: - 监测屏幕点击事件
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            guard let touch = touches.first else {
                return
            }
            let touchLocation = touch.location(in: self) ///获得点击的位置
            /// 判断目前的GameScene场景舞台是哪个state
            switch stateMachine.currentState {
            case is WaitingState:
                /// 获得按钮的点击位置
                guard let body = physicsWorld.body(at: touchLocation) else {
                    return
                }
                /// 判断是否是点击了PlayButton
                guard  let playButton = body.node?.childNode(withName: "PlayButton") as? SKSpriteNode else {
                    return
                }
                /// 如果点击位置是在PlayButton
                if (playButton.contains(touchLocation)){
                    playButton.isHidden = true
                    stateMachine.enter(PlayState.self) /// 进入开始游戏;
                }
                
            case is PlayState:
                reverseGravity() /// 反转物理世界;
                
            case is GameOverState:
                
                guard let body = physicsWorld.body(at: touchLocation) else {
                    return
                }
                // TapToPlay按钮;
                if let tapToPlay  = body.node?.childNode(withName: "tapToPlay"){
                    
                    if tapToPlay.contains(touchLocation){
                        print("重新开始游戏!")
                        restartGame()
                    }
                }
                
            default:
                break;
                
                
            }
        }
        // MARK: - 监测碰撞
        func didBegin(_ contact: SKPhysicsContact) {
            
            let bodyA:SKPhysicsBody
            let bodyB:SKPhysicsBody
            if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
                bodyA = contact.bodyA
                bodyB = contact.bodyB
            }else{
                bodyA = contact.bodyB
                bodyB = contact.bodyA
            }
            ///检测碰到中间线
            if bodyA.categoryBitMask == PhysicsCategory.Player && bodyB.categoryBitMask == PhysicsCategory.MiddleLine {
                /// print("碰到屏幕线人物反转")
                playerNode.reversePlayer() 
            }
            
            ///检测碰到coin
            if bodyA.categoryBitMask == PhysicsCategory.Player && bodyB.categoryBitMask == PhysicsCategory.Coin {
                /// print("碰到屏幕线人物反转")
                let coinAction = SKAction.playSoundFileNamed("coin.wav", waitForCompletion: false)
                run(coinAction)
                bodyB.node?.removeFromParent()
            }
            
            ///检测碰到Bomb
            if bodyA.categoryBitMask == PhysicsCategory.Player && bodyB.categoryBitMask == PhysicsCategory.Bomb {
                /// 播放音乐
                let bombAction = SKAction.playSoundFileNamed("ninjaHit.wav", waitForCompletion: false)
                run(bombAction)
                /// 移除BOMB
                /// bodyB.node?.removeFromParent()
                stateMachine.enter(GameOverState.self)
            }
            
        }
        // MARK: - 时时更新update
        override func update(_ currentTime: TimeInterval) {
            
            /// 获取时间差
            if lastUpdateTimeInterval == 0 {
                lastUpdateTimeInterval = currentTime
            }
            dt = currentTime - lastUpdateTimeInterval
            lastUpdateTimeInterval = currentTime
            
            stateMachine.update(deltaTime: dt)  /// 把update传进各个State里;
        }
    }
    

    源码传送门:https://github.com/apiapia/SkyNinjaGameSpriteKitTutorial
    更多手机游戏教程:http://www.iFIERO.com

    相关文章

      网友评论

        本文标题:iFIERO -- SkyNinja天猪之城 SpriteKit

        本文链接:https://www.haomeiwen.com/subject/wvpleftx.html