I have created a HomeScene.sks and a HomeScene.swift. In the GameViewController I've changed the default "GameScene" to "HomeScene" like this"
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'HomeScene.sks'
if let scene = SKScene(fileNamed: "HomeScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
When I run it in the Simulator it shows the LaunchScreen but than it stops. In my xcode output screen I see:
2017-05-22 21:02:07.910 DiceWar[88707:2060079] -[DiceWar.HomeScene setNormalTexture:]: unrecognized selector sent to instance 0x7fdba6008af0
2017-05-22 21:02:07.915 DiceWar[88707:2060079] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[DiceWar.HomeScene setNormalTexture:]: unrecognized selector sent to instance 0x7fdba6008af0'
I Googled around but can't find anything useful. What are these lines telling me? Did I use a bad texture? I also tried to message out all the code in HomeScene.swift but the error still occurs.
I'm not sure if it's because English isn't my native language or if it's my logic but I really don't understand what xcode is trying to tell me.
Edit
I think I found it. Because there was something with a texture. I deleted a Color Sprite from the HomeScene.sks and now it does start. When I replaced the Color Sprite there wasn't a problem until I used this line:
settingsButton = self.childNode(withName: "settingsButton") as! SKSpriteNode
The strange thing is, I used the exact some line for another image (instead of settingsButton I used playButton) The playButton line doesn't seem to be a problem.
The error message is saying that your application logic is trying to call setNormalTexture(_:) on your class DiceWar.HomeScene, but that class does not know how to handle that method call.
From the documentation:
This action can only be executed by an SKSpriteNode object.
Related
I'm creating an app that users AVFoundation to record a video. I have a Login View Contoller which leads to a View Controller (user can see their profile details here) which leads to a Data Collection View Controller (this is where the video camera is presented). All works fine but when I click on a back button to go back to the View Controller, and then click 'start data collection' to go back into the video camera a 2nd time, the app crashes.
Crash info:
2018-08-11 11:39:51.861569+0100 LiopaDatacapture-iOS[6343:1642808] *
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '* -[AVCaptureMetadataOutput
setMetadataObjectTypes:] Unsupported type found - use
-availableMetadataObjectTypes'
*** First throw call stack: (0x1842e6d8c 0x1834a05ec 0x189e54b44 0x10128f4fc 0x1012989a4 0x101298ca4 0x18df01e64 0x18df01a50
0x18eaa4fd8 0x18e12b398 0x18e12a25c 0x18e3a33a0 0x18e0e13e4
0x18e1297bc 0x18e129654 0x18e3a6350 0x18e734d24 0x18e881af4
0x18e8819a0 0x18e39a49c 0x101297f5c 0x18e05564c 0x18e176870
0x18e05b700 0x18e1911a8 0x18e0d89e0 0x18e0cd890 0x18e0cc1d0
0x18e8add1c 0x18e8b02c8 0x18e8a9368 0x18428f404 0x18428ec2c
0x18428c79c 0x1841acda8 0x186192020 0x18e1cc758 0x101284a90
0x183c3dfc0) libc++abi.dylib: terminating with uncaught exception of
type NSException (lldb)
I'm using AVCaptureMetadataOutput to detect faces. This code is in my SessionHandler class where the video camera is being set up.
// define metadata
let metaOutput = AVCaptureMetadataOutput()
if cameraSession.canAddOutput(metaOutput) {
metaOutput.setMetadataObjectsDelegate(self, queue: faceQueue)
cameraSession.addOutput(metaOutput)
print("metaoutput added")
}
// set metadata to look for faces
metaOutput.metadataObjectTypes = [AVMetadataObject.ObjectType.face]
In my DataCaptureViewController.swift file this is where I load the video camera-
override func viewDidLoad() {
super.viewDidLoad()
sessionHandler.setupCamera()
audioRecorder.setUpAudioSession()
createObservers()
let layer = sessionHandler.layer
layer.frame = previewView.bounds
previewView.layer.addSublayer(layer)
view.layoutIfNeeded()
jsonSentence.text = "Press start button to get phrase"
startButton.setTitle("Start", for: .normal)
}
I've played around a bit trying viewDidAppear and viewWillAppear but I'm new to swift and don't think I fully understand what they do or if this is what's causing the problem.
The SessionHandler class variables need to be shared with an Objective-C class so I've created a shared instance of it to be used throughout the app.
static let sharedSession = SessionHandler()
It's hard to know what code is useful to share but if you need any more info I'm happy to provide it.
The answer seemed pretty straightforward in the end, sorry for the big question!
It was because the metadataObjectTypes was empty when the DataCaptureViewController was reloaded. So I created a viewDidAppear method and set the metadataObjectTypes to face in there.
override func viewDidLoad() {
super.viewDidLoad()
sessionHandler.setupCamera()
audioRecorder.setUpAudioSession()
createObservers()
let layer = sessionHandler.layer
layer.frame = previewView.bounds
previewView.layer.addSublayer(layer)
view.layoutIfNeeded()
jsonSentence.text = "Press start button to get phrase"
startButton.setTitle("Start", for: .normal)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
sessionHandler.metaOutput.metadataObjectTypes = [AVMetadataObject.ObjectType.face]
}
The app seems to be working now. If I've done this incorrectly though feel free to correct!
Recently, I decided to apply my previous knowledge in C++ and Python to learning Swift. After which, I decided to see what I could do with the SceneKit framework. After hours of checking through the documentation, and consulting a tutorial, I have to wonder what's going wrong with my code:
class GameViewController: UIViewController {
var gameView:SCNView!
var gameScene:SCNScene!
var cameraNode:SCNNode!
override func viewDidLoad() {
super.viewDidLoad()
initScene()
initView()
initCamera()
}
func initView() {
//initialize the game view - this view holds everything else in the game!
gameView = self.view as! SCNView
//allow the camera to move to gestures - mainly for testing purposes
gameView.allowsCameraControl = true
//use default lighting while still practicing
gameView.autoenablesDefaultLighting = true
}
func initScene() {
//initialize the scene
gameScene = SCNScene()
//set the scen in the gameView object to the scene created by this function
gameView.scene = gameScene
}
func initCamera() {
//create a node that will become the camera
cameraNode = SCNNode()
//since a node can be any object in the scene, this needs to be set up as a camera
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3 (x:0, y:5, z:15)
}
}
After more checking through the documentation and making sure that I was now copying from the tutorial directly to get it to work, I still have no luck with this. According to a lot of the other questions I found here on StackOverflow, it looks like it has something to do with the forced unwrapping, the exclamation points, but I'm not exactly sure why that is.
I've probably been staring the answer in the face combing through this documentation, but I'm not quite seeing what the problem is.
Also, apologies if my comments are a bit long and/or distracting.
You have the following problems:
1) you should re-order the initializations in your viewDidLoad, doing so:
initView() // must be initialized before the scene
initScene() // you have been crashing here on getting `gameView.scene`, but gameView was nil
initCamera()
2) cameraNode is not attached on the rootNode, so you may add the following code at the end of initCamera:
gameScene.rootNode.addChildNode(cameraNode)
I have a Swift SpriteKit scene and it doesn't happen every time but I did some testing on the restart game button by pressing menu and restart in quick successions repeatedly till it crashes. It also crashes randomly from other points that load the scene. My estimate is that theres about a 30% chance that it will crash on this line
class AppDelegate: UIResponder, UIApplicationDelegate {
with with the following error:
2015-03-07 09:52:14.347 DDgame[1457:433285] * Terminating app due
to uncaught exception 'NSGenericException', reason: '* Collection
<__NSArrayM: 0x170051e50> was mutated while being enumerated.'
*** First throw call stack: (0x18342259c 0x193b6c0e4 0x183421f50 0x187a57538 0x187a56c84 0x187a5661c 0x187a56510 0x10008f4c0
0x1000913b0 0x187a46bcc 0x187a43fd8 0x187a41038 0x187a6dfd8
0x100360a9c 0x187575280 0x187575118 0x1845718d0 0x1833c55e4
0x1833da200 0x1833da160 0x1833d80e0 0x1833050a4 0x18c49f5a4
0x187c36aa4 0x1000d1204 0x1000d1244 0x1941daa08) libc++abi.dylib:
terminating with uncaught exception of type NSException (lldb)
I used an catch all exception breakpoint and found out it is crashing in my update method on the gyro updates. Does anyone have a safer way of keeping gyro going safely with an update button?
Here is an extract of the touchesBegan method, I've removed the code sitting inside the other buttons just not to create too much fluff here: (not sure if you guys need the extras here?)
else if node.name == "replayButton" {
if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
NSNotificationCenter.defaultCenter().removeObserver(self, name: "pauseGame", object: nil)
let transition = SKTransition.revealWithDirection(SKTransitionDirection.Down, duration: 1.0)
scene.scaleMode = SKSceneScaleMode.AspectFill
self.view?.presentScene(scene, transition: transition)
}
I have discovered that it is crashing on the gyro updates in the update method:
override func update(currentTime: NSTimeInterval) {
var timeSinceLast = currentTime - lastUpdateTimeInterval
lastUpdateTimeInterval = currentTime
if(!gameOver) {
//check for gameover
if (self.goldStash <= 0) {
gameOver = true
gameIsPaused = true
self.gameOverMethod()
}
//spawning logic
spawnEngine()
//gyro update
if (self.motionManager.gyroData != nil) {
self.updateGyroNodeWithData(self.childNodeWithName("GoldBack") as? SKSpriteNode, gyroIn: motionManager.gyroData)
self.updateGyroNodeWithDataReverse(self.childNodeWithName("GoldFront") as? SKSpriteNode, gyroIn: motionManager.gyroData)
self.updateGyroNode(self.childNodeWithName("CBack1") as? SKSpriteNode, gyroIn: motionManager.gyroData, minIn: cBack1position.x-10.0, maxIn: cBack1position.x+10.0, sensitivity: 0.8)
self.updateGyroNode(self.childNodeWithName("CBack2") as? SKSpriteNode, gyroIn: motionManager.gyroData, minIn: cBack2position.x-10.0, maxIn: cBack2position.x+10.0, sensitivity: 0.8)
self.updateGyroNode(self.childNodeWithName("CBack3") as? SKSpriteNode, gyroIn: motionManager.gyroData, minIn: cBack3position.x-10.0, maxIn: cBack3position.x+10.0, sensitivity: 0.8)
self.updateGyroNode(self.childNodeWithName("CBack4") as? SKSpriteNode, gyroIn: motionManager.gyroData, minIn: cBack4position.x-10.0, maxIn: cBack4position.x+10.0, sensitivity: 0.8)
}
}
if timeSinceLast > 1 {
timeSinceLast = kMinTimeInterval
}
updateWithTimeSinceLastUpdate(timeSinceLast)
}
func updateWithTimeSinceLastUpdate(timeSinceLast: NSTimeInterval) {
for dwarf in dwarves {
dwarf.updateWithTimeSinceLastUpdate(timeSinceLast)
}
}
I fixed the problem. It was in this piece of code:
self.childNodeWithName("GoldBack") as? SKSpriteNode
It kept enumerating the scene for the nodes while the scene was then destroyed. I tried putting in a boolean to prevent the gyro updates running while the menu was open but for some reason it kept crashing, the percentage dropped significantly but something wasn't working.
The best solution was to create variables of the nodes in the init instead and then run gyro updates on those in the update method.
var goldBack = SKSpriteNode?()
goldBack = self.childNodeWithName("GoldBack") as? SKSpriteNode
Then in the update method:
self.updateGyroNodeWithData(goldBack, gyroIn: motionManager.gyroData)
I didn't know about this before but, another thing that really helped me was adding 'All Exceptions' breakpoint in Xcode. (This is done by going to the breakpoint navigator, pressing the + on the bottom left and add exception breakpoint)
My app contains two scenes. Playscene.swift and gamescene.swift.
Transitioning from gamescene to playscene (where play takes place) works perfectly. However, once a gameover is reached, I have a "replay" button appear allowing the user to return back to gamescene.swift. Upon transitioning back it crashes with an error "Attempted to add a SKNode which already has a parent." Is there a correct way to transition back to a home screen or restart the game so I don't receive the error? Thank you for all your help!!
if self.nodeAtPoint(touchLocation) == self.replay {
let scene = GameScene(size: self.size)
let skView = self.view as SKView!
scene.size = skView.bounds.size
self.view?.presentScene(scene)
let transition = SKTransition.crossFadeWithDuration(1.0)
transition.pausesOutgoingScene = false
skView.presentScene(scene, transition: transition)
}
}
Gamescene.swift error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attemped to add a SKNode which already has a parent: <SKLabelNode> name:'(null)' text:'Highscore:' fontName:'Avenir-Black' position:{344, 549}'
*** First throw call stack:
I feel stupid. I had added a breakpoint and wasn't paying attention. its safe to say its time for bed.
You are trying to present the same scene twice :
self.view?.presentScene(scene)
skView.presentScene(scene, transition: transition)
Maybe you wan't to do like so :
if self.nodeAtPoint(touchLocation) == self.replay {
let skView = self.view as SKView!
let scene = GameScene(size: skView.bounds.size)
// Might also work with the following line instead :
// let scene = GameScene(size: self.frame.size)
let transition = SKTransition.crossFadeWithDuration(1.0)
transition.pausesOutgoingScene = false
self.view?.presentScene(scene, transition: transition)
}
You might be declaring that SKLabelNode outside of the GameScene class, making the SKLabelNode global, you should declare it inside the class so it dies when scene A transitions to scene B.
I just update to xCode beta 6. In beta5 everything works but in beta 6 got to change somethings. I add a lot of "!" in my codes. Anyway, My project is little game. And after complete the level(won or lose) I want to call skscene.
The code is where in mainviewController:
if(flag==true){// IF we WON
/* That below lines should call "Won.swift" file But it doesn't */
var scene = Won(fileNamed: "GameScene")
let skView = self.view as SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
scene.scaleMode = SKSceneScaleMode.AspectFill
scene.position = CGPointMake(0, 568)
skView.presentScene(scene)
}
Won.swift file is:
class Won: SKScene {
var audioPlayer = AVAudioPlayer()
override func didMoveToView(view: SKView) {
/* Setup your scene here */
var alertSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("Fireworks", ofType: "mp3")!)
// Removed deprecated use of AVAudioSessionDelegate protocol
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)
var error:NSError?
audioPlayer = AVAudioPlayer(contentsOfURL: alertSound2, error: &error)
audioPlayer.prepareToPlay()
audioPlayer.play()
let myLabel = SKLabelNode(fontNamed:"Chalkduster")
myLabel.text = "You Win";
myLabel.fontSize = 65;
myLabel.fontColor = UIColor.redColor()
myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame));
self.addChild(myLabel)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
this code does work in beta 5 but doesn't in beta6.
Additionally I re-add uikit, avfoundation, spritekit foundations to project. And also check media files "mp3" or images are exist in bundle resources... The project runs perfectly without any eroor or warnings. But Skscene section doesn't work. I also tried to put breakpoint to trace how it does goes. the process goes on these lines but never goes "Won.swift" file.
Any suggestion?
Thank you.
It sounds like your variable "flag" isn't being properly set to true. Have you tried putting some println()'s in that code to see whether that code is actually being called?
Also, why are you putting it in the mainViewController? Put it in the play scene, so that the scene is controlling when you move to a different scene. Your main view controller should (probably) only fire up the very first scene, and let the scene logic handle transitioning to other scenes.
In fact, that could be your problem - if that "flag" is set to true, and that function is called repeatedly, say, every frame, then that file is getting called up repeatedly. Or, if you're setting flag back to false in there, and that code gets executed, somewhere else, it might be calling your other scene file, and immediately switching back to it so that you never see the "Won" scene.
Could be a lot of things. Start sprinkling some println() and set breakpoints to figure out what exactly is going on.