SpriteKit, Swift 2.0 - ScrollView Missing from Scene Startup via Button - ios

I recently implemented a scrollView into my GameViewController and it works really well, but the tutorial I looked at had only one scene which was the start up scene (GameScene) rather than a seperate scene which I'm going to be calling "Menu" so I managed to get it to launch the Menu scene rather than the regular "GameScene" but when I go from GameScene to the Menu scene with a button that I implemented in GameScene, the scrollview does not work, but it does show the pictures, but I just can't scroll through them.
My question is how do I get scrollView to work when I use the button (that is in GameScene) to go to the Menu scene?
This is the button (which is In GameScene)
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch: UITouch = touches.first!
let location: CGPoint = touch.locationInNode(self)
let node: SKNode = self.nodeAtPoint(location)
if (node == menubutton) {
let MenuScene = Menu(size: self.size, viewController: viewController)
let transition = SKTransition.flipVerticalWithDuration(0.5)
MenuScene.scaleMode = SKSceneScaleMode.AspectFill
self.scene!.view?.presentScene(MenuScene, transition: transition)
}
Here is my Menu scene:
import Foundation
import SpriteKit
let kMargin: CGFloat = 40
var backButton = SKSpriteNode()
var selectButton = SKSpriteNode()
class Menu: SKScene {
let world2 = SKSpriteNode()
private var imageSize = CGSize.zero
private weak var viewController: GameViewController?
init(size: CGSize, viewController: GameViewController?) {
self.viewController = viewController
super.init(size: size)
}
required init?(coder aDecoder: NSCoder) {
assert(false, "Use init(size:viewController:)")
super.init(coder: aDecoder)
}
override func didMoveToView(view: SKView) {
physicsWorld.gravity = CGVector.zero
imageSize = SKSpriteNode(imageNamed: "card_level01").size
let initialMargin = size.width/2
let marginPerImage = kMargin + imageSize.width
world2.size = CGSize(width: initialMargin*2 + (marginPerImage * 7), height: size.height)
addChild(world2)
for i in 1...8 {
let sprite = SKSpriteNode(imageNamed: String(format: "card_level%02d", i))
sprite.position = CGPoint(x: initialMargin + (marginPerImage * (CGFloat(i) - 1)), y: size.height / 2)
world2.addChild(sprite)
}
}
override func update(currentTime: NSTimeInterval) {
viewController?.applyScrollViewToSpriteKitMapping()
}
Here is my GameViewController
import UIKit
import SpriteKit
class GameViewController: UIViewController {
var scrollView: UIScrollView!
var contentView: UIView!
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let skView = view as! SKView
if (skView.scene === Menu.self) {
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
let scene = Menu(size: skView.bounds.size, viewController: self)
scene.scaleMode = .AspectFill
skView.presentScene(scene)
scrollView = UIScrollView(frame: self.view.bounds)
scrollView.delegate = self
scrollView.contentSize = scene.world2.frame.size
view.addSubview(scrollView)
contentView = UIView(frame: CGRect(origin: CGPoint.zero, size: scene.world2.size))
contentView.backgroundColor = UIColor.greenColor().colorWithAlphaComponent(0.2)
scrollView.addSubview(contentView)
applyScrollViewToSpriteKitMapping()
}
}
func applyScrollViewToSpriteKitMapping() {
let origin = contentView.frame.origin
let skPosition = CGPoint(x: -scrollView.contentOffset.x + origin.x, y: -scrollView.contentSize.height + CGRectGetHeight(view.bounds) + scrollView.contentOffset.y - origin.y)
let skView = view as! SKView
if let scene = skView.scene as? Menu {
scene.world2.position = skPosition
}
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return UIInterfaceOrientationMask.Portrait
} else {
return UIInterfaceOrientationMask.All
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override func shouldAutorotate() -> Bool {
return true
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
extension GameViewController: UIScrollViewDelegate {
}

I recently helped another member with a similar question, check it out it might be helpful to you
How to create a vertical scrolling menu in spritekit?
In my way I am subclassing scroll view and therefore can add it to SKScenes directly rather than the view controllers.
If you want the scrollView on more than 1 SKScene without duplicate code than need to subclass your SKScnenes
class BaseScene: SKScene ...
// Add scroll view
class Menu: BaseScene...
class GameScene: BaseScene ...
As a side note, you shouldn't really reference your viewController in your SKScenes, they shouldn't have to know about each other.

Related

Save state of GameScene through transitions

I want to build an inventory for my SpriteKit game. For this I want to go to another SKScene File which represents my inventory when I press the pause button. My problem is that when I make a transition from my InventoryScene back to my GameScene, the GameScene loads completely new. This is the transition code from my GameScene class:
func loadInventory(){
if !transitionInProgress{
transitionInProgress = true
if let scene = InventoryScene(fileNamed: "Inventory"){
Globals.InventoryGlobals.levelBeforeSwitch = currentLevel
scene.scaleMode = .aspectFill
let transition = SKTransition.push(with: .down, duration: 0.5)
self.view?.presentScene(scene, transition: transition)
}
}
}
With this code I'll go to my InventoryScene.
Now in my InventoryScene I want to go back to my GameScene with this:
func loadLevel(level: String){
if !transitionInProgress{
transitionInProgress = true
if let scene = GameScene(fileNamed: level){
scene.currentLevel = level
scene.scaleMode = .aspectFill
let transition = SKTransition.doorsOpenHorizontal(withDuration: 1)
self.view?.presentScene(scene, transition: transition)
}
}
}
The transitions are working but my problem is that the GameScene loads completely new, which is obviously because I instantiate a new GameScene. So when the player is in the middle of the level and then goes to the Inventory and back to the GameScene, the player is back at the beginning of the level. If I go from the Inventory back to the scene I want to be the GameScene as it was before (player position, enemy health, etc.)
Has anyone an idea how i can do this?
Retaining your scene is very simple, all you need to do is retain the scene with a strong reference
In your ViewController, it is as simple as storing a variable
class ViewController : UIViewController
{
var gameScene = GameScene(fileNamed:"GameScene")
}
Now for as long as your view controller is alive, your scene will be alive.
To access it, you just need to find a way to tell the MenuScene where your view controller is, then present the scene.
class ViewController : UIViewController
{
var gameScene = GameScene(fileNamed:"GameScene")
lazy var skView : SKView = self.view as! SKView
func gotoMenu()
{
let menu = MenuScene(fileNamed"MenuScene")
menu.viewController = self
skView.presentScene(menu)
}
}
class MenuScene : SKScene
{
var viewController : ViewController!
func returnToGame()
{
view.presentScene(viewcontroller.gameScene)
}
}
But, what if you don't want to use custom SKScene classes all the time, use a view controller, or would rather rely on components, why isn't there a convenient way to go back to a scene.
Well my friend, there is, and it is where userData comes into play
class GameScene : SKScene
{
func gotoMenu()
{
let menu = MenuScene(fileNamed:"MenuScene")
menu.userData = menu.userData ?? ["":Any]()
menu.userData["backToScene"] = self
view.presentScene(menu)
}
}
class MenuScene : SKScene
{
func returnToGame()
{
guard let userData = userData, let scene = userData["backToScene"] as? SKScene
view.presentScene(scene)
}
}
Since we are retaining it in the user data, we can now present the old scene anywhere we have access to the menu scene.
userData is also great in transferring inventory, of course I would create a class to manage the inventory, and just pass the reference via userData
Now, to create a menu that overlays the current scene, that is as simple as applying a new node onto your scene.
You can even use a separate SKS file to layout your menu, and overlay it:
class GameScene : SKScene
{
let menu = MenuScene(fileNamed:"MenuScene")
func overlayMenu()
{
scene.addChild(menu) //You probably want to add an SKCameraNode, and add it to the camera instead
}
override func update(currentTime: CFTimeInterval)
{
if menu.parent != nil
{
menu.update(currentTime:currentTime) //do this only when you need to have a constant update call, be sure to include additional functionality like `didFinishUpdate` in the approprate functions when needed
}
}
}
Of course now would be a good time to develop what is called a worldNode, also may be referred to as gameNode
Essentially what this node is, is the node that holds all your game elements.
This allows you to add overlay nodes that can pause your game.
Your scene hierarchy would like like this:
SKScene
--worldNode
----all nodes that belong in the game
--menuNode
----all nodes that belong on the menu
Now at any time, menu can set the worldNode's isPaused state to true, allowing the game to pause and still giving you the ability to interact with the menuNode
I do layover windows all the time in my Spritekit games, and it doesn't have to be as complicated as you are thinking. Here is how you can do it all in Spritekit without leaving the Scene.
Create a new class which is a subclass of SKSpriteNode for your InventoryDialog.
import SpriteKit
protocol InventoryDialogDelegate: class {
func close()
}
class InventoryDialog: SKSpriteNode {
private var closeButton: SKSpriteNode!
weak var delegate: InventoryDialogDelegate?
init(size: CGSize) {
super.init(texture: nil, color: .clear, size: size)
name = "inventoryDialog"
//do some background design work here
let background = SKSpriteNode(color: .white, size: self.size)
background.zPosition = 1
addChild(background)
closeButton = SKSpriteNode(texture: SKTexture(imageNamed: "closeButton"))
closeButton.position = CGPoint(x: self.size.width / 2 - closeButton.size.width / 2, y: self.size.height / 2 - closeButton.size.height / 2)
closeButton.zPosition = 2
addChild(closeButton)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.location(in: self)
if closeButton.contains(touchLocation) {
close()
}
}
func close() {
self.delegate?.close()
}
}
inside your GameScene file
class GameScene: SKScene {
var inventoryDialog: InventoryDialog!
var openButton: SKSpriteNode!
override func didMove(to view: SKView) {
openButton = SKSpriteNode(texture: SKTexture(imageNamed: "openButton"))
openButton.position = CGPoint(x: self.size.width / 2 - closeButton.size.width / 2, y: self.size.height / 2 - closeButton.size.height / 2)
openButton.zPosition = 2
addChild(openButton)
}
func displayInventoryDialog() {
backgroundBlocker = SKSpriteNode(imageNamed: "background3")
backgroundBlocker.size = self.size
backgroundBlocker.zPosition = 4999
addChild(backgroundBlocker)
inventoryDialog = InventoryDialog(size: CGSize(width: 500, height: 800))
inventoryDialog.delegate = self
inventoryDialog.zPosition = 5000
addChild(inventoryDialog)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//pause any action that you don't want running while the dialog is open
gameLayer.isPaused = true
let touch = touches.first
let touchLocation = touch!.location(in: self)
if openButton.contains(touchLocation) {
displayInventoryDialog()
}
}
}
//MARK: - InventoryDialogDelegate Methods
extension GameScene: InventoryDialogDelegate {
func close() {
//at this point you could update any GUI nesc. based on what happened in your dialog
backgroundBlocker.removeFromParent()
inventoryDialog?.removeFromParent()
gameLayer.isPaused = false
}
}
here is exactly what you wanted.
class GameScene: SKScene {
class InventoryManager: UIView {
override init(frame: CGRect) {
super.init(frame: CGRect(x: 0, y: 0, width: 300, height: 200))
//here is where you set up anything you want to display inside the view
self.backgroundColor = UIColor.green
let Button = UIButton()
Button.frame = CGRect(x: 0, y: 0, width: 40, height: 20)
Button.backgroundColor = UIColor.red
let TouchView = UIView()
TouchView.frame = self.frame
Button.center.x = TouchView.center.x
Button.center.y = TouchView.center.y
TouchView.backgroundColor = UIColor.brown
self.addSubview(TouchView)
//make sure you add any objects that you want to the TouchView and not to the self
TouchView.addSubview(Button)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
var InventoryView = InventoryManager()
var Node = SKSpriteNode()
override func didMove(to view: SKView) {
//this keeps the view centered in the screen
InventoryView.center.x = (self.view?.center.x)!
InventoryView.center.y = (self.view?.center.y)!
Node = SKSpriteNode(color: UIColor.blue, size: CGSize(width: 40, height: 40))
Node.position = CGPoint(x: self.frame.size.width / 3, y: self.frame.size.height / 3)
self.addChild(Node)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if !(self.view?.frame.contains(location))! {
InventoryView.removeFromSuperview()
}
if Node.contains(location) && InventoryView.isDescendant(of: self.view!) {
self.view?.addSubview(InventoryView)
}
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
clicking the blue button adds it to the scene again and clicking anywhere else removes it from the scene

Segue between SKScene and UIViewController

I'm writing simple math game with swift 3 and I have a problem with segues. I have two GameViewController's, oneUIViewController and one NavigationViewController.
I want to make two menus, one with game modes and one with difficulties, I want that the difficulties menu be interactive and have gravity so I decided to make it in SkScene. Everything works fine except segue between SkScene with dark screen and difficulties menu when user lost the game. I write a protocol which works when I'm going from main menu to second menu but when I'm going from SkScene with game it doesn't. I was trying to make a manual segue but protocol doesn't want to execute.
MyGVViewController code:
import UIKit
import SpriteKit
import GameplayKit
enum skad1 {
case game,menu
}
var skad = skad1.gra
class MyGVViewController: UIViewController, GameViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "Menugame") {
let gameScene = scene as! MySKScene
gameScene.gameViewControllerDelegate = self
gameScene.scaleMode = .aspectFill
gameScene.size = view.bounds.size
// Present the scene
view.presentScene(gameScene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
func goback() {
print("goback executed")
let view = self.view as! SKView?
view?.presentScene(nil)
navigationController?.popViewController(animated: true)
}
func goback2() {
print("goback2 executed")
let view = self.view as! SKView?
view?.presentScene(nil)
self.performSegue(withIdentifier: "comeback", sender: nil)
}
}
My SkScene with difficulties menu code:
import GameKit
import SpriteKit
import CoreMotion
protocol GameViewControllerDelegate: class {
func goback()
func goback2()
}
class MySKScene: SKScene {
//Variables
var motionManager: CMMotionManager!
var ball = SKSpriteNode()
var ball1 = SKSpriteNode()
var ball2 = SKSpriteNode()
var ball3 = SKSpriteNode()
var ball4 = SKSpriteNode()
weak var gameViewControllerDelegate: GameViewControllerDelegate?
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if ball.contains(location){
showgame(zakres: 200)
}else if ball1.contains(location){
showgame(zakres: 100)
}else if ball2.contains(location) {
showgame(zakres: 50)
}else if ball3.contains(location) {
showgame(zakres: 150)
}else if ball4.contains(location) {
if skad == .menu{
print("should execut goback")
self.gameViewControllerDelegate?.goback()
}else{
print("should execut goback2")
self.gameViewControllerDelegate?.goback2()
}
}
}
}
func showgame(zakres:Int) {
if let view = self.view {
range = zakres
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
let gameScene = scene as! GameScene
gameScene.scaleMode = .aspectFill
// Present the scene
gameScene.size = view.bounds.size
view.presentScene(gameScene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
override func didMove(to view: SKView) {
ball = self.childNode(withName: "ball") as! SKSpriteNode
ball1 = self.childNode(withName: "ball1") as! SKSpriteNode
ball2 = self.childNode(withName: "ball2") as! SKSpriteNode
ball3 = self.childNode(withName: "ball3") as! SKSpriteNode
ball4 = self.childNode(withName: "ball4") as! SKSpriteNode
motionManager = CMMotionManager()
motionManager.startAccelerometerUpdates()
let border = SKPhysicsBody(edgeLoopFrom: self.frame)
border.friction = 0
border.restitution = 0
self.physicsBody = border
}
override func update(_ currentTime: TimeInterval) {
if let accelerometerData = motionManager.accelerometerData {
physicsWorld.gravity = CGVector(dx: accelerometerData.acceleration.x * 20, dy: accelerometerData.acceleration.y * 35)
}
}
}
PS.
I'm reading what I wrote and I have to correct it, in difficulties menu I have a button which execute protocol that hide SKScene and show main menu. It works only when I'm going from main menu but when segue is from game when user lost, protocol isn't executed

iCarousel in Sprite Kit

Explanation
I'm trying to build a character selection menu similar to Crossy Road's one (as you can see here). So I found this iCarousel, which would help me with all of it, but everything I read talk about implementing it to a ViewController, which isn't my case. I'm using GameScene and I didn't found anything talking about it. Is there anyway I could implement it to my game? or even another effect similar to the character selection menu I mentioned above?
Attempt (beyowulf)
You can download it here.
GameScene.swift
import SpriteKit
class GameScene: SKScene {
var show = SKSpriteNode()
var hide = SKSpriteNode()
func showCharPicker(){
NSNotificationCenter.defaultCenter().postNotificationName("showCharPicker", object: nil)
}
func hideCharPicker(){
NSNotificationCenter.defaultCenter().postNotificationName("hideCharPicker", object: nil)
}
override func didMoveToView(view: SKView) {
/* Setup your scene here */
print("didMoveToView")
show = SKSpriteNode(imageNamed: "show")
show.anchorPoint = CGPointZero
show.position = CGPointZero
addChild(show)
hide = SKSpriteNode(imageNamed: "hide")
hide.anchorPoint = CGPointZero
hide.position = CGPoint(x: self.frame.width / 2 - hide.frame.width / 2, y: 0)
addChild(hide)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches{
let location = touch.locationInNode(self)
let node = nodeAtPoint(location)
if node == show{
print("show")
showCharPicker()
}
else if node == hide{
print("hide")
hideCharPicker()
}
}
}
}
GameViewController.swift
import UIKit
import SpriteKit
class GameViewController: UIViewController, iCarouselDataSource, iCarouselDelegate{
var squaresArray : NSMutableArray = NSMutableArray()
#IBOutlet weak var carousel: iCarousel!
deinit{
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func showCarousel(){
self.carousel.hidden = false
}
func hideCarousel(){
self.carousel.hidden = true
}
override func viewDidLoad(){
super.viewDidLoad()
// Configure iCarousel
carousel.dataSource = self
carousel.delegate = self
carousel.type = .CoverFlow
carousel.reloadData()
self.carousel.hidden = true
// Register showCarousel and hideCarousel functions
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.showCarousel), name: "showCharPicker", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.hideCarousel), name: "hideCharPicker", object: nil)
// Configure view
let skView = SKView()
self.view.insertSubview(skView, belowSubview: self.carousel)
skView.frame = self.view.bounds
// Additionals
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
// Configure scene
let scene = GameScene(size:self.view.bounds.size)
scene.scaleMode = .ResizeFill
scene.size = self.view.bounds.size
skView.presentScene(scene)
}
//iCarousel
override func awakeFromNib(){
super.awakeFromNib()
squaresArray = NSMutableArray(array: ["square1","square2","square3"])
}
func numberOfItemsInCarousel(carousel: iCarousel) -> Int{
return squaresArray.count
}
func carousel(carousel:iCarousel, didSelectItemAtIndex index:NSInteger){
//self.hideCarousel()
}
func carousel(carousel: iCarousel, viewForItemAtIndex index: Int, reusingView view: UIView?) -> UIView{
var itemView: UIImageView
if (view == nil){
itemView = UIImageView(frame:CGRect(x:0, y:0, width:200, height:200))
itemView.contentMode = .Center
}
else{
itemView = view as! UIImageView;
}
itemView.image = UIImage(named: "\(squaresArray.objectAtIndex(index))")
return itemView
}
func carousel(carousel: iCarousel, valueForOption option: iCarouselOption, withDefault value: CGFloat) -> CGFloat{
if (option == .Spacing){
return value * 2
}
return value
}
}
What's happening:
Thanks in advance,
Luiz.
You can use NSNotifications to show your character picker. You just need to observe the notifications posted by your SKScene. Your viewDidLoad should look something like:
override func viewDidLoad(){
super.viewDidLoad()
carousel.type = .CoverFlow
carousel.reloadData()
let spriteKitView = SKView()
spriteKitView.frame = self.view.bounds
self.view.insertSubview(spriteKitView, belowSubview: self.carousel)
spriteKitView.showsFPS = true
spriteKitView.showsNodeCount = true
spriteKitView.ignoresSiblingOrder = true
self.gameScene = GameScene(size:self.view.bounds.size)
self.gameScene.scaleMode = .AspectFill
self.gameScene.imageName = self.images[0] as! String
self.carousel.hidden = true
spriteKitView.presentScene(self.gameScene)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.showCarousel), name: gameScene.kShowNotification, object: nil)
}
You'll want to implementing carousel(carousel:iCarousel, didSelectItemAtIndex index:NSInteger) so you know what is selected, and so you can return to game play. For example:
func carousel(carousel:iCarousel, didSelectItemAtIndex index:NSInteger)
{
self.gameScene.imageName = self.images[index] as! String
self.hideCarousel()
}
You also need to remove observing before your view controller is deallocated.
deinit
{
NSNotificationCenter.defaultCenter().removeObserver(self)
}
Your SKScene can then post a notifications:
import SpriteKit
class GameScene: SKScene {
var imageName = "square1"{
didSet{
self.hidden = false
self.childNode.texture = SKTexture(imageNamed: imageName)
}
}
let kShowNotification = "showPicker"
var childNode = SKSpriteNode()
override func didMoveToView(view: SKView) {
/* Setup your scene here */
self.childNode = SKSpriteNode(imageNamed: imageName)
self.childNode.anchorPoint = CGPointZero
self.childNode.position = CGPointZero
self.addChild(self.childNode)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.showCharPicker()
}
func showCharPicker()
{
self.hidden = true
NSNotificationCenter.defaultCenter().postNotificationName(kShowNotification, object: nil)
}
}
If you want to change hit detection, you need to subclass the view for which you need it to change. This case your iCarousel view.
You can then either override hitTest or pointInside. I've created an iCarousel subclass and overrode pointInside to only return true when the point is inside one of the carousel's contentView's subviews.
class CarouselSubclass: iCarousel {
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
var inside = false
for view in self.contentView.subviews
{
inside = CGRectContainsPoint(view.frame, point)
if inside
{
return inside
}
}
return inside
}
}
You need to remember to change the class of your carousel in interface builder and update your outlet as well.

How to change scene when button is pressed using Sprite Kit and swift 2

I am new to swift and trying add some extra features after reading this tutorial http://www.raywenderlich.com/66877/how-to-make-a-game-like-candy-crush-part-1. What I want to do is combine with the element of digital pets, kind like the iOS game 'Best Friend'. So first, I add a main scene(picture of main scene) and several buttons, when button is pressed, the scene would change. Below are the codes I used to achieve it.
MainSceneController.swift:
import UIKit
import SpriteKit
class MainSceneController: UIViewController {
#IBOutlet weak var settingButton: UIButton!
#IBOutlet weak var storeButton: UIButton!
#IBOutlet weak var petsButton: UIButton!
#IBOutlet weak var levelButton: UIButton!
override func prefersStatusBarHidden() -> Bool {
return true
}
override func shouldAutorotate() -> Bool {
return true
}
override func viewDidLoad() {
super.viewDidLoad()
let mainScene = MainScene(size:CGSize(width: 320, height: 568))
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
mainScene.scaleMode = .AspectFill
skView.presentScene(mainScene)
}
#IBAction func changeScene(sender: UIButton) {
let labelText = sender.titleLabel?.text!
print(labelText)
switch (labelText!) {
case "LevelButton":
let scene = LevelScene(size:CGSize(width: 320, height: 568))
let reveal = SKTransition.fadeWithDuration(1)
scene.scaleMode = SKSceneScaleMode.AspectFill
scene.view?.presentScene(scene, transition: reveal)
case "PetsButton":
let scene = PetsScene(size:CGSize(width: 320, height: 568))
let reveal = SKTransition.fadeWithDuration(1)
scene.scaleMode = SKSceneScaleMode.AspectFill
scene.view?.presentScene(scene, transition: reveal)
default:
break
}
}
}
import SpriteKit
class PetsScene: SKScene {
override init(size: CGSize) {
super.init(size: size)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.redColor()
let mylabel = SKLabelNode(fontNamed: "Chalkduster")
mylabel.text = "PetsScene"
mylabel.fontSize = 70
mylabel.position = CGPoint(x: self.frame.size.width / 2,
y: self.frame.size.height / 2)
self.addChild(mylabel)
}
}
When I pressed the button, the scene do not change. I also use segue to achieve it. But is seems like not what I want. And I have looked for some projects source code, but I cannot find similar one. I would appreciate any advice, thanks in advance.
I am not sure if this is what you are looking for either, but it is the method I use to transition between scenes. I do not use Segues. I am not sure why, other than that I never bothered to learn how to use them well enough to default to them. I only rarely use Storyboards, even. So the below code is an example of how to transition from scene to another just in code. I usually have a GlobalFunctions.swift file that holds functions that are shared by many classes, the following would be in such a file:
func transitionToScene(node: String, sendingScene: SKScene) {
//println("Entering transition to scene method with \(node) and \(sendingScene.name)")
let transDur = 1.5
let transition = SKTransition.fadeWithColor(sendingScene.backgroundColor, duration: transDur)
var scene = SKScene()
switch node {
case splashSceneString:
scene = SplashScene(size: sendingScene.size)
case gameSceneString:
scene = GameScene(size: sendingScene.size)
case optionsSceneString:
scene = OptionsScene(size: sendingScene.size)
case menuSceneString:
scene = MenuScene(size: sendingScene.size)
default:
if general_debug == true { print("Default should not be reached. No Scene?") }
}
scene.scaleMode = SKSceneScaleMode.AspectFill
sendingScene.view!.presentScene(scene, transition: transition)
}
So this func would be called by some event. For example if you touched a button within your game/app the touchesEnded: function would do something like this:
transitionToScene("optionsSceneString", sendingScene: self.scene)
Hope this helps somewhat. Good luck!

Add UIView to back of scene in SpriteKit game

I am trying to make a advent calendar with a snow affect in swift2. I am using the game template while using SpiteKit.
Here is my code so far:
GameScene.swift
import SpriteKit
class GameScene: SKScene {
/*override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
let location = touch.locationInNode(self)
print(location)
}
}*/
func test()
{
//Generate Doors
//Initilization
var adventDoors = [AdventDoor]()
let offset = CGVector(dx: 10,dy: 10)
var size = CGRectMake(offset.dx, offset.dy, 60, 60)
var ypos:CGFloat = 20
var xpos:CGFloat = 10
var index = 0
var xDoor = AdventDoor(frame: size)
let randomIdentifier = [Int](1...24).shuffle()
for _ in 1...4
{
for i in 1...6
{
size = CGRectMake(xpos, ypos, 60, 60)
xDoor = AdventDoor(frame: size)
xDoor.opaque = false
xDoor.restorationIdentifier = "\(randomIdentifier[index])"
xDoor.generateDoor()
adventDoors.append(xDoor)
print("1...6")
ypos += 80
//xpos += 20
index++
if i == 6
{
print("Moving to next view")
}
}
xpos += 80
ypos = 20
}
size = CGRectMake(10, 500, 300, 60)
xDoor = AdventDoor(frame: size)
xDoor.opaque = false
xDoor.restorationIdentifier = "\(25)"
xDoor.generateDoor()
adventDoors.append(xDoor)
index = 0
for door in adventDoors
{
index++
self.view?.addSubview(door)
}
print("\(index) doors were added")
}
}
GameViewController.swift
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView
//skView.showsFPS = true
//skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
scene.backgroundColor = UIColor.greenColor()
skView.presentScene(scene)
scene.test()
//Snow
let wrappedSnowPath = NSBundle.mainBundle().pathForResource("Snow", ofType: "sks")
if let snowPath = wrappedSnowPath
{
let snowEmitter:SKEmitterNode = NSKeyedUnarchiver.unarchiveObjectWithFile(snowPath) as! SKEmitterNode
let screenBounds = UIScreen.mainScreen().bounds
snowEmitter.position = CGPointMake(screenBounds.width, screenBounds.height)
scene.addChild(snowEmitter)
}
}
}
override func shouldAutorotate() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .AllButUpsideDown
} else {
return .All
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
AdventDoor.swift just contains a custom UIView (AdventDoor) along with some more functions
This is what it looks like.
As you can see, the snow SKEmitterNode particles are behind the AdventDoors and not in front.
How would I get my snow to display in front of the UIView Advent Doors instead of behind?
Instead of addsubview for the door, What you need to do is rearrange how your views are laid out. What you need is a UIView as your main view, then you add the SKView as a child to the main view. Then if you want to add the doors in during the scene creation process, you need to do self.view.superview.insertSubView(door, atIndex:0) or self.view.superview.insertSubView(door, belowSubView:self.view) so that the doors are placed behind the scene subview

Resources