奄奄一息的山姆躺在地上,脑海中浮现了人生跑马灯。
「我为什麽会在这里?我的梦想,终究只是梦想吧...。」
『要嘛等死,要嘛写歌』出自《反正我很闲》的一段话冒了出来。
「对!这是发生山难的生存法则,我怎麽给忘了呢!」山姆跳了起来。
「写歌吗?找我就对了」一个名为伊索雷托的森林小精灵望着山姆,手里抱着一把三味线。
PS. 这里是开发 iOS 手机游戏的系列文,如果还没看过之前
剧情文章的朋友,欢迎先点这边回顾唷!
我想一个游戏的灵魂,最重要的就在於背景音乐跟音效了。虽然老姐我可以自己画图跟写程序码,但是音乐创作还是很讲求天份的!这个就交给专业的老弟来吧!
风格:8-bit 复古游戏风格的音乐
音乐:
音效:
请直接将所有 mp3 档案拖移到专案左侧的档案导览器中,会跳出以下的讯息,请点击 Finish
请先在游戏场景中 import AVFoundation,它是一个框架,可以用来处理视听媒体
import AVFoundation
在游戏场景中新增 4 个播放器,可以将它想像成四个音轨,分别播放背景音乐、主角与收集物碰触的音效、主角与怪物碰触的音效、点击方向键的音效,彼此间可以互相叠加。
分别新增四个播放器播音乐的方法,让呼叫方法的时候可以带入档名 musicName
,其中音乐类型的再加上是否循环播放的参数 loop
,并且设定预设为 false
方法内容:
Bundle.main.url
取得指定的档案位置,并新增一个播放器来播放音乐档,使用 play()
可以开始播放音乐numberOfLoops
可以设定重复播放的次数,预设为 0
,音乐只会播一次,不会重复。设定 -1
则会不断循环播放,直到呼叫 stop()
才会停止class GameScene: SKScene {
...
var musicPlayer: AVAudioPlayer?
var soundCollectionPlayer: AVAudioPlayer?
var soundWeatherPlayer: AVAudioPlayer?
var soundClickPlayer: AVAudioPlayer?
func playMusicByName(musicName: String, loop: Bool = false) {
guard let url = Bundle.main.url(forResource: musicName, withExtension: "mp3") else {
return
}
do {
self.musicPlayer = try AVAudioPlayer(contentsOf: url)
self.musicPlayer!.play()
if loop {
self.musicPlayer?.numberOfLoops = -1
}
}
catch {
print(error)
}
}
func playCollectionSoundByName(soundName: String) {
guard let url = Bundle.main.url(forResource: soundName, withExtension: "mp3") else {
return
}
do {
self.soundCollectionPlayer = try AVAudioPlayer(contentsOf: url)
self.soundCollectionPlayer!.play()
}
catch {
print(error)
}
}
func playWeatherSoundByName(soundName: String) {
guard let url = Bundle.main.url(forResource: soundName, withExtension: "mp3") else {
return
}
do {
self.soundWeatherPlayer = try AVAudioPlayer(contentsOf: url)
self.soundWeatherPlayer!.play()
}
catch {
print(error)
}
}
func playClickSound() {
guard let url = Bundle.main.url(forResource: "ClickSound", withExtension: "mp3") else {
return
}
do {
self.soundClickPlayer = try AVAudioPlayer(contentsOf: url)
self.soundClickPlayer!.play()
}
catch {
print(error)
}
}
}
StartMusic
playMusicByName
方法,musicName
带入音乐档名false
class GameScene: SKScene {
...
func gameStart() {
...
self.playMusicByName(musicName: "StartMusic")
}
}
BaseMusic
playMusicByName
方法,musicName
带入音乐档名loop
带入 true
playMusicByName
方法播放背景音乐1class GameScene: SKScene {
...
@objc func gameStartAction() {
...
self.playMusicByName(musicName: "BaseMusic", loop: true)
}
}
class GameScene: SKScene {
...
// 逃跑->攻击
@objc func eacapeToAttackModeAction() {
...
self.playMusicByName(musicName: "BaseMusic", loop: true)
}
}
FastMusic
playMusicByName
方法,musicName
带入音乐档名loop
带入 true
class GameScene: SKScene {
...
override func update(_ currentTime: TimeInterval) {
...
for magicCrystal in self.magicCrystals where !magicCrystal.isGotten && magicCrystal.gridX == sam.gridX && magicCrystal.gridY == sam.gridY {
...
self.playMusicByName(musicName: "FastMusic", loop: true)
}
}
}
GameOverMusic
playMusicGameOver
方法,找到音乐档案,并且播放playMusicGameOver
方法self.musicGameOver?.stop()
import AVFoundation
class GameOverScene: SKScene {
...
var musicGameOver: AVAudioPlayer?
override func didMove(to view: SKView) {
...
self.playMusicGameOver()
}
func playMusicGameOver() {
if let url = Bundle.main.url(forResource: "GameOverMusic", withExtension: "mp3") {
self.musicGameOver = try? AVAudioPlayer(contentsOf: url)
self.musicGameOver?.play()
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches) {
let location = touch.location(in: self)
if self.atPoint(location) == self.restartNode || self.atPoint(location) == self.labelRestart {
if let gameScene = GameScene(fileNamed: "GameScene") {
...
self.musicGameOver?.stop()
}
}
}
}
}
FallSound
stop()
将背景音乐关闭playWeatherSoundByName
方法,soundName
带入音效档名class GameScene: SKScene {
...
func gameStop() {
...
self.musicPlayer?.stop()
self.playWeatherSoundByName(soundName: "FallSound")
}
}
HitSound
playWeatherSoundByName
方法,soundName
带入音效档名class GameScene: SKScene {
...
override func update(_ currentTime: TimeInterval) {
...
for weather in self.weathers where (weather.gridX == sam.gridX && abs(weather.node.position.y - sam.node.position.y) <= CGFloat(self.gridWH + 6) || weather.gridY == sam.gridY && abs(weather.node.position.x - sam.node.position.x) <= CGFloat(self.gridWH + 6)) && (gridMapping.purpleTree.x != sam.gridX && gridMapping.purpleTree.y != sam.gridY)
{
if weather.mode == .ATTACK || weather.mode == .PLAY {
...
} else if weather.mode == .ESCAPE {
...
self.playWeatherSoundByName(soundName: "HitSound")
}
}
}
}
GotSound
playCollectionSoundByName
方法,soundName
带入音效档名class GameScene: SKScene {
...
override func update(_ currentTime: TimeInterval) {
...
for crystal in self.crystals where !crystal.isGotten && crystal.gridX == sam.gridX && crystal.gridY == sam.gridY {
...
self.playCollectionSoundByName(soundName: "GotSound")
}
for magicCrystal in self.magicCrystals where !magicCrystal.isGotten && magicCrystal.gridX == sam.gridX && magicCrystal.gridY == sam.gridY {
...
self.playCollectionSoundByName(soundName: "GotSound")
}
}
}
EatSound
playCollectionSoundByName
方法,soundName
带入音效档名class GameScene: SKScene {
...
override func update(_ currentTime: TimeInterval) {
...
for mushroom in self.mushrooms where !mushroom.isGotten && mushroom.gridX == sam.gridX && mushroom.gridY == sam.gridY {
...
self.playCollectionSoundByName(soundName: "EatSound")
}
}
}
ClickSound
playClickSound
方法class GameScene: SKScene {
...
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
...
for touch in (touches) {
let location = touch.location(in: self)
if self.atPoint(location) == btnLeft {
...
self.playClickSound()
}
if self.atPoint(location) == btnRight {
...
self.playClickSound()
}
if self.atPoint(location) == btnUp {
...
self.playClickSound()
}
if self.atPoint(location) == btnDown {
...
self.playClickSound()
}
}
}
}
目前游戏已经套用音乐及音效了,还差一个游戏破关音乐还没套用。
明日会带大家实作游戏破关时的动作,到时候再将音乐也套用上去吧!
参考来源:
AVFoundation
AVAudioPlayer
<<: DAY 23 『 客制化按钮 Custom Button 』
>>: Day20 Let's ODOO: Scheduled Actions
我们已将资料集上传到 nilvana 的 Vision Studio 中, 也知道标注格式的种类与基...
With data collection, ‘the sooner the better’ is ...
闭包 内部函数总是可以访问其所在的外部函数中声明的参数和变数,即使外部函式已经结束执行了。 看看这个...
杂记 这是情绪十分起伏的一周,首先花了大约一天的时间在考试及检讨,经过上周密集的练习在包好多层的...
Android tv 上一篇我们使用了browsefragment,再来就把资料带到里面 首先先把J...