I have an SKSpriteNode that functions properly. But when I add a UIImage, the SKSpriteNode becomes hidden behind the UIImage. I have been trying to figure out why, but I am having a little bit of trouble and can't seem to figure out what I am missing to allow the SKSSpriteNode to appear on top of the UI background Image, instead of behind it where it can't be seen. Any help would be greatly appreciated!
import UIKit
import SpriteKit
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
skView.presentScene(scene)
}
}
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
}
}
import SpriteKit
import SceneKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var blueBall:SKSpriteNode!
override func didMoveToView(view: SKView) {
self.physicsWorld.gravity = CGVectorMake(0.0, -5.0)
self.physicsWorld.contactDelegate = self
blueBall = SKSpriteNode( imageNamed: "ball")
blueBall.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
blueBall.physicsBody = SKPhysicsBody(circleOfRadius: blueBall.size.width / 1.5 )
blueBall.physicsBody!.dynamic = true
blueBall.physicsBody!.allowsRotation = false
self.addChild(blueBall)
}
override func touchesBegan(touches: Set<UITouch> , withEvent event: UIEvent?) {
self.blueBall.physicsBody?.velocity = CGVectorMake(35, 0)
self.blueBall.physicsBody?.applyImpulse(CGVectorMake(4, 10))
}
}
For draw order use the zPosition property:
In your case you will need to give the Sprite you want to be displayed in front a higher .zPosition value than the one to be displayed further back.
Example: ball.zPosition = 10
zPosition definition:
The height of the node relative to its parent.
Tipps:
The default value is 0.0. The positive z axis is projected toward the viewer so that nodes with larger z values are closer to the viewer.
Related
Explanation
My question is pretty straight forward: how do I print a variable from GameScene on GameViewController?
Code
I created this code below so it's easier to get the idea.
GameScene.swift
import SpriteKit
class GameScene: SKScene {
var variable = Int()
override func didMoveToView(view: SKView) {
/* Setup your scene here */
variable = 50
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
NSNotificationCenter.defaultCenter().postNotificationName("calledFromGameSceneVC", object: nil)
}
}
GameViewController.swift
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Set view size.
let scene = GameScene(size: view.bounds.size)
// Configure the view.
let skView = 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 = .ResizeFill
skView.presentScene(scene)
//------------------------------//
//Register observer
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(GameViewController.calledFromGameScene), name: "calledFromGameSceneVC", object: nil)
}
func calledFromGameScene(){
//Print variable
let scene = GameScene(size: view.bounds.size)
print("My variable from GameScene is: ", scene.variable)
}
}
Sorry for the brief explanation.
Thanks in advance,
Luiz.
The type of inter-object communication you are trying to achieve is probably best addressed through a delegation pattern using a protocol:
GameScene.swift
import SpriteKit
protocol GameSceneDelegate {
func calledFromGameScene(scene: GameScene)
}
class GameScene: SKScene {
var variable = Int()
var gameDelegate: GameSceneDelegate?
override func didMoveToView(view: SKView) {
/* Setup your scene here */
variable = 50
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
gameDelegate?.calledFromGameScene(self)
}
}
GameViewController.swift
class GameViewController: UIViewController, GameSceneDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Set view size.
let scene = GameScene(size: view.bounds.size)
// Configure the view.
let skView = 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 = .ResizeFill
/* Set the delegate */
scene.gameDelegate = self
skView.presentScene(scene)
}
func calledFromGameScene(scene:GameScene){
//Print variable
print("My variable from GameScene is: ", scene.variable)
}
}
Note that you can't use the property name delegate on your GameScene as SKScene already has a delegate property
I can't find anything about this when searching around so I decided to ask.
For some reason when I try to create a GameScene and try to scale it to the iPad according the suggestions around here. I get black borders around my view like so:
Here is my code:
import UIKit
import SpriteKit
class GameViewController: UIViewController {
var scene: GameScene!
override func viewDidLoad() {
super.viewDidLoad()
let skView = view as! SKView
skView.multipleTouchEnabled = false;
scene = GameScene(size: skView.bounds.size)
scene.scaleMode = .ResizeFill
scene.backgroundColor = UIColor.whiteColor()
print(scene.frame.size)
skView.presentScene(scene)
}
override func shouldAutorotate() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Pad {
return .Landscape
} else {
return .AllButUpsideDown
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
and this:
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
let testLabel = SKLabelNode(text: "Hello World!")
testLabel.position = CGPoint(x: size.width/2, y: size.height/2)
testLabel.fontColor = UIColor.blueColor()
addChild(testLabel)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
How can I get rid of the black borders around my view?
First set in your project targets in the deployment info section Devices to universal (I think you set it up with iPhone) see screenshot:
Then set scene.scaleMode = .ResizeFill to scene.scaleMode = .AspectFill and after that run your app and see the result with no black borders:
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.
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
I am very new to Swift and iOS development. I was watching tutorials on iOS development w/ Swift and SpriteKit. Following the tutorials I opened Xcode, new project, game, universal; and all I changed was the GameScene.swift. Here is the new code:
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
var node1 = SKNode()
node1.position = CGPoint(x: 100, y: 100)
self.addChild(node1)
var spr1 = SKSpriteNode(imageNamed: "Spaceship")
spr1.position = CGPointZero
spr1.zPosition = 1
node1.addChild(spr1)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
Note: the Spaceship image is provided by default.
So in the tutorials, this code added a spaceship to the scene. However, when I run the simulator, the scene remains blank. What can be the problem? If more info is needed, please say so and I will provide.
It could be running correctly, and you just can't see it on the screen. Depending on the device that you're emulating, you may be showing the spaceship off screen. Try a different, smaller device, to emulate, and check if you can see it.
Try going into your GameViewController.swift and making sure that you see something along the lines of this:
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene(fileNamed:"GameScene") {
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
scene.scaleMode = .AspectFill
scene.size = skView.bounds.size
skView.presentScene(scene)
}
}
}