Code Monkey home page Code Monkey logo

skyninjagamespritekittutorial's Introduction

SKY NINJA SpriteKit Game Tutorial

GIF动画演示:https://upload-images.jianshu.io/upload_images/3896436-8488f7fa265bdeb1.gif?imageMogr2/auto-orient/strip

// // 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
百度源码备份:https://pan.baidu.com/s/1_1rt4fZR2OuDSg-lFDAUQw
更多手机游戏教程:http://www.iFIERO.com

skyninjagamespritekittutorial's People

Contributors

apiapia avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.