I am making a game for fun and I want to make my sound effect loop every time it ends, these are my codes so far.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
let touchLocation = touch.location(in: self)
if atPoint(touchLocation).name == "startGame"{
let gameScene = SKScene(fileNamed: "GameScene")!
gameScene.scaleMode = .aspectFill
view?.presentScene(gameScene, transition: SKTransition.doorsOpenHorizontal(withDuration: TimeInterval(2)))
self.run(SKAction.playSoundFileNamed("mainmenu.wav", waitForCompletion: false))
You can't play background music over the scenes with using SKActions. Use AVAudioPlayer instead:
if let filePath = Bundle.main.path(forResource: "mainmenu", ofType: "wav") {
let url = URL(fileURLWithPath: filePath)
do {
let player = try AVAudioPlayer(contentsOf: url)
player.numberOfLoops = -1
player.play()
} catch {
// catch error if necessary
}
}
original post:
https://stackoverflow.com/a/23268462/6846532
In gamescene, or any other scene you want the music to loop in use this
let music = SKAudioNode(fileNamed: "backgroundmusic.mp3")
music.autoplayLooped = true
addChild(music)
Personally I would go with AVAudioPlayer or SKAudioNode because playSoundFileNamed turned being buggy in many SpriteKit releases, but I will show you how you can do it with actions:
import SpriteKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
run(SKAction.repeatForever(SKAction.playSoundFileNamed("sound", waitForCompletion: true)))
}
}
Related
I'm trying to make a game and in the main menu, there is a label to start music, and another to stop music.
let backgroundMusic = SKAction.playSoundFileNamed("background.mp3", waitForCompletion: false)
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
let touchedNode = atPoint(location)
if touchedNode.name == self.BEGIN_MUSIC {
// start music
self.run(backgroundMusic)
}
if touchedNode.name == self.STOP_MUSIC {
// stop music
SKAction.stop()
}
}
}
the music starts fine and works on all scenes which is what i wanted. But when I press the stop music, the music keeps playing. How do I stop the music.
I've tried SKAction.pause(), i've tried using withKey: "music" and then self.removeAction(forKey: "music").
I also tried making backgroundMusic: SKAudioNode and this worked but once i stopped the music i couldn't turn it back on, also it doesn't work on any other scene just the main menu.
How do I stop the sound?
I've had the same issue a while ago: This is how I got it working. This is specific for background music.
var backgroundMusic: AVAudioPlayer!
func playMusic() {
if let musicURL = Bundle.main.url(forResource: "musicSource", withExtension: "wav") {
if let audioPlayer = try? AVAudioPlayer(contentsOf: musicURL) {
backgroundMusic = audioPlayer
backgroundMusic.numberOfLoops = -1
backgroundMusic.play()
backgroundMusic.volume = 0.2
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
if let touch = touches.first {
let pos = touch.location(in: self)
let node = self.atPoint(pos)
let musicOff = UserDefaults.standard.bool(forKey: "musicOff")
if node == musicButton && musicOff == false {
backgroundMusic.stop()
UserDefaults.standard.set(true, forKey: "musicOff")
}
if node == musicButton && musicOff == true {
backgroundMusic.play()
backgroundMusic.volume = 0.3
UserDefaults.standard.set(false, forKey: "musicOff")
}
}
}
So I have a background music looping in the background in my GameViewController. The pause music button is available in the GameScene where a user can mute or unmute the game music.
I have two global variables:
var muteButton = SKSpriteNode(imageNamed: "pause")
var mute: Bool = false
Inside my GameScene I've added, things work like they are suppose to (the print responses are triggered).
class GameScene: SKScene{
override func didMove(to view: SKView){
...
muteButton.position = CGPoint(x: self.size.width*0.2, y: self.size.height*0.90)
muteButton.name = "Mute Button"
muteButton.zPosition = 10
self.addChild(muteButton)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches{
let pointOfTouch = touch.location(in: self)
let nodeITapped = atPoint(pointOfTouch)
if nodeITapped.name == "Mute Button"{
if mute == false {
print("music will now turn OFF")
mute = true
}
else{
print("music will now turn ON")
mute = false
}
}
}
}
}
I suspect the mute variable is only being called once in the GameViewController viewDidLoad, and thus the if statement is being checked only once. Since I have multiple senses connected that all need to have music playing, the best place for me to put the backgroundAudio would be here.
In my GameViewController:
class GameViewController: UIViewController{
var backgroundAudio = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
// Background Audio plays throughout the game
let filePath = Bundle.main.path(forResource: "Track1",ofType:"mp3")
let audioNS_URL = NSURL(fileURLWithPath: filePath!)
if mute == false{
do{ backgroundAudio = try AVAudioPlayer(contentsOf: audioNS_URL as URL)}
catch { return print("No Audio Found")}
// audio will loop forever
backgroundAudio.numberOfLoops = -1
backgroundAudio.play()
}
else{
backgroundAudio.pause()
}
}
}
Add an observer inside GameViewController:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(switchBackgroundAudio), name: NSNotification.Name.init("switchBackgroundAudio"), object: nil)
//... other stuff here ...//
}
then add a function for switching the sound on/off:
#objc func switchBackgroundAudio() {
if mute == false {
backgroundAudio.play()
} else {
backgroundAudio.pause()
}
}
finally whenever inside your GameScene you touch the button, you may launch an event:
if nodeITapped.name == "Mute Button"{
//... stuff here ...//
NotificationCenter.default.post(Notification(name: NSNotification.Name("switchBackgroundAudio")))
}
So I have been trying to get this audio file to play through all scenes of the game but it cuts off after the MenuScene. I call the MenuScene from GameViewController
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'MenuScene.sks'
if let scene = SKScene(fileNamed: "MenuScene") {
// Set the scale mode to scale to fit the window
Then in the MenuScene I start the music by doing this.
class MenuScene: SKScene {
var starfield:SKEmitterNode!
var newGameButtonNode:SKSpriteNode!
var difficultyButtonNode:SKSpriteNode!
var difficultyLabelNode:SKLabelNode!
var backgroundMusic:SKAudioNode!
override func didMove(to view: SKView) {
let backgroundMusic = SKAudioNode(fileNamed: "Snap.mp3")
backgroundMusic.autoplayLooped = true
addChild(backgroundMusic)
The music starts playing. Then I have in the touchesBegan () if the GameScene button is touched it will load that scene. I made the transition really long to find out when the music stops and it plays through the entire transition but when the GameScene is loaded it stops.
if nodesArray.first?.name == "newGameButton" {
let transition = SKTransition.fade(withDuration: 5.0)
run(SKAction.wait(forDuration: 5.0), completion: {
self.backgroundMusic = SKAudioNode(fileNamed: "Snap.mp3")
self.backgroundMusic.autoplayLooped = true
self.addChild(self.backgroundMusic)
})
let gameScene = GameScene(size: self.size)
self.view!.presentScene(gameScene, transition: transition)
I want it to continue playing throughout the game and on the GameOver scene and loop over forever.
Make sure your in main SKScene and
You can do something like this:
self.runAction(SKAction.playSoundFileNamed("YourFileName.extension", waitForCompletion: false))
Repeat Music:
SKAction.repeatForever(SKAction.playSoundFileNamed("YourFileName.extension", waitForCompletion: true))
Your music will keep on playing..
With AVFoundation
One of my code I have done.
import AVFoundation
var backgroundMusicPlayer: AVAudioPlayer!
func playBackgroundMusic(filename: String) {
let url = NSBundle.mainBundle().URLForResource(
filename, withExtension: nil)
if (url == nil) {
print("Could not find file: \(filename)")
return
}
var error: NSError? = nil
backgroundMusicPlayer =
AVAudioPlayer(contentsOfURL: url, error: &error)
if backgroundMusicPlayer == nil {
print("Could not create audio player: \(error!)")
return
}
backgroundMusicPlayer.numberOfLoops = -1
backgroundMusicPlayer.prepareToPlay()
backgroundMusicPlayer.play()
}
import SpriteKit
Thanks for replying.
I am trying to make a program when I press a button once, two random sounds will play. I can get the random sounds to play if I press the button, but I am looking that if I press the button, the random sound will play differently each time.
I can paste the sounds together to hear them in the sequence I want them, but I would like swift to generate the sounds.
I thought of the AVqueplayer to make it as a playlist. I was thinking this can be like a pair of dice in an analogy. For example, if I were to throw the dice down, the random sounds will occur.
I am still a newbie, and tried to figure this out on my own, because it seemed so simple, but I am out of options now.
Here is what I got so far. This will play a random sound when I press the button each time.
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player: AVAudioPlayer = AVAudioPlayer()
var sounds = ["sound1", "sound2", "sound3"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
if event!.subtype == UIEventSubtype.motionShake {
let randomNumber = Int(arc4random_uniform(UInt32(sounds.count)))
let fileLocation = Bundle.main.path(forResource: sounds[randomNumber], ofType: "mp3")
var error: NSError? = nil
do { try player = AVAudioPlayer(contentsOf: URL(fileURLWithPath: fileLocation!))
player.play()
} catch {}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Using the same code, and adding a second random number with matching file location will allow the sounds to play back to back, both being random:
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player: AVAudioPlayer = AVAudioPlayer()
var sounds = ["sound1", "sound2", "sound3"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
if event!.subtype == UIEventSubtype.motionShake {
let randomNumber1 = Int(arc4random_uniform(UInt32(sounds.count)))
let randomNumber2 = Int(arc4random_uniform(UInt32(sounds.count)))
let fileLocation1 = Bundle.main.path(forResource: sounds[randomNumber1], ofType: "mp3")
let fileLocation2 = Bundle.main.path(forResource: sounds[randomNumber2], ofType: "mp3")
//var error: NSError? = nil
do {
try player = AVAudioPlayer(contentsOf: URL(fileURLWithPath: fileLocation1!))
player.play()
try player = AVAudioPlayer(contentsOf: URL(fileURLWithPath: fileLocation2!))
player.play()
} catch {}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I have background music which starts when the app is launched in GameViewController.swift using the following code:
class GameViewController: UIViewController {
// VARIABLES
var backgroundMusicPlayer : AVAudioPlayer!
// AUDIO PLAYER
func playBackgroundMusic(filename: String) {
let url = NSBundle.mainBundle().URLForResource(filename, withExtension: nil)
var error : NSError? = nil
do {
backgroundMusicPlayer = try AVAudioPlayer(contentsOfURL: url!)
} catch let error1 as NSError {
error = error1
backgroundMusicPlayer = nil
}
if backgroundMusicPlayer == nil {
print("Could not create audio player: \(error!)")
return
}
backgroundMusicPlayer.numberOfLoops = -1
backgroundMusicPlayer.prepareToPlay()
backgroundMusicPlayer.play()
}
func stopBackgroundMusic() {
backgroundMusicPlayer.stop()
}
override func viewDidLoad() {
super.viewDidLoad()
playBackgroundMusic("MainTheme.mp3")
<< Various irrelevant code >>
}
Because this is run in the viewController, it persists through changing scenes on the menu (i.e. opening the "shop" scene) and creates a seamless track. When I click the "Play" button on the menu scene I want the music to then stop, and transition to the game. I have the stopBackgroundMusic() method in the GameViewController but I don't know how to call it on on the menu scene. IN THE MENU SCENE I tried this:
// TOUCH
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first as UITouch?
let touchLocation = touch!.locationInNode(self)
let touchedNode = self.nodeAtPoint(touchLocation)
if touchedNode.name == "startGame" {
GameViewController.stopBackgroundMusic()
let transitionType = SKTransition.fadeWithDuration(2)
let viewSize = self.view?.bounds.size
let scene = GameScene(size: viewSize!)
self.view?.presentScene(scene, transition: transitionType)
}
}
But I get an error saying I'm missing parameter #1 in call for stopBackgroundMusic() which shouldn't require any parameters. Am I calling this method wrong? Thanks!
You are referring to your class by using GameViewController but your function is at the object instance level.
If you declare the variable and function at the class level, your code in the touchesBegan function should work fine.
static var backgroundMusicPlayer : AVAudioPlayer!
class func playBackgroundMusic(filename: String) ...
class func stopBackgroundMusic()
override func viewDidLoad() {
super.viewDidLoad()
GameViewController.playBackgroundMusic("MainTheme.mp3")
<< Various irrelevant code >>
}