Skip to content
iFIERO -- TUTORIALS FOR IPHONE GAMES
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
SpaceBattle.xcodeproj
SpaceBattle
.DS_Store
README.md

README.md

*** 更新重力感应为游戏摇杆 ***

/*

  • 游戏中的所有元素全部由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)
            }]))
        
    }
    
}

}

视频讲解:宇宙大战 SPACE BATTLE (一) SpriteKit游戏视频教程 http://www.ifiero.com/index.php/archives/spacebattlespritekitvideogametutorial-1 http://www.ifiero.com/index.php/archives/spacebattlespritekitvideogametutorial-2

You can’t perform that action at this time.