Music Stops playing after scene change SKAudioNode Xcode 8 - ios

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

Related

audioPlayerDidFinishPlaying func issues

I am trying to make my app so that when a user touches a UIImageView A certain sound will play. However, while that sound is playing, I want the UIImageView.isUserInteractionEnabled to be assigned to false. Once the sound is done playing I want the UIImageView.isUserInteractionEnabled to be assigned to true. When ever I run the following code I get an error in the card class, even if I force unwrap. Below is the class that contains the image view I want to disable.
class SecondViewController: UIViewController , UIGestureRecognizerDelegate {
#IBOutlet weak var imgPhoto: UIImageView!
func imageTapped(tapGestureRecognizer: UITapGestureRecognizer)
{
imgPhoto.isUserInteractionEnabled = false
itemList[imageIndex].playSound()
}
}
This is the class where the playSound func is located.
import Foundation; import UIKit; import AVFoundation
class Card: NSObject
{
var player: AVAudioPlayer?
var svc = SecondViewController()
var image: UIImage
var soundUrl: String
init(image: UIImage, soundUrl: String, isActive:Bool = true) {
self.image = image
self.soundUrl = soundUrl
}
func playSound()
{
guard let url = Bundle.main.url(forResource: self.soundUrl, withExtension: "m4a") else { return }
do
{
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
player = try AVAudioPlayer(contentsOf: url)
player?.delegate = self
guard let player = player else { return }
player.prepareToPlay()
player.play()
audioPlayerDidFinishPlaying(player)
print("play")
} catch let error {
print(error.localizedDescription)
}
}
}
extension Card: AVAudioPlayerDelegate {
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer){
svc.imgPhoto.isUserInteractionEnabled = true
}
}
this is the error I get
The problem is this line:
var svc = SecondViewController()
That svc is not your SecondViewController, the existing one in the interface with an imgPhoto. Instead, you are creating a new, separate, and above all blank view controller with an empty view, so its imgPhoto is nil and you crash when you refer to it.
What you want to do is use the "delegate" pattern to hold a reference to the real SecondViewController so you can talk to it.

How to pause background music in game using AVAudioPlayer for multiple scenes

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")))
}

What is the code for my sound effect to loop

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)))
}
}

Become nil Referring to YTPlayer from a different view

I want to control the video player of the video view from the screen transition destination.
I want to play a video by tapping the cell
Try to
// Select(Tap) to cell
override func tableView(_ table: UITableView,didSelectRowAt indexPath: IndexPath) {
let video = ViewController()
video.playerView.playVideo()
}
But fatal error: unexpectedly found nil while unwrapping an Optional value
Append:
I tried to use YTPlayer from https://github.com/youtube/youtube-ios-player-helper
But I did not understand how to write in your code "YTPlayer".
YTPlayer doesn't have YTPlayer(exist YTPlayerView) and YTPlayerLayer.
My viewcontoller's code here
import UIKit
import youtube_ios_player_helper
import AVFoundation
class ViewController: UIViewController, YTPlayerViewDelegate{
#IBOutlet var playerView: YTPlayerView!
var appDelegate:AppDelegate = UIApplication.shared.delegate as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
self.playerView.delegate = self
self.playerView.sizeToFit()
let vars = ["playsinline": 1, "controls": 2, "showinfo": 0, "origin":"https://www.google.com"] as [String : Any]
if self.appDelegate.selectMV == ""
{
print("Not Load",self.appDelegate.selectMV)
}
else
{
print("Load",self.appDelegate.selectMV)
playerView.load(withVideoId: self.appDelegate.selectMV, playerVars: vars)
}
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSessionCategoryPlayback)
}
catch {
print("")
}
}
func playerViewDidBecomeReady(_ playerView: YTPlayerView) {
playerView.playVideo()
}
func play() {
print("Push! play")
//Error playerView nil
playerView.load(withVideoId: self.appDelegate.selectMV)
playerView.playVideo()
}
}
It's quit simple.. You need to code in didselect as you've done. But This kinds of code will never works here. You have to code like this in ViewDidLoad() or ViewWillAppear() First.
self.player = AVPlayer(url: NSURL(fileURLWithPath: filePath) as URL)
self.playerLayer = AVPlayerLayer(player: self.player)
self.playerLayer.frame = self.vview.bounds
Then In didSelect Only code for simply
Player.play()
If you want to change playerItem There simply code in didSelect Like this..
player.replaceCurrentItemWithPlayerItem(AVPlayerItem(URL: NSURL(fileURLWithPath: filePath)))
It's for the single View. Try like this and if you need any help feel free to ask me.

Stopping the background music when starting a game

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 >>
}

Resources