(三)宇宙大战 Space Battle -- 场景SCENE切换、UserDefaults统计分数、Particle粒子效果
此《宇宙大战 Space Battle》SpirteKit手机游戏教程共分为三系列:
(一)宇宙大战 Space Battle -- 新建场景Scene、精灵节点、Particle粒子及背景音乐
(二)宇宙大战 Space Battle -- 无限循环背景Endless、SpriteKit物理碰撞、CoreMotion加速计
(三)宇宙大战 Space Battle — 场景SCENE切换、UserDefaults统计分数、Particle粒子效果(你正在此处进行学习)
一、如何进行各个场景之间的切换
如上图所示,共分为三个场景:
1、MainScene.sks -- 用户打开APP时一开始看到的画面,等待用户点击"Play"按钮;
2、GameScene.sks -- 游戏进行中的场景画面,用于创建无限循环背景Endless、监测SpriteKit物理碰撞、应用CoreMotion加速计,判断游戏的业务逻辑;
3、LoseScene.sks -- 游戏结束时的场景画面,记录当届分数,记录最高分并应用UserDefaults储存分数在手机沙盒当中,点击"Tap to play"按钮回到GameScene游戏场景画面;
我们依据第一节所学到的知识,新建一个文件,在Scenes文件夹中,Mouse右建 -> New File -> 选择 iOS -> SpriteKit Scene -> Next 命名一个新的场景为 MainScene.sks
另新建一个文件,也是在Scenes文件夹中,Mouse右建 -> New File -> 选择 iOS -> Swift File -> Next 命名为 MainScene.swfit ,关联 MainScene.sks的 Custom Class 为 MainScene.swift
在Game ViewController设置开始场景为 MainScene.sks
if let scene = MainScene(fileNamed: "MainScene") {
scene.size = CGSize(width: 1536, height: 2048)
scene.scaleMode = .aspectFill
view.presentScene(scene)
}
设置好启动场景后,我们再来MainScene.swfit编写代码:
在didMove(to view: SKView)里,
override func didMove(to view: SKView) {
/// Play为场景命名的节点名称
play = childNode(withName: "Play") as! SKSpriteNode
learnTemp = childNode(withName: "learnTemp") as! SKSpriteNode
// 背景音乐
let bgMusic = SKAudioNode(fileNamed: "spaceBattle.mp3")
bgMusic.autoplayLooped = true
addChild(bgMusic)
}
接下来,我们就要来判断用户的触摸位置,是不是按到 Play 按钮
在 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) 函数里:
/// 判断用户是否有点击
guard let touch = touches.first else {
return
}
let touchLocation = touch.location(in: self) /// 获得点击的位置
if play.contains(touchLocation) {
/// 表示触摸点击在play按钮当中
}
把切换进入GameScene.sks的代码写在 play.contains(){}函数里
let reveal = SKTransition.doorsOpenVertical(withDuration: TimeInterval(0.5))
///场景切换
let mainScene = GameScene(fileNamed: "GameScene")
mainScene?.size = self.size
mainScene?.scaleMode = .aspectFill
self.view?.presentScene(mainScene!, transition: reveal)
这样子,我们就完成了场景之间的切换了!
同样,GameScene切换到LoseScene、LoseScene切换为GameScene 也是应用 self.view?.presentScene的方法,具体代码如下:
在外星人Alien撞击到飞船,游戏结束,GameScene切换到LoseScene:
// MARK: 外星人Alien撞击到飞船,游戏结束
func alienHitSpaceShip(nodeA:SKSpriteNode,nodeB:SKSpriteNode){
// 切换游戏结束场景
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)
}
二、应用UserDefaults储存游戏分数和最高分
我们在GameScene.swift里
private var currentScore:SKLabelNode! // 当前分数节点
private var cScore:Int = 0 /// Int 存当前分数
private var highScore:SKLabelNode! // 最高分数
private var hScore:Int = 0 /// Int 存最高分数
在子弹击中外星人时记录分数
func bulletHitAlien(nodeA:SKSpriteNode,nodeB:SKSpriteNode){}
func bulletHitAlien(nodeA:SKSpriteNode,nodeB:SKSpriteNode){
// 分数统计
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")
}
}
我们应用UserDefaults.standard.set方法,分别储存当前分数和最高分数对应的键值forKey:CURRENTSCORE和HIGHSCORE,然后,在游戏结束的场景LoseScene.swift通过UserDefaults.standard.integer(forKey: "CURRENTSCORE")取出存在手机沙盒里的值;
currentScore.text = "SCORE:\(UserDefaults.standard.integer(forKey: "CURRENTSCORE"))" // 取出当前分数
highScore.text = "HIGH SCORE:\(UserDefaults.standard.integer(forKey: "HIGHSCORE"))" // 取出沙盒中的最高分数
代码如下:
private var currentScore:SKLabelNode! // 当局分数
private var highScore:SKLabelNode! // 最高分数
override func didMove(to view: SKView) {
// 找到 名称为Play的节点
play = childNode(withName: "Play") as! SKSpriteNode
currentScore = childNode(withName: "currentScore") as! SKLabelNode
highScore = childNode(withName: "highScore") as! SKLabelNode
currentScore.text = "SCORE:\(UserDefaults.standard.integer(forKey: "CURRENTSCORE"))" // 取出当前分数
highScore.text = "HIGH SCORE:\(UserDefaults.standard.integer(forKey: "HIGHSCORE"))" // 取出沙盒中的最高分数
}
我们补充一下有关Swift数据储存方式的相关知识,数据储存是存在iOS沙盒的当中,沙盒,顾名思义,即各个app之间是无法互相访问数据的,其目录结构为:
每个iOS应用都有自己的应用沙盒(应用沙盒就是文件系统目录),与其他文件系统隔离。应用必须待在自己的沙盒里,其他应用不能访问该沙盒。沙盒下的目录如下:
Documents: 保存应⽤运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录。例如,游戏应用可将游戏存档保存在该目录。
tmp: 保存应⽤运行时所需的临时数据,使⽤完毕后再将相应的文件从该目录删除。应用 没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时不会备份该目录。
Library/Caches: 保存应用运行时⽣成的需要持久化的数据,iTunes同步设备时不会备份 该目录。⼀一般存储体积大、不需要备份的非重要数据,比如网络数据缓存存储到Caches下。
Library/Preference: 保存应用的所有偏好设置,如iOS的Settings(设置) 应⽤会在该目录中查找应⽤的设置信息。iTunes同步设备时会备份该目录。
UserDefaults可以存储的数据类型:NSData、NSString、NSNumber、NSDate、NSArray、NSDictionary,如果把有null的value放入userDefaults,程序会崩。
//存储基础类型,以Int为例。
UserDefaults.standard.set(15, forKey:"theKey")
//读取基础类型,以Int为例。
let num = UserDefaults.standard.integer(forKey: "theKey")
注意:不要用UserDefaults储存用户的密码。
三、SpirteKit Particle粒子效果
我们选中Explosion.sks来查看Particle粒子效果,右侧为粒子效果的属性面板,我们可以修改属性中的相关值,来观察爆炸的变化。
在代码中引入,外星人Alien撞击到飞船引入粒子特效。
// 击中粒子效果 Particle
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) {
let explosion = SKEmitterNode(fileNamed: "Explosion")!
explosion.position = nodeA.position
self.addChild(explosion)
}
}
子弹击中外星飞船的粒子特效
// MARK: 子弹vs外星人
func bulletHitAlien(nodeA:SKSpriteNode,nodeB:SKSpriteNode){
// 击中粒子效果 Particle
let explosion = SKEmitterNode(fileNamed: "ExplosionBlue")!
explosion.position = nodeA.position
self.addChild(explosion)
explosion.run(SKAction.sequence([
SKAction.wait(forDuration: 0.3),
SKAction.run {
explosion.removeFromParent()
}]))
}
子弹引入粒子特效,在func spawnBulletAndFire(){}里
/*
* 粒子效果
* 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加上粒子效果;
如果去除 emitterNode.targetNode = trailNode,则子弹没有拖影特效
其中特别要注意的是要判断哪个是子弹精灵节点,并移除所有子弹精灵的子节点的特效。
nodeA.removeAllChildren() // 移除所有子效果 emitter
// 判断哪个是子弹节点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
}
至此,我们就完成了Space Battle宇宙大战SpirteKit游戏的所有章节了。
很感谢大家的阅读,如果有疑问可以在文章下方的评论栏提问,也请大家多多指出文中的不足之处,一起努力,一起从开发游戏当中获得满满的乐趣,收获满满的自豪感。
游戏源码传送门:https://github.com/apiapia/SpaceBattleSpriteKitGame
更多游戏教程:http://www.iFIERO.com
(三)宇宙大战 Space Battle -- 场景SCENE切换、UserDefaults统计分数、Particle粒子效果的更多相关文章
- iFIERO - (一) 宇宙大战 SPACE BATTLE — 场景SCENE、SpriteKit精灵、PARTICLE粒子及背景音乐
开始游戏教程前,首先介绍一下SpriteKit是什么?SpriteKit提供了一个图形渲染和动画的基础结构,你可以使用它让任意类型的纹理图片或者精灵动起来.SpriteKit使用渲染循环,利用图形硬件 ...
- iFIERO - (二)宇宙大战 Space Battle -- SpriteKit 无限循环背景Endless、SpriteKit物理碰撞、CoreMotion加速计
本节主要讲解如何创建无限循环Endless的星空背景(如下图).玩家飞船发射子弹,监测子弹击外星敌机的SpriteKit物理碰撞并消灭敌机,以及应用iOS的CoreMotion加速计移动飞船躲避外星敌 ...
- 【Cocos2dx】新建场景、场景的切换、设置启动场景与菜单的新建
这是Cocos2dx最简单的部分.主要是体现对场景的操作,其实这东西就是Flash的舞台,安卓的Activity,WIN32窗体程序的Framework窗体,网页的body,反正就是对那个容纳各种东西 ...
- iOS宇宙大战游戏、调试工具、各种动画、AR相册、相机图片编辑等源码
iOS精选源码 日期时间选择器,swift Space Battle 宇宙大战 SpriteKit游戏源码 LLDebugTool - 便捷的IOS调试工具(新增截屏功能) 相机扫描or长按识别二维码 ...
- Git知识总览(三) 分支的创建、删除、切换、合并以及冲突解决
前两篇博客集中的聊了git的一些常用命令,具体请参见<Git知识总览(一) 从 git clone 和 git status 谈起>.<Git知识总览(二) git常用命令概览> ...
- iOS Sprite Kit教程之场景的切换
iOS Sprite Kit教程之场景的切换 Sprite Kit中切换场景 每一个场景都不是单独存在的.玩家可以从一个场景中切换到另外一个场景中.本小节,我们来讲解场景切换.在每一个游戏中都会使用到 ...
- Phaser3 场景Scene之间的传值 -- HTML JAVASCRIPT 网页游戏开发
PHASERJS3 一.首先当然得有至少有二个场景sceneA.js,sceneB.js 二.从场景A传值到场景B二种方法 1)通过事件this.events.emit('event key',{ ...
- Phaser3 场景Scene之间的传值 -- HTML网页游戏开发
一.首先当然得有至少有二个场景sceneA.js,sceneB.js 二.从场景A传值到场景B二种方法 1)通过事件this.events.emit('event key',{objKey:objVa ...
- Spring事务Transactional和动态代理(三)-事务失效的场景
系列文章索引: Spring事务Transactional和动态代理(一)-JDK代理实现 Spring事务Transactional和动态代理(二)-cglib动态代理 Spring事务Transa ...
随机推荐
- UliPad安装
1 http://www.cnblogs.com/dolphin0520/p/4012804.html 2 http://www.iplaypython.com/editor/ulipad.html
- Java虚拟机垃圾回收:基础点(转载)
1.Java虚拟机垃圾回收 垃圾回收,或称垃圾收集(Garbage Collection,GC)是指自动管理回收不再被引用的内存数据. 在1960年诞生于MIT的Lisp语言首次使用了动态内存分配和垃 ...
- toad安装错误—Failed to Download products and updates
近日,在公司云电脑上安装Toad for oracle,安装到中途总会出现如下错误,个人认为是Toad安装时需要下载/更新一些组件,公司网络对下载有所限制,导致报错,无法进行后续安装. 图1.Toad ...
- 卸载MySQL以及重装卡到Start Services的解决办法(亲测有效,刚重装成功)
卸载MySQL以及重装卡到Start Services的解决办法 重装系统永远是个好办法,但是对于我们程序员来说只要一想到电脑上的环境变量和其他的配置就蔫了.所以这一条就当作是废话吧. 一般来说装My ...
- cornerstone提交报错"but is missing"以及xocde提示"missing from working copy"
问题描述 xocde提示"missing from working copy" 虽然这种警告不会影响程序到运行,但是数量很多,而且在svn提交的时候回出现这种问题 使用的svn工具 ...
- 『ACM C++』 PTA 天梯赛练习集L1 | 016-017
今天开了两个大会,时间都给占掉了,就刷了两道题~ 明天加油!!! ------------------------------------------------L1-016------------- ...
- 没有上司的舞会(树形DP)
题目描述 某大学有N个职员,编号为1~N.他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司.现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri, ...
- 使用Python第三方库生成二维码
本文主要介绍两个可用于生成二维码的Python第三方库:MyQR和qrcode. MyQR的使用: 安装: pip install MyQR 导入: from MyQR import myqr imp ...
- MySQL数据库查看数据表占用空间大小和记录数
MySQL数据库中每个表占用的空间.表记录的行数的话,可以打开MySQL的 information_schema 数据库.在该库中有一个 TABLES 表,这个表主要字段分别是: TABLE_SCHE ...
- vi/vim连续注释
知识点: 1-可视块模式方法 2-替换方法 3-自定义快捷键方式 今天刚好重新在linux上手工搭建完Lamp环境,用来下vi操作,一段时间不用就有些生疏了,正好经常要注释,回顾下自己会的方法,小结一 ...