当前位置:   article > 正文

iFIERO -- Space Battle 宇宙大战 SpriteKit游戏源码_宇宙大战源代码

宇宙大战源代码




  1. /*
  2. * 游戏中的所有元素全部由iFIERO所原创(除引用之外),包括人物、音乐、场景等,
  3. * 创作的初衷就是让更多的游戏爱好者可以在开发游戏中获得自豪感 -- 让手机游戏开发变得简单。
  4. * 秉着开源分享的原则,iFIERO发布的游戏都尽可能的易懂实用,并开放所有源码,
  5. * 任何使用者都可以使用游戏中的代码块,也可以进行拷贝、修改、更新、升级,无须再经过iFIERO的同意。
  6. * 但这并不表示可以任意复制、拆分其中的游戏元素:
  7. * 用于[商业目的]而不注明出处,
  8. * 用于[任何教学]而不注明出处,
  9. * 用于[游戏上架]而不注明出处;
  10. * 另外,iFIERO有商用授权游戏元素,获得iFIERO官方授权后,即无任何限制!
  11. * 请尊重帮助过你的iFIERO的知识产权,非常感谢!
  12. *
  13. * Created by VANGO杨 && ANDREW陈
  14. * Copyright © 2018 iFiero. All rights reserved.
  15. * www.iFIERO.com
  16. * iFIERO -- 让手机游戏开发变得简单
  17. *
  18. * SpaceBattle 宇宙大战 在此游戏中您将获得如下技能:
  19. * 1、LaunchScreen 学习如何设置游戏启动画面;
  20. * 2、Scenes 学习如何切换不同的场景 主菜单+游戏场景+游戏结束场景;
  21. * 3、Accleroation 利用重力加速度 让飞船左右移动;
  22. * 4、Endless Background 无限循环背景;
  23. * 5、Scene Edit 直接使用可见即所得操作;
  24. * 6、UserDefaults 保存游戏分数、最高分;
  25. * 7、Random 利用可复用的随机函数生成Enemy;
  26. * 8、Background Music 如何添加背景音乐;
  27. * 9、Particle 粒子爆炸特效;
  28. */
  29. import SpriteKit
  30. import GameplayKit
  31. import CoreMotion
  32. struct PhysicsCategory {
  33. // static let BulletRed :UInt32 = 0x1 << 1 // Alien的子弹
  34. static let BulletBlue:UInt32 = 0x1 << 2
  35. static let Alien :UInt32 = 0x1 << 3
  36. static let SpaceShip :UInt32 = 0x1 << 4
  37. static let None :UInt32 = 0
  38. }
  39. class GameScene: SKScene,SKPhysicsContactDelegate {
  40. private var bgNode1:SKSpriteNode!
  41. private var bgNode2:SKSpriteNode!
  42. private var playerNode:SKSpriteNode! // 玩家 宇宙飞船
  43. private var currentScore:SKLabelNode! // 当前分数节点
  44. private var cScore:Int = 0
  45. private var highScore:SKLabelNode! // 最高分数
  46. private var hScore:Int = 0
  47. var lastUpdateTimeInterval:TimeInterval = 0
  48. var deltaTime:TimeInterval = 0
  49. let motionManager = CMMotionManager() // 重力加速度管理器
  50. var xAcceleration:CGFloat = 0 // 存放x左右移动的加速度变量
  51. var yAcceleration:CGFloat = 0
  52. override func didMove(to view: SKView) {
  53. // 建立物理世界 重力向下
  54. physicsWorld.gravity = CGVector(dx: 0, dy: -9.8)
  55. // 碰撞接触代理
  56. physicsWorld.contactDelegate = self
  57. // 背景节点
  58. bgNode1 = childNode(withName: "BG1") as! SKSpriteNode
  59. bgNode2 = childNode(withName: "BG2") as! SKSpriteNode
  60. // 分数节点
  61. currentScore = childNode(withName: "currentScore") as! SKLabelNode
  62. highScore = childNode(withName: "highScore") as! SKLabelNode
  63. //表示第一次加载游戏
  64. if !UserDefaults.standard.bool(forKey: "HIGHSCORE") {
  65. UserDefaults.standard.set(0, forKey: "CURRENTSCORE")
  66. UserDefaults.standard.set(0, forKey: "HIGHSCORE")
  67. }
  68. // 表示重新游戏
  69. UserDefaults.standard.set(0, forKey: "CURRENTSCORE") // 清空沙盒中保存的上一局的分数
  70. hScore = UserDefaults.standard.integer(forKey: "HIGHSCORE") // 取出沙盒中的数字
  71. highScore.text = "HIGH:\(hScore)"
  72. // 背景音乐
  73. let bgMusic = SKAudioNode(fileNamed: "spaceBattle.mp3")
  74. bgMusic.autoplayLooped = true
  75. addChild(bgMusic)
  76. // 加入玩家飞船
  77. playerNode = childNode(withName: "SpaceShip") as! SKSpriteNode
  78. playerNode.physicsBody = SKPhysicsBody(circleOfRadius: self.playerNode.size.width / 2)
  79. playerNode.physicsBody?.affectedByGravity = false // 不受物理世界的重力影响
  80. playerNode.physicsBody?.isDynamic = true
  81. playerNode.physicsBody?.categoryBitMask = PhysicsCategory.SpaceShip
  82. playerNode.physicsBody?.contactTestBitMask = PhysicsCategory.Alien
  83. playerNode.physicsBody?.collisionBitMask = PhysicsCategory.None
  84. /*
  85. * 手机加速度感应
  86. * 注意:加速度感应在模拟器Simulater无法感应,须用真机进行调试
  87. */
  88. motionManager.accelerometerUpdateInterval = 0.2 // 感应时间
  89. motionManager.startAccelerometerUpdates(to: OperationQueue.current!) { (data, error) in
  90. //1. 取得data数据;
  91. guard let accelerometerData = data else {
  92. return
  93. }
  94. //2. 取得加速度
  95. let acceleration = accelerometerData.acceleration
  96. //3. 更新XAcceleration的值
  97. self.xAcceleration = CGFloat(acceleration.x) * 0.75 + self.xAcceleration * 0.5
  98. self.yAcceleration = CGFloat(acceleration.y) * 0.75 + self.yAcceleration * 0.5
  99. }
  100. // spawnAlien()
  101. Timer.scheduledTimer(timeInterval: TimeInterval(0.5), target: self, selector: #selector(GameScene.spawnAlien), userInfo: nil, repeats: true)
  102. // Action 无限生成Alien
  103. }
  104. override func update(_ currentTime: TimeInterval) {
  105. // 每Frame的时间差
  106. if lastUpdateTimeInterval == 0 {
  107. lastUpdateTimeInterval = currentTime
  108. }
  109. deltaTime = currentTime - lastUpdateTimeInterval
  110. lastUpdateTimeInterval = currentTime
  111. // endless 无限循环星空背景
  112. updateBackground(deltaTime: deltaTime)
  113. }
  114. func updateBackground(deltaTime:TimeInterval){
  115. // 下移
  116. bgNode1.position.y -= CGFloat(deltaTime * 300)
  117. bgNode2.position.y -= CGFloat(deltaTime * 300)
  118. // 第一个背景node
  119. if bgNode1.position.y < -bgNode1.size.height {
  120. bgNode1.position.y = bgNode2.position.y + bgNode2.size.height
  121. }
  122. // 第二个背景node
  123. if bgNode2.position.y < -bgNode2.size.height {
  124. bgNode2.position.y = bgNode1.position.y + bgNode1.size.height
  125. }
  126. }
  127. override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
  128. guard let touch = touches.first else {
  129. return
  130. }
  131. let _ = touch.location(in: self) // touchLocation
  132. // 播放torpedo发射音乐
  133. let actionFire = SKAction.playSoundFileNamed("torpedo.mp3", waitForCompletion: false)
  134. run(actionFire)
  135. spawnBulletAndFire() // 生成并发射子弹
  136. }
  137. // MARK: - 生成并发射子弹;
  138. func spawnBulletAndFire(){
  139. // 子弹
  140. let bulletNode = SKSpriteNode(imageNamed: "BulletBlue")
  141. bulletNode.position.x = playerNode.position.x
  142. // 子弹的Y轴位置 因为playNode的AnchorPoit位于飞船中心 所以子弹发射时的瞬间位置位于飞船正中心,要加上飞船的半径,位于枪口;
  143. bulletNode.position.y = playerNode.position.y + playerNode.size.height / 2
  144. bulletNode.zPosition = 1
  145. self.addChild(bulletNode)
  146. bulletNode.physicsBody = SKPhysicsBody(circleOfRadius: bulletNode.size.width / 2)
  147. bulletNode.physicsBody?.affectedByGravity = false // 子弹不受重力影响;
  148. bulletNode.physicsBody?.categoryBitMask = PhysicsCategory.BulletBlue
  149. bulletNode.physicsBody?.contactTestBitMask = PhysicsCategory.Alien
  150. bulletNode.physicsBody?.collisionBitMask = PhysicsCategory.None
  151. //子弹飞速运动,设置探测精细碰撞
  152. bulletNode.physicsBody?.usesPreciseCollisionDetection = true
  153. // 把子弹往上移出屏幕
  154. let moveTo = CGPoint(x: playerNode.position.x, y: playerNode.position.y + self.frame.size.height)
  155. // bulletNode.run(SKAction.move(to:moveTo, duration: TimeInterval(0.5)))
  156. /*
  157. * 粒子效果
  158. * 1.新建一个SKNODE => trailNode
  159. * 2.新建粒子效果SKEmitterNode,设置tragetNode = trailNode
  160. * 3.子弹加上emitterNode
  161. */
  162. let trailNode = SKNode()
  163. trailNode.zPosition = 1
  164. trailNode.name = "trail"
  165. addChild(trailNode)
  166. let emitterNode = SKEmitterNode(fileNamed: "ShootTrailBlue")! // particles文件夹存放粒子效果
  167. emitterNode.targetNode = trailNode // 设置粒子效果的目标为trailNode => 跟随新建的trailNode
  168. bulletNode.addChild(emitterNode) // 在子弹节点Node加上粒子效果;
  169. bulletNode.run(SKAction.sequence([
  170. SKAction.move(to: moveTo, duration: TimeInterval(0.5)),
  171. SKAction.run({
  172. bulletNode.removeFromParent() // 移除 子弹bulltedNode
  173. trailNode.removeFromParent() // 移除 trailNode
  174. })]))
  175. }
  176. // 生成随机Alien
  177. @objc func spawnAlien() {
  178. // 1 or 2
  179. let i = Int(CGFloat(arc4random()).truncatingRemainder(dividingBy: 2) + 1)
  180. let imageName = "Enemy0\(i)"
  181. let alien = SKSpriteNode(imageNamed: imageName)
  182. alien.anchorPoint = CGPoint(x: 0.5, y: 0.5)
  183. alien.zPosition = 1
  184. alien.name = "Alien"
  185. var xPosition:CGFloat = 0.0
  186. // 生成随机的x-Axis轴的位置
  187. xPosition = CGFloat.random(min: -self.frame.size.width+alien.size.width, max: self.frame.size.width - alien.size.width)
  188. alien.position = CGPoint(x: xPosition, y: self.frame.size.height + alien.size.height * 2)
  189. self.addChild(alien)
  190. // 物理世界 PhysicsWorld
  191. // 1.设置物理身体
  192. alien.physicsBody = SKPhysicsBody(circleOfRadius: alien.size.width / 2)
  193. // 不受重力影响,自定义飞船移动速度;
  194. alien.physicsBody?.affectedByGravity = false
  195. // 2.设置唯一属性
  196. alien.physicsBody?.categoryBitMask = PhysicsCategory.Alien
  197. // 3.和哪些节点Node会发生碰撞
  198. alien.physicsBody?.contactTestBitMask = PhysicsCategory.BulletBlue | PhysicsCategory.SpaceShip
  199. alien.physicsBody?.collisionBitMask = PhysicsCategory.None
  200. let duration = CGFloat.random(min: CGFloat(1.0), max: CGFloat(3.8))
  201. let actionDown = SKAction.move(to: CGPoint(x: xPosition, y: -self.frame.size.height), duration: TimeInterval(duration))
  202. alien.run(SKAction.sequence([actionDown,
  203. SKAction.run({
  204. alien.removeFromParent() // 移除节点;
  205. })]))
  206. }
  207. // 手机重力感应
  208. override func didSimulatePhysics() {
  209. // 取得xAcceleration的位置并进行更新
  210. self.playerNode.position.x += self.xAcceleration * 50
  211. self.playerNode.position.y += self.yAcceleration * 50
  212. // 让player => SpaceShip在屏幕之间滑动 x
  213. // X-Axis X轴水平方向 最小值
  214. // 如果player的x-axis最小值 < player飞船的size.with 1/2 设飞船的最小值为 size.with/2
  215. if self.playerNode.position.x < -self.frame.size.width / 2 + self.playerNode.size.width {
  216. self.playerNode.position.x = -self.frame.size.width / 2 + self.playerNode.size.width
  217. }
  218. // 最大值
  219. if self.playerNode.position.x > self.frame.size.width / 2 - self.playerNode.size.width {
  220. self.playerNode.position.x = self.frame.size.width / 2 - self.playerNode.size.width
  221. }
  222. // Y-Axis Y轴方向
  223. if self.playerNode.position.y > -self.playerNode.size.height {
  224. self.playerNode.position.y = -self.playerNode.size.height
  225. }
  226. if self.playerNode.position.y < -self.frame.size.height / 2 + self.playerNode.size.height {
  227. self.playerNode.position.y = -self.frame.size.height / 2 + self.playerNode.size.height
  228. }
  229. }
  230. //MARK:- 发生碰撞
  231. func didBegin(_ contact: SKPhysicsContact) {
  232. let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
  233. switch contactMask {
  234. // 子弹vs外星人
  235. case PhysicsCategory.Alien | PhysicsCategory.BulletBlue:
  236. bulletHitAlien(nodeA: contact.bodyA.node as! SKSpriteNode,nodeB: contact.bodyB.node as! SKSpriteNode)
  237. // 外星人Alien撞击到飞船
  238. case PhysicsCategory.Alien | PhysicsCategory.SpaceShip:
  239. alienHitSpaceShip(nodeA: contact.bodyA.node as! SKSpriteNode, nodeB: contact.bodyB.node as! SKSpriteNode)
  240. default:
  241. break
  242. }
  243. }
  244. // MARK: 子弹vs外星人
  245. func bulletHitAlien(nodeA:SKSpriteNode,nodeB:SKSpriteNode){
  246. // 击中粒子效果 Particle
  247. let explosion = SKEmitterNode(fileNamed: "ExplosionBlue")!
  248. explosion.position = nodeA.position // 或者 nodeB.position
  249. self.addChild(explosion)
  250. explosion.run(SKAction.sequence([
  251. SKAction.wait(forDuration: 0.3),
  252. SKAction.run {
  253. explosion.removeFromParent()
  254. }]))
  255. // 击中的音乐
  256. let actionColision = SKAction.playSoundFileNamed("explosion.mp3", waitForCompletion: false)
  257. run(actionColision)
  258. // 分数统计
  259. cScore += 1
  260. currentScore.text = "SCORE:\(cScore)"
  261. // 保存当前分数
  262. UserDefaults.standard.set(cScore, forKey: "CURRENTSCORE")
  263. if cScore > hScore {
  264. hScore = cScore
  265. highScore.text = "High:\(hScore)"
  266. // 保存最高分数
  267. UserDefaults.standard.set(cScore, forKey: "HIGHSCORE")
  268. }
  269. // 判断哪个是子弹节点bulletNode,碰撞didBegin没有比较大小时,则会相互切换,也就是A和B互相切换;
  270. if nodeA.physicsBody?.categoryBitMask == PhysicsCategory.BulletBlue {
  271. nodeA.removeAllChildren() // 移除所有子效果 emitter
  272. nodeA.isHidden = true // 子弹隐藏
  273. nodeA.physicsBody?.categoryBitMask = 0 // 设置子弹不会再发生碰撞
  274. nodeB.removeFromParent() // 移除外星人
  275. }else if nodeB.physicsBody?.categoryBitMask == PhysicsCategory.BulletBlue {
  276. nodeA.removeFromParent() // 移除外星人
  277. nodeB.removeAllChildren()
  278. nodeB.isHidden = true
  279. nodeB.physicsBody?.categoryBitMask = 0
  280. }
  281. }
  282. // MARK: 外星人Alien撞击到飞船
  283. func alienHitSpaceShip(nodeA:SKSpriteNode,nodeB:SKSpriteNode){
  284. if (nodeA.physicsBody?.categoryBitMask == PhysicsCategory.Alien || nodeB.physicsBody?.categoryBitMask == PhysicsCategory.Alien) && (nodeA.physicsBody?.categoryBitMask == PhysicsCategory.SpaceShip || nodeB.physicsBody?.categoryBitMask == PhysicsCategory.SpaceShip) {
  285. // print("撞击到飞船")
  286. // 击中粒子效果 Particle
  287. let explosion = SKEmitterNode(fileNamed: "Explosion")!
  288. explosion.position = nodeA.position
  289. self.addChild(explosion)
  290. nodeA.removeFromParent()
  291. nodeB.removeFromParent()
  292. // 播放失败音乐 + 切换到结束游戏
  293. let loseMusicAction = SKAction.playSoundFileNamed("", waitForCompletion: false)
  294. self.run(SKAction.sequence([
  295. loseMusicAction,
  296. SKAction.wait(forDuration: TimeInterval(0.7)),
  297. SKAction.run {
  298. // 切换游戏结束场景
  299. let reveal = SKTransition.doorsOpenHorizontal(withDuration: TimeInterval(0.5))
  300. let loseScene = LoseScene(fileNamed: "LoseScene")
  301. loseScene?.size = self.size
  302. loseScene?.scaleMode = .aspectFill
  303. self.view?.presentScene(loseScene!, transition: reveal)
  304. }]))
  305. }
  306. }
  307. }

iFIERO 游戏源码传送门:https://github.com/apiapia/SpaceBattleSpriteKitGame

更多教程:http://www.iFiero.com



声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/611715
推荐阅读
相关标签
  

闽ICP备14008679号