Coder Social home page Coder Social logo

spacebattlespritekitgame's Introduction

/*

  • 游戏中的所有元素全部由iFIERO所原创(除引用之外),包括人物、音乐、场景等,

  • 创作的初衷就是让更多的游戏爱好者可以在开发游戏中获得自豪感 -- 让手机游戏开发变得简单。

  • 秉着开源分享的原则,iFIERO发布的游戏都尽可能的易懂实用,并开放所有源码,

  • 任何使用者都可以使用游戏中的代码块,也可以进行拷贝、修改、更新、升级,无须再经过iFIERO的同意。

  • 但这并不表示可以任意复制、拆分其中的游戏元素:

  • 用于[商业目的]而不注明出处,

  • 用于[任何教学]而不注明出处,

  • 用于[游戏上架]而不注明出处;

  • 另外,iFIERO有商用授权游戏元素,获得iFIERO官方授权后,即无任何限制!

  • 请尊重帮助过你的iFIERO的知识产权,非常感谢!

  • Created by VANGO杨 && ANDREW陈

  • Copyright © 2018 iFiero. All rights reserved.

  • www.iFIERO.com

  • iFIERO -- 让手机游戏开发变得简单

  • SpaceBattle 宇宙大战 在此游戏中您将获得如下技能:

  • 1、LaunchScreen 学习如何设置游戏启动画面;

  • 2、Scenes 学习如何切换不同的场景 主菜单+游戏场景+游戏结束场景;

  • 3、Accleroation 利用重力加速度 让飞船左右移动;

  • 4、Endless Background 无限循环背景;

  • 5、Scene Edit 直接使用可见即所得操作;

  • 6、UserDefaults 保存游戏分数、最高分;

  • 7、Random 利用可复用的随机函数生成Enemy;

  • 8、Background Music 如何添加背景音乐;

  • 9、Particle 粒子爆炸特效; */

import SpriteKit import GameplayKit import CoreMotion

struct PhysicsCategory { // static let BulletRed :UInt32 = 0x1 << 1 // Alien的子弹 static let BulletBlue:UInt32 = 0x1 << 2 static let Alien :UInt32 = 0x1 << 3 static let SpaceShip :UInt32 = 0x1 << 4 static let None :UInt32 = 0 }

class GameScene: SKScene,SKPhysicsContactDelegate {

private var bgNode1:SKSpriteNode!
private var bgNode2:SKSpriteNode!
private var playerNode:SKSpriteNode!  // 玩家 宇宙飞船
private var currentScore:SKLabelNode! // 当前分数节点
private var cScore:Int = 0
private var highScore:SKLabelNode!    // 最高分数
private var hScore:Int = 0


var lastUpdateTimeInterval:TimeInterval = 0
var deltaTime:TimeInterval = 0
let motionManager = CMMotionManager() // 重力加速度管理器
var xAcceleration:CGFloat  = 0        // 存放x左右移动的加速度变量
var yAcceleration:CGFloat  = 0


override func didMove(to view: SKView) {
    // 建立物理世界 重力向下
    physicsWorld.gravity = CGVector(dx: 0, dy: -9.8)
    // 碰撞接触代理
    physicsWorld.contactDelegate = self
    // 背景节点
    bgNode1 = childNode(withName: "BG1") as! SKSpriteNode
    bgNode2 = childNode(withName: "BG2") as! SKSpriteNode
    // 分数节点
    currentScore = childNode(withName: "currentScore") as! SKLabelNode
    highScore    = childNode(withName: "highScore")    as! SKLabelNode
    
    //表示第一次加载游戏
    if !UserDefaults.standard.bool(forKey: "HIGHSCORE") {
        UserDefaults.standard.set(0, forKey: "CURRENTSCORE")
        UserDefaults.standard.set(0, forKey: "HIGHSCORE")
    }
    // 表示重新游戏
    UserDefaults.standard.set(0, forKey: "CURRENTSCORE")      // 清空沙盒中保存的上一局的分数
    hScore = UserDefaults.standard.integer(forKey: "HIGHSCORE")    // 取出沙盒中的数字
    highScore.text = "HIGH:\(hScore)"
    
    // 背景音乐
    let bgMusic = SKAudioNode(fileNamed: "spaceBattle.mp3")
    bgMusic.autoplayLooped = true
    addChild(bgMusic)
    // 加入玩家飞船
    playerNode = childNode(withName: "SpaceShip") as! SKSpriteNode
    playerNode.physicsBody = SKPhysicsBody(circleOfRadius: self.playerNode.size.width / 2)
    playerNode.physicsBody?.affectedByGravity = false // 不受物理世界的重力影响
    playerNode.physicsBody?.isDynamic = true 
    playerNode.physicsBody?.categoryBitMask    = PhysicsCategory.SpaceShip
    playerNode.physicsBody?.contactTestBitMask = PhysicsCategory.Alien
    playerNode.physicsBody?.collisionBitMask   = PhysicsCategory.None
    
    /*
     * 手机加速度感应
     * 注意:加速度感应在模拟器Simulater无法感应,须用真机进行调试
     */
    motionManager.accelerometerUpdateInterval = 0.2 // 感应时间
    motionManager.startAccelerometerUpdates(to: OperationQueue.current!) { (data, error) in
        //1. 取得data数据;
        guard let accelerometerData = data else {
            return
        }
        //2. 取得加速度
        let acceleration = accelerometerData.acceleration
        //3. 更新XAcceleration的值
        self.xAcceleration = CGFloat(acceleration.x) * 0.75 + self.xAcceleration * 0.5
        self.yAcceleration = CGFloat(acceleration.y) * 0.75 + self.yAcceleration * 0.5
        
    }
    // spawnAlien()
    Timer.scheduledTimer(timeInterval: TimeInterval(0.5), target: self, selector: #selector(GameScene.spawnAlien), userInfo: nil, repeats: true)
    
    // Action 无限生成Alien
    
}

override func update(_ currentTime: TimeInterval) {
    // 每Frame的时间差
    if lastUpdateTimeInterval == 0 {
        lastUpdateTimeInterval = currentTime
    }
    deltaTime = currentTime - lastUpdateTimeInterval
    lastUpdateTimeInterval = currentTime
    
    // endless 无限循环星空背景
    updateBackground(deltaTime: deltaTime)
}

func  updateBackground(deltaTime:TimeInterval){
    // 下移
    bgNode1.position.y -= CGFloat(deltaTime * 300)
    bgNode2.position.y -= CGFloat(deltaTime * 300)
    // 第一个背景node
    if bgNode1.position.y  < -bgNode1.size.height {
        bgNode1.position.y = bgNode2.position.y + bgNode2.size.height
    }
    // 第二个背景node
    if bgNode2.position.y  < -bgNode2.size.height {
        bgNode2.position.y = bgNode1.position.y + bgNode1.size.height
    }
    
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first else {
        return
    }
    let _ = touch.location(in: self) // touchLocation
    // 播放torpedo发射音乐
    let actionFire = SKAction.playSoundFileNamed("torpedo.mp3", waitForCompletion: false)
    run(actionFire)
    
    spawnBulletAndFire() // 生成并发射子弹
    
}
// MARK: - 生成并发射子弹;
func spawnBulletAndFire(){
    // 子弹
    let bulletNode = SKSpriteNode(imageNamed: "BulletBlue")
    bulletNode.position.x = playerNode.position.x
    // 子弹的Y轴位置 因为playNode的AnchorPoit位于飞船中心 所以子弹发射时的瞬间位置位于飞船正中心,要加上飞船的半径,位于枪口;
    bulletNode.position.y = playerNode.position.y + playerNode.size.height / 2
    bulletNode.zPosition = 1
    self.addChild(bulletNode)
    bulletNode.physicsBody = SKPhysicsBody(circleOfRadius: bulletNode.size.width / 2)
    bulletNode.physicsBody?.affectedByGravity = false // 子弹不受重力影响;
    bulletNode.physicsBody?.categoryBitMask   =  PhysicsCategory.BulletBlue
    bulletNode.physicsBody?.contactTestBitMask = PhysicsCategory.Alien
    bulletNode.physicsBody?.collisionBitMask = PhysicsCategory.None
    //子弹飞速运动,设置探测精细碰撞
    bulletNode.physicsBody?.usesPreciseCollisionDetection = true
    
    // 把子弹往上移出屏幕
    let moveTo = CGPoint(x: playerNode.position.x, y: playerNode.position.y + self.frame.size.height)
    // bulletNode.run(SKAction.move(to:moveTo, duration: TimeInterval(0.5)))
    /*
     * 粒子效果
     * 1.新建一个SKNODE => trailNode
     * 2.新建粒子效果SKEmitterNode,设置tragetNode = trailNode
     * 3.子弹加上emitterNode
     */
    let trailNode = SKNode()
    trailNode.zPosition = 1
    trailNode.name = "trail"
    addChild(trailNode)
    
    let emitterNode = SKEmitterNode(fileNamed: "ShootTrailBlue")! // particles文件夹存放粒子效果
    emitterNode.targetNode = trailNode  // 设置粒子效果的目标为trailNode => 跟随新建的trailNode
    bulletNode.addChild(emitterNode)    // 在子弹节点Node加上粒子效果;
    
    bulletNode.run(SKAction.sequence([
        SKAction.move(to: moveTo, duration: TimeInterval(0.5)),
        SKAction.run({
            bulletNode.removeFromParent() // 移除 子弹bulltedNode
            trailNode.removeFromParent()  // 移除 trailNode
        })]))
}
// 生成随机Alien
@objc func spawnAlien() {
    // 1 or 2
    let i = Int(CGFloat(arc4random()).truncatingRemainder(dividingBy: 2) + 1)
    
    let imageName = "Enemy0\(i)"
    let alien  = SKSpriteNode(imageNamed: imageName)
    alien.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    alien.zPosition   = 1
    alien.name = "Alien"
    var xPosition:CGFloat = 0.0
    // 生成随机的x-Axis轴的位置
    xPosition = CGFloat.random(min: -self.frame.size.width+alien.size.width, max: self.frame.size.width - alien.size.width)
    alien.position = CGPoint(x: xPosition, y: self.frame.size.height + alien.size.height * 2)
    self.addChild(alien)
    // 物理世界 PhysicsWorld
    // 1.设置物理身体
    alien.physicsBody = SKPhysicsBody(circleOfRadius: alien.size.width / 2)
    // 不受重力影响,自定义飞船移动速度;
    alien.physicsBody?.affectedByGravity = false
    // 2.设置唯一属性
    alien.physicsBody?.categoryBitMask   = PhysicsCategory.Alien
    // 3.和哪些节点Node会发生碰撞
    alien.physicsBody?.contactTestBitMask = PhysicsCategory.BulletBlue | PhysicsCategory.SpaceShip
    alien.physicsBody?.collisionBitMask   = PhysicsCategory.None
    
    let duration = CGFloat.random(min: CGFloat(1.0), max: CGFloat(3.8))
    let actionDown = SKAction.move(to: CGPoint(x: xPosition, y: -self.frame.size.height), duration: TimeInterval(duration))
    alien.run(SKAction.sequence([actionDown,
                                 SKAction.run({
                                    alien.removeFromParent() // 移除节点;
                                 })]))
    
}
// 手机重力感应
override func didSimulatePhysics() {
    // 取得xAcceleration的位置并进行更新
    self.playerNode.position.x += self.xAcceleration * 50
    self.playerNode.position.y += self.yAcceleration * 50
    // 让player => SpaceShip在屏幕之间滑动 x
    // X-Axis X轴水平方向 最小值
    // 如果player的x-axis最小值 < player飞船的size.with 1/2 设飞船的最小值为 size.with/2
    if self.playerNode.position.x <  -self.frame.size.width / 2 + self.playerNode.size.width {
        self.playerNode.position.x =  -self.frame.size.width / 2 + self.playerNode.size.width
    }
    // 最大值
    if self.playerNode.position.x >   self.frame.size.width / 2 - self.playerNode.size.width {
        self.playerNode.position.x =  self.frame.size.width / 2 - self.playerNode.size.width
    }
    // Y-Axis Y轴方向
    if self.playerNode.position.y  > -self.playerNode.size.height {
        self.playerNode.position.y =  -self.playerNode.size.height
    }
    
    if self.playerNode.position.y <  -self.frame.size.height / 2 + self.playerNode.size.height {
        self.playerNode.position.y = -self.frame.size.height / 2 + self.playerNode.size.height
    }
}

//MARK:- 发生碰撞
func didBegin(_ contact: SKPhysicsContact) {
    
    let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
    
    switch contactMask {
    // 子弹vs外星人
    case PhysicsCategory.Alien | PhysicsCategory.BulletBlue:
        bulletHitAlien(nodeA: contact.bodyA.node as! SKSpriteNode,nodeB: contact.bodyB.node as! SKSpriteNode)
    // 外星人Alien撞击到飞船
    case PhysicsCategory.Alien | PhysicsCategory.SpaceShip:
        alienHitSpaceShip(nodeA: contact.bodyA.node as! SKSpriteNode, nodeB: contact.bodyB.node as! SKSpriteNode)
        
    default:
        break
    }
}
// MARK: 子弹vs外星人
func bulletHitAlien(nodeA:SKSpriteNode,nodeB:SKSpriteNode){
    
    // 击中粒子效果 Particle
    let explosion = SKEmitterNode(fileNamed: "ExplosionBlue")!
    explosion.position = nodeA.position // 或者 nodeB.position
    self.addChild(explosion)
    
    explosion.run(SKAction.sequence([
        SKAction.wait(forDuration: 0.3),
        SKAction.run {
            explosion.removeFromParent()
        }]))
    // 击中的音乐
    let actionColision = SKAction.playSoundFileNamed("explosion.mp3", waitForCompletion: false)
    run(actionColision)
    
    // 分数统计
    cScore += 1
    currentScore.text = "SCORE:\(cScore)"
    // 保存当前分数
    UserDefaults.standard.set(cScore, forKey: "CURRENTSCORE")
    
    if cScore > hScore {
        hScore = cScore
        highScore.text = "High:\(hScore)"
        // 保存最高分数
        UserDefaults.standard.set(cScore, forKey: "HIGHSCORE")
    }
    // 判断哪个是子弹节点bulletNode,碰撞didBegin没有比较大小时,则会相互切换,也就是A和B互相切换;
    if nodeA.physicsBody?.categoryBitMask == PhysicsCategory.BulletBlue {
        nodeA.removeAllChildren() // 移除所有子效果 emitter
        nodeA.isHidden = true     // 子弹隐藏
        nodeA.physicsBody?.categoryBitMask = 0 // 设置子弹不会再发生碰撞
        nodeB.removeFromParent()  // 移除外星人
    }else if nodeB.physicsBody?.categoryBitMask == PhysicsCategory.BulletBlue {
        nodeA.removeFromParent()  // 移除外星人
        nodeB.removeAllChildren()
        nodeB.isHidden =  true
        nodeB.physicsBody?.categoryBitMask = 0
    }
}

// MARK: 外星人Alien撞击到飞船
func alienHitSpaceShip(nodeA:SKSpriteNode,nodeB:SKSpriteNode){
    
    if (nodeA.physicsBody?.categoryBitMask == PhysicsCategory.Alien  || nodeB.physicsBody?.categoryBitMask == PhysicsCategory.Alien) && (nodeA.physicsBody?.categoryBitMask == PhysicsCategory.SpaceShip || nodeB.physicsBody?.categoryBitMask == PhysicsCategory.SpaceShip) {
        // print("撞击到飞船")
        // 击中粒子效果 Particle
        let explosion = SKEmitterNode(fileNamed: "Explosion")!
        explosion.position = nodeA.position
        self.addChild(explosion)
        nodeA.removeFromParent()
        nodeB.removeFromParent()
        // 播放失败音乐 + 切换到结束游戏
        let loseMusicAction = SKAction.playSoundFileNamed("", waitForCompletion: false)
        self.run(SKAction.sequence([
            loseMusicAction,
            SKAction.wait(forDuration: TimeInterval(0.7)),
            SKAction.run {
                // 切换游戏结束场景
                let reveal = SKTransition.doorsOpenHorizontal(withDuration: TimeInterval(0.5))
                let loseScene = LoseScene(fileNamed: "LoseScene")
                loseScene?.size = self.size
                loseScene?.scaleMode = .aspectFill
                self.view?.presentScene(loseScene!, transition: reveal)
            }]))
        
    }
    
}

}

spacebattlespritekitgame's People

Contributors

apiapia avatar

Watchers

 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.