SceneKit —从零到英雄,第6部分

我们现在已经有一款可以正常工作的游戏,只是缺少了一些到目前为止尚未涉及的部分。 因此,本部分教程的目标是立即添加它们并对其进行一些润饰。

缺少的部分

声音

声音是游戏中最重要的部分,也是最被低估的部分之一。 您将看到并感觉到第5部分与第6部分的不同之处仅在于我们现在将具有声音效果。 SceneKit使集成它们变得非常容易,并且还支持空间声音(3D声音)。 因此,我们仅向给定节点添加声音效果,SceneKit自行进行所有处理。 这样,添加声音就很容易了,我还可以通过创建GameSound类以一种简单的方式在游戏中做到这一点,我为在游戏中使用的每种声音实现了它自己的功能。 有了这个小小的抽象,我就可以承担一个责任,因此很容易更改,并且也很重要,编译器能够检查是否存在声音效果。 但是自己看:

import AudioToolbox.AudioServices 
 class GameSound { 
private static let explosion = SCNAudioSource(fileNamed: "sounds/explosion.wav")
private static let fire = SCNAudioSource(fileNamed: "sounds/fire.wav")
private static let bonus = SCNAudioSource(fileNamed: "sounds/bonus.wav")

static func vibrate() {
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
}
 private static func play(_ name: String, source: SCNAudioSource, node: SCNNode) { 
let _ = GameAudioPlayer(name: name, source: source, node: node)
}
 static func explosion(_ node: SCNNode) { 
guard explosion != nil else { return }
  GameSound.play("explosion", source: GameSound.explosion!, node: node) 
GameSound.vibrate()
}
 static func fire(_ node: SCNNode) { 
guard fire != nil else { return }
GameSound.play("fire", source: GameSound.fire!, node: node) GameSound.vibrate()
}
 static func bonus(_ node: SCNNode) { 
guard bonus != nil else { return }
GameSound.play("bonus", source: GameSound.bonus!, node: node)
}

这是将3D声音效果实现到基于SceneKit的游戏所需的全部。 不错,不是吗? 也许您已经注意到,我使用了一个名为GameAudioPlayer的类而不是SCNAudioPlayerGameAudioPlayerSCNAudioPlayer的子类, 实现了一件重要的事情:完成播放后自动删除声音。 除此之外,其余所有操作均由SceneKit本身完成。

 class GameAudioPlayer : SCNAudioPlayer { 
private var _node: SCNNode!
  init(name: String, source: SCNAudioSource, node: SCNNode) { 
super.init(source: source)
node.addAudioPlayer(self)
  _node = node 
rbDebug("GameAudioPlayer: play \(name)")
  self.didFinishPlayback = { 
rbDebug("GameAudioPlayer: stopped \(name)")
self._node.removeAudioPlayer(self)
}
}
}

现在先尝试游戏,看看有声音时感觉如何。 要添加新效果,只需添加一个新功能和一个音频文件,您就可以立即使用它。

先进的HUD技术

我想提到的第二件事是一个好点系统和在HUD中使用的高级动画。 每当我们在游戏中碰到某些东西(例如环飞过槽)或射击飞机时,我们都会得到分数。 也许您已经在其他游戏中看到这些点在动画中被抵消了。 我还已经在HUD中实现了该功能(请参阅HUD.swift ),并且您可以看到它看起来要好得多,然后仅显示要点。 这些东西(当然还包括出色的游戏性)使游戏与最终的出色游戏有所不同,我们将在本教程的第10部分中看到更多这些技巧

 private func changePoints(_ pointsToAdd: Int, total: Int, count: Int) { 
_totalPoints += pointsToAdd
points = _totalPoints
  let scaling: CGFloat = 1.5 
let action = SKAction.zoomWithNode(_points, amount: CGPoint.make(scaling, scaling), oscillations: 1, duration: 0.01)
_points.run(action)
  if count < total { 
Run.after(0.01, {
self.changePoints(1, total: total, count: count+1)
})
}
}

子弹和敌机

但是,这部分最大的改进可能是这样一个事实,即我们现在拥有敌机,而且我们以及他们也可以发射子弹! 此附加功能需要对当前代码进行一些重构(经常执行某些操作)。 我添加了一个新的Plane类,并将Player类作为它的子类。 新的敌人职业也是如此。 然后,我将代码(对于Player飞机和敌人飞机)都相同的代码移到了plane类,并在其中加入了Player飞机特有的内容。 在GameLevel类( addEnemies )中,将创建飞机并向我们飞行。

但是我们不仅希望他们飞行,我们还希望我们能够发射子弹,而敌机也可以对付我们。

子弹班

您现在应该知道实现这种技术所需的所有技术。 是的,它也将是GameObject的子类,并且还使用碰撞检测来检查我们是否击中了物体或子弹击中了我们; Player对象。 我们也可以将所有创建的类添加到_gameObjects列表中。 但是我将使用项目符号来展示游戏开发技术的另一个方面:**重用*。 因为我们创建了很多项目符号,所以当状态不再* .alive时,再次使用此SceneKit节点是有意义的 。 为此,我们创建一个数组,其中包含我们在GameLevel发射的所有子弹:

 func fireBullet(enemy: Bool, position: SCNVector3, sideDistance: CGFloat = 0, fallDistance: CGFloat = 0) -> Bullet { 
让方向:PlaneDirection =敌人? .down:.up
  // Search in list if we have an unused bullet 
for bullet in _bullets {
if (bullet.state == .died && bullet.enemy == enemy) {
// Ok, use this
bullet.isHidden = false
bullet.state = .initialized
bullet.position = position
  bullet.fire(direction: direction, sideDistance: sideDistance, fallDistance: fallDistance, speed: 3.0) 
rbDebug("Reuse bullet \(bullet)")
  return bullet 
}
}
  // No bullets free, so create one 
let bullet = Bullet(enemy: enemy)
bullet.position = position
_bullets.append(bullet)
  self.rootNode.addChildNode(bullet) 
  bullet.fire(direction: direction, sideDistance: sideDistance, fallDistance: fallDistance, speed: 3.0) 
  return bullet 
}

这样,我们可以节省很多子弹,并且可以通过在给定时间后将其从内存中删除来进一步改善。 练习:尝试完全实现!

我们从这里去哪里?

游戏从未真正完成过,您可以随时改进或添加新功能。 但请记住:

游戏开发(和一般软件开发)中最重要的是发布

因此,首先要专注于游戏的主要(关键)游戏功能。 完成并使其有趣。 然后遍历您想让用户看到(甚至更好)的所有功能!

通常,您仍然可以学到很多有关游戏开发的知识,但是从第1到第6部分的所有内容中,您都有良好的基础开始自己的游戏。

接下来的三章将介绍如何在不同的(Apple)平台上使用SceneKit,以及在ARKit中使用增强现实。 作为奖励章节,第10章将介绍一些高级SceneKit功能,以进一步完善您的游戏。

教程章节:

–第1部分–建立地形
–第2部分–创建一个真实的玩家游戏对象
–第3部分–为您的地形增添生命
–第4部分–实施游戏循环
–第5部分–使用CoreMotion平稳地飞行
–第六部分–完成游戏(缺少的部分)
–第7部分– ARKit:在您的环境中玩
–第8部分–多平台:我们在macOS上的游戏
–第9部分– tvOS:出色的游戏平台
–第10部分-高级SceneKit(有待改进)

别忘了……再次链接到源代码