Changing view with SKView? - swift

I want to know how to change to a scene when the character collides with the enemy at game over. I have made a scene under main.storyboard and I want to know how to hook it up through code, I only know how to hook it up using buttons but thats not what I am looking for as you would not press a button when you die to take you to the game over scene.
UPDATE:
func gameOver() {
gameDelegate?.gameDelegateGameOver(score)
let gameOverScene: GameOverScene = GameOverScene(size: self.size)
self.view!.presentScene(gameOverScene, transition: SKTransition.fadeWithDuration(0.0))
Thats what I have for my gameOver when collision is detected. Yes it does take me to a new scene but not the scene I made in main.storyboard.

What you could do is to create a collision boolean and if it's true (hence, something has collided), you can present a new scene with view.presentScene(YOUR_SCENE, SK_ANIMATION) in the update function.
EDIT:
I've found the tutorial from where you got the code (or at least I assume you did) and got it working with the following:
In "didMoveView" add:
player.physicsBody?.categoryBitMask = PhysicsCategory.Player
player.physicsBody?.contactTestBitMask = PhysicsCategory.Monster
player.physicsBody?.collisionBitMask = PhysicsCategory.None
player.physicsBody = SKPhysicsBody(circleOfRadius: player.size.width/2)
player.physicsBody?.dynamic = true
(PhysicsCategory.Player is just a value I added in the PhysicsCategory)
Then, in the function where you add the enemy sprites you have to add in order to make the two collide:
monster.physicsBody?.contactTestBitMask = PhysicsCategory.Player
Last but not least, you have to add the following code to add "an action" to the collision the didBeginContact function:
if ((secondBody.categoryBitMask & PhysicsCategory.Monster != 0) &&
(firstBody.categoryBitMask & PhysicsCategory.Player != 0)) {
let gameOverScene = GameOverScene(size: self.size, won: false)
view?.presentScene(gameOverScene, transition: SKTransition.flipHorizontalWithDuration(0.5))
}
Hope it's working for you now!

If you want to present a storyboard scene, you need to use something like
let gameOverScene = self.storyboard!.instantiateViewControllerWithIdentifier("GameOverViewController") as! GameOverViewController
self.view!.presentScene(gameOverScene, transition: SKTransition.fadeWithDuration(0.0))

Related

How to use the SpriteKit method body(at : CGPoint)?

I have game that during the game your finger moves around and should avoid hitting some obstacles. I know how to use collision but I recently heard that there is a funtion called the body(at : point).
I should say that I have masked my obstacles. Because of that I can't use the contains(point) function.
I really want to use the body(at : point) function otherwise I know how to work with collisions.
Some of my code:
play_button = self.childNode(withName: "play_button") as! SKSpriteNode
for t in touches{
let fingerlocation = t.location(in: self)
if physicsworld.body(at : fingerlocation)
{
let so = level6(fileNamed:"level6")
so?.scaleMode = .aspectFill
self.view?.presentScene(so!, transition:
SKTransition.crossFade(withDuration: 1))
}
}
but it does not work.
physicsworld.body returns an SKPhysicsBody if found, so you just need to check if it exists
if let _ = physicsworld.body(at : fingerlocation)
If your goal is to make a button, then I would not do this approach.
I would recommend just subclassing SKSpriteNode and enabling the isUserInteractionEnable`
class Button : SKSpriteNode
{
override init()
{
super.init()
isUserInteractionEnabled = true
}
//then in your touchesBegan/touchesEnded code (wherever you placed it)
....
let so = level6(fileNamed:"level6")
so?.scaleMode = .aspectFill
scene.view?.presentScene(so!, transition:
SKTransition.crossFade(withDuration: 1))
....
}
I've had a similar problem, "if let body = physicsWorld.body(at: touchLocation){...}" was responding wherever I've touched the screen.
I figured out, that by default the SKScene object created its own SKPhysicsBody which was set on top of all other SKPhysicsBodies.
I solved this problem by making a new SKPhysicsBody for the scene, which didn't interfere anymore. Hope this code will help you.
inside of didMove:
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody

Swift SpriteKit presentScene error

I am trying to make a asteroids type game, and every time the asteroid collides with the player the end scene loads fine, cut when I try to restart its like the code is stuck looping the collision function that called the end scene. Here is the collision function
func didBeginContact(contact: SKPhysicsContact) {
print("5")
//Creating a variable that holds which two items collide
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
//Creating a switch case statement
//This will run a different case depending on whitch two objects collide
switch(contactMask) {
//If the satellite and the asteroid collide do this
case bitMask.satellite.rawValue | bitMask.asteroid.rawValue:
//removing the satellite
let secondNode = contact.bodyB.node
secondNode?.physicsBody?.allowsRotation = true
let firstNode = contact.bodyA.node
firstNode?.removeFromParent()
//Stopping the mission timer
removeActionForKey("missionDurationTime")
self.removeAllChildren()
print("6")
self.view?.presentScene(EndScene())
print("7")
default:
return
}
}
And here is the function that goes back to my game scene from my end scene
func Restart(){
print("hello")
RestartBtn.removeFromSuperview()
speach.removeFromSuperview()
score.removeFromSuperview()
gameOver.removeFromSuperview()
self.view?.presentScene(GameScene())
}
After I hit the restart button all of the labels in the EndScene go away like they should but then the screen is blank and the console just prints `5
6
7
Too many times to count until the labels in the end scene come back. I am very confused and in way over my head. Any help is much appreciated!
UPDATE:
I changed the code in my restart button to this
print("hello")
RestartBtn.removeFromSuperview()
speach.removeFromSuperview()
score.removeFromSuperview()
gameOver.removeFromSuperview()
let scene = GameScene(size: self.view!.bounds.size)
scene.backgroundColor = SKColor(red: 1, green: 1, blue: 1, alpha: 1)
scene.scaleMode = .AspectFill
scene.backgroundColor = UIColor.whiteColor()
let transition = SKTransition.pushWithDirection(SKTransitionDirection.Left, duration:0.5)
self.scene?.view?.presentScene(scene, transition: transition)
Now the game scene loads and everything is as it should be, except for the fact that everything is five times bigger. This is what it should look like:
What it should look like
And this is what it looks like
What it does look like
In you restart code you are initialising the GameScene with a size of view.bounds.
If you have not changed your GameViewController code than by default you are using the GameScene.sks file as reference (visual level editor) with a default scene size of 1024*768 and scale mode .AspectFill.
In short in your restart function try this
if let scene = GameScene(fileNamed: "GameScene") { // fileNamed is the GameScene.sks file
scene.backgroundColor = SKColor(red: 1, green: 1, blue: 1, alpha: 1)
....
}
or alternatively
let scene = GameScene(size: CGSize(width: 1024, height: 768)
scene.backgroundColor ....

SKSpriteNode won't position after didBeginContact

I'm working with collision detection with SpriteKit and Swift. I have a SKScene that responds to a collision with the didBeginContact function:
func didBeginContact(contact: SKPhysicsContact) {
if (contact.bodyA.categoryBitMask == ColliderType.Food && contact.bodyB.categoryBitMask == ColliderType.Head) {
placeFoodInRandomGridLocation()
}
}
func placeFoodInRandomGridLocation() {
let randomX = arc4random_uniform(UInt32(myGrid.columnCount))
let randomY = arc4random_uniform(UInt32(myGrid.rowCount))
foodSpriteHolder.position = CGPoint(x: colLines[Int(randomX)], y: rowLines[Int(randomY)])
}
The problem is that I can easily adjust the position of the foodSpriteHolder before this didBeginContact function fires. It simply will not move the foodSpriteHolder when the placeFoodInRandomGridLocation is called.
It seems like a scope issue. I'm just not sure how to isolate why the position won't update. I can even make the foodSpriteHolder visibility hidden in this flow...So, I know i can access it.
For reference here is how the physics body is setup for the Food class item that is within the foodSpriteHolder:
self.physicsBody = SKPhysicsBody(rectangleOfSize: self.size, center: CGPointMake(reducedSize/2, reducedSize/2))
self.physicsBody?.affectedByGravity = false
self.physicsBody?.categoryBitMask = ColliderType.Food
Lastly the placeFoodInRandomGridLocation function definitely gets called...The position just won't update.
Thanks for any ideas,
Josh
The answer in this case was to remove the node when the collision is detected:
contact.bodyA.node?.removeFromParent()
Now I can create a new node in a new position. Since affectedByGravity is set to false the node wouldn't update its position.

Transitioning back to gamescene.swift causes error "'Attemped to add a SKNode which already has a parent"

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.

Swift multiple scenes

I have made a swift SpriteKit game but now I want to add a menu scene(and eventually a lose scene). So what I did was
I created a new scene called Menu scene.
I edited GameViewController and changed it to load menu scene(it worked).
In MenuScene.swift I put some code in so when a sprite was clicked it would run this code:
let reveal = SKTransition.flipHorizontalWithDuration(0.5)
let scene = GameScene(size: size)
self.view?.presentScene(scene, transition:reveal)
But when I click the button it flips round to the next scene and immediately crashes.
So my question is: Am I doing this the right way and if I am why does it crash?
It turns out I wasn't deleting a particle emitter so I put in a bit of code to delete it just before the transition and it worked.
If you do, it should work:
let reveal = SKTransition.flipHorizontalWithDuration(0.5)
let scene = GameScene(size: self.frame.size)
let skView = view as SKView!
skView.presentScene(scene, transition:reveal)
I hope this works for you.
Since you're in sprite kit , it's wrong to use :
self.view?.presentScene(scene, transition:reveal)
You should do the following :
let scene = GameScene(size: view.bounds.size)
let skView = view as SKView
//skView.showsFPS = true
//skView.showsNodeCount = true
//skView.ignoresSiblingOrder = true
//scene.scaleMode = .ResizeFill
skView.presentScene(scene, transition:reveal)