奄奄一息的山姆躺在地上,腦海中浮現了人生跑馬燈。
「我為什麼會在這裡?我的夢想,終究只是夢想吧...。」
『要嘛等死,要嘛寫歌』出自《反正我很閒》的一段話冒了出來。
「對!這是發生山難的生存法則,我怎麼給忘了呢!」山姆跳了起來。
「寫歌嗎?找我就對了」一個名為伊索雷托的森林小精靈望著山姆,手裡抱著一把三味線。
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()
}
}
}
}
目前遊戲已經套用音樂及音效了,還差一個遊戲破關音樂還沒套用。
明日會帶大家實作遊戲破關時的動作,到時候再將音樂也套用上去吧!