I'm developing a tiny game and at first it works super perfect. It loads sks files and sound files pretty well. But after a few minutes of testing, I start to get "Error loading sound resource" and particle effects all disappear.
I thought it might be the memory problem, however, the game only takes about 120M, and I haven't received any memory warnings yet.
So how can it manages to read files in the beginning and fails to do so a few minutes later? Is there something tricky in SpriteKit that I don't know? Such as after loading one hundred files successfully, the game cannot read any files anymore until the user shakes the iPhone twice and turns it upside down... If it's true, I will possible inform the players to do so in the game's tutorial...
So can anyone help me handle this...? Lots of thanks...
// The way I load background music:
let musicPath=Bundle.main.path(forResource: "Main", ofType: "mp3")
let url = URL(fileURLWithPath: musicPath!)
do{
try main=AVAudioPlayer(contentsOf: url)
}catch{
return
}
// The way I load sound files:
let a = SKAction.playSoundFileNamed("Fire",waitForCompletion:false)
audioNode.run(a)
// The way I load SKS files:
let emitterPath = Bundle.main.path(forResource: "Fire", ofType: "sks")!
let emitter = NSKeyedUnarchiver.unarchiveObject(withFile: emitterPath) as! SKEmitterNode
self.addChild(emitter)
It's hard to say without seeing your code but if you're opening a lot of file readers you could run out of memory or make the files inaccessible until the other nodes reading from the files are gone.
Do you happen to know if you're creating new audio nodes every time they are rendered?
Apples documentation suggests that you create a single SKAction to be reused. So if you need to reuse your Fire action, I would create a singleton of it and run that single object over and over instead of allocating the new SKAction again and again.
Source
Related
I'm new to xCode/swift so please forgive my inexperience. I have a ViewController where my viewers can listen to some audio playback. The playback is accessed like this when the player clicks a play button:
#IBAction func buttonClicked(_ sender: RoundButton)
{
self.clickedButton = sender
guard let url = sender.url else {
return
}
let player = AVPlayer(url: url)
let controller = AVPlayerViewController()
controller.player = player
present(controller, animated: true) {
player.play()
}
}
I got this code from another StackOverflow question, so I don't completely understand it. My goal is to be able to save the URL and the last played time so that the user can minimize the app, or navigate to a different screen, and then be able to click a "continue listening" button which will pull up another AVPlayer with the last used URL. This "continue listening" AVPlayer will then seek to the last played time.
I know that I need to observe the first AVPlayer somehow, so that when it is paused, stopped, or put in the background, I save the currentTime to a NSUserDefault (I think?). I also need to save the URL, because there are many different URLs that the user could click on.
I tried doing this, and besides not being able to figure out the observation, I also couldn't figure out the type inconsistencies present with NSUserDefault. I tried to retrieve URL NSDefault value as a String after setting it, but when I went to cast the String to a URL using URL(string: lastPlayedURL), xCode complained about "Cannot convert type Data? to expected type String".
My issue with using other StackOverflow questions to solve my problem is that I don't understand where to put the code blocks. Where do I create the observer? Because xCode did not seem happy when I created it inside the body of "buttonClicked".
Thank you for listening to my rambling.
Yes, UserDefaults seem appropriate to store the URL in. Use this method to do that.
To observe the player you need to use an AVPlayerItem. Here's some code that shows that.
About your general issues with Xcode (note the capitalisation) and Swift language, I'm afraid these are things you need to work through yourself by reading/watching tutorials/documentation. Then when you find detailed issues, post your code here and ask.
Good luck and have fun!
I'm trying to assign a table view to an already existing UIView object on my storyboard but every time I assign it by specifying the correct frame I get an issue.
let sampleTable = AKTableView(AKTable(file: sampleReferences[indexPath.row].sampleFile!), frame: samplePlot.bounds)
samplePlot.addSubview(sampleTable)
This is the code I'm using at the moment. I've also used samplePlot.frame.
Basically the sampleReferences[indexPath.row].sampleFile! is an AKAudioFile stored earlier from a recording stored as a reference from a UITableView element. It's quite a large program split into multiple files so it's hard to show everything.
The only extra issue I can think of is I haven't stopped any of the current AudioKit processes. I'm not sure if the table can only be drawn at startup.
The following works with a .sine specifier AKTable(.sine) so it's not an issue with my UI code. It also fails when trying to load even a basic .mp3 file from .resources.
Update:
I've compressed the test .mp3 file significatly and shortened it's overall length to 2 seconds and I'm still getting the same issue. I think it might be an issue with the actual function used to draw the file. Although I could be wrong.
Another update:
So everything works fine using the following code
let file = EZAudioFile(url: sampleAudio!.url)
guard let data = file?.getWaveformData() else {
print("Failed to get waveform data")
return }
let waveform = EZAudioPlot()
waveform.frame = samplePlot.bounds
waveform.plotType = EZPlotType.buffer
waveform.shouldFill = true
waveform.shouldMirror = true
waveform.color = UIColor.black
waveform.updateBuffer(data.buffers[0], withBufferSize: data.bufferSize )
samplePlot.addSubview(waveform)
but obviously I'm using EZAudioPlot. I grabbed this from a previous stack overflow issue for the same thing and edited it a bit.
After loading two or more audio files 250 times, the app fails to load further files. This appears to happen regardless of the length of the audio files or the duration between consecutive plays.
Error:
SKAction: Error loading sound resource: "audio/1.mp3"
Code (GameScene.swift):
import GameplayKit
class GameScene: SKScene {
var count = 0
override func didMove(to view: SKView) {
infiniteLoop()
}
func infiniteLoop() {
let number = count % 3 + 1
let playLetter = SKAction.playSoundFileNamed("audio/\(number).mp3", waitForCompletion: true)
self.run(playLetter, completion: {
self.count += 1
print(self.count)
self.infiniteLoop()
})
}
}
Audio Files (example):
http://s000.tinyupload.com/index.php?file_id=87262242252758332431
Reproducing The Error:
The error appears to be reproducible by simply making a new iOS game in Xcode, copying the "audio" (assets) folder linked above into the root of the project, and copying the code above into the GameScene.swift file. After getting to "250" in the console the error will likely appear.
Analysis:
After cycling through two or more audio files 250 times, the app fails to load the audio resource. I encountered this problem while making a spelling game which cycles through images and audio files in a similar manner. The problem originally presented itself by failing to load both the image files and the audio files after the app being used for ~15 minutes. I've managed to strip the app down to this core code and still trigger the error regardless of whether I simulate on my computer, simulate on my iPad, or even export and install (Ad Hoc) on my iPad.
I'm new to Swift and am hoping there is something simple at play here. Please let me know if you have any suggestions on how to fix this error.
Turns out the answer is to use AVAudioPlayer instead of SKAction for playing the audio file if it is to be played a large number of times.
So I am following through an old tutorial and I think with the changes in xcode and swift this code is now no longer usable, but i am not sure. Would love some help.
Declaring constant for the audio effect
let cannonSound = SKAction.playSoundFileNamed("cannon.wav", waitForCompletion: false)
Calling the audio effect within my funtion
let hotdogSequence = SKAction.sequence([cannonSound, moveHotdog, deleteHotdog])
hotdog.run(hotdogSequence)
For more info I am using SpriteKit in Xcode and this code is contained within GameScene.swift file.
Update:
The error I receive is
2017-03-30 00:52:43.631 Ballpark Weiner[95999:1983181] SKAction: Error loading sound resource: "cannon.wav"
The game doesn't crash just no sound plays
This message usually means that the file cannot be found in your project or might be corrupt.
You should check that the file is actually copied into your project and is spelled correctly. Its case sensitive so if the actual file is called "Cannon.wav" it will not work.
Hope this helps
I'm using GameplayKit's GKObstacleGraph to add path finding support to my iOS 10.2 SpriteKit game.
The game is a top-down 2D game, it features impassable obstacles which my 'soldiers' can't walk through.
Creating a GKObstacleGraph for my game level, with about 500 obstacles, takes ~50 seconds. This is too long for my users to wait.
Since the game map layout never changes dynamically after being loaded, I decided to create the graph once:
let graph = GKObstacleGraph(obstacles: obstacles, bufferRadius: 5, nodeClass: GKGraphNode2D.self)
Archive it to file:
let directories = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
if let documents = directories.first {
if let filePath = String(documents + "/obstacle.graph") {
NSKeyedArchiver.archiveRootObject(graph, toFile: filePath)
}
}
Copy the file from device to my laptop and add the file to my bundle. Then I just unarchive the graph object when I load my game level:
if let filePath = Bundle.main.path(forResource: "obstacle", ofType: "graph") {
let graph = NSKeyedUnarchiver.unarchiveObject(withFile: filePath) as! GKObstacleGraph<GKGraphNode2D>
return graph
}
In theory, this should be much quicker since I don't have to calculate the graph, only read it(unarchive) from file.
However, depending on the number of obstacles and their relative placement one of three things happens:
NSKeyedArchiver.archiveRootObject crashes.
Archive works but NSKeyedUnarchiver.unarchiveObject crashes.
Both archive and unarchive works, but I can not find a path around obstacles with GKObstacleGraph.findPath
I can get all of this working if I skip the (un)archive steps:
Successful path finding
Also, on simulator (iPhone 7), (un)archive never crashes, but path finding always fails afterwards.
What is going on here? I've reported it as a bug to Apple, but I'm still hoping that I've missed something.
I tried alternative solutions to the problem where I'm writing/reading nodes and obstacles to file using my own format. But the obstacle property of GKObstacleGraph is get only, which leaves me with the constructor, and then I'm back to waiting 50 seconds again.
I've created a public test project git repo:
https://bitbucket.org/oixx/gkobstaclegraphcrashproof.git
which shows the three different scenarios that fails when running on device. For simplicity it reads/writes the file to device, not bundle.
I don't know anything about the crashes, but #3 in your list is probably caused by connectNodeToLowestCostNode:bidirectional: not actually connecting your soldier to the graph because the nodes array is immutable after unarchiving.
The solution is to archive the nodes instead of the graph, then just create a graph from the nodes
NSArray *nodes = [NSKeyedUnarchiver unarchiveObjectWithData:self.nodesArray];
GKGraph *graph = [[GKGraph alloc] initWithNodes:nodes];