swift background color + button/image bug - ios

I have just encountered a really odd bug when testing my app.
import Foundation
import SpriteKit
import AVFoundation
class EndScene : SKScene {
var Highscore : Int!
var ScoreLabel : UILabel!
var HighscoreLabel : UILabel!
var AudioPlayer = AVAudioPlayer()
override func didMoveToView(view: SKView) {
scene?.backgroundColor = UIColor.grayColor()
var EndGameMusic = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("endSong", ofType: "wav")!)
AudioPlayer = AVAudioPlayer(contentsOfURL: EndGameMusic, error: nil)
AudioPlayer.prepareToPlay()
AudioPlayer.play()
var scoreDefault = NSUserDefaults.standardUserDefaults()
var Score = scoreDefault.valueForKey("Score") as! NSInteger
var HighscoreDefault = NSUserDefaults.standardUserDefaults()
Highscore = HighscoreDefault.valueForKey("Highscore") as! NSInteger
NSLog("\(Highscore)")
ScoreLabel = UILabel(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 30))
ScoreLabel.center = CGPoint(x: view.frame.size.width, y: view.frame.size.width/4)
ScoreLabel.text = "Last Game:\(Score)"
self.view?.addSubview(ScoreLabel)
HighscoreLabel = UILabel(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 30))
HighscoreLabel.center = CGPoint(x: view.frame.size.width, y: view.frame.size.width/2)
HighscoreLabel.text = "Highscore: \(Highscore)"
self.view?.addSubview(HighscoreLabel)
let RestartBtn = SKSpriteNode(imageNamed: "RestartBtn")
RestartBtn.position = CGPointMake(size.width/2, size.height/2)
RestartBtn.name = "Restart"
addChild(RestartBtn)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let touchLocation = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(touchLocation)
if(touchedNode.name == "Restart"){
Restart()
}
}
}
func Restart(){
self.view?.presentScene(InGame(), transition: SKTransition.crossFadeWithDuration(0.3))
HighscoreLabel.removeFromSuperview()
ScoreLabel.removeFromSuperview()
}
}
what happens is that when this scene is called, the two labels, highscore and score appear, but my background goes a yellow color and my image i'm using as a button, isn't present, yet if i touch anywhere on the screen (regardless of bounds, I've tried) it will activate the button and exit.
if i remove this code:
let RestartBtn = SKSpriteNode(imageNamed: "RestartBtn")
RestartBtn.position = CGPointMake(size.width/2, size.height/2)
RestartBtn.name = "Restart"
addChild(RestartBtn)
Then the bug doesn't occur and the background sets normally, so i know there is an issue with this code, and assumed that for some reason it was zoomed in on my image (cant post without 10 reputation...)
However, after all attempts at resizing and fixing this issue, nothing actually helped, but i still cant help thinking that this has a relatively simple solution, its just that this code was used in my previous scenes without any issue.
Any Suggestions?
EDIT: i am certain that its taken the color of my image and used that as the background, and made the entire background the restart button, i just tried it with another image styled differently.
EDIT 2: image size = 200x80, the image i used in my first edit i scaled down to 40x80

This is not a bug, so basically the way SKSpriteNode's work is they take on the size of your image file since you did not explicitly set the size anywhere else. From the code you have, I'm guessing your image is over 1000 by 1000 pixels, so it is taking up the entire screen. The iPhone 6's size for example, is something like 667 by 375 pixels within xCode so in a sense your restart button is actually larger than the screen. To fix this, set the xScale and yScale values low and see what works(makes the size of the sprite smaller)
RestartBtn.xScale = 0.01
RestartBtn.yScale = 0.01
That would make the button 100x smaller(or 1/100 the previous size), just play around with the values until you get a size you like.
Edit: So I had this issue before as well in spriteKit. For some reasons the size of the screen gets messed up unless you set its size where you declare the scene.
(What you called your screen).size = skView.bounds.size
For example if you did like
let skView = self.view as SKView!
myScene = GameScene()
myScene.size = skView.bounds.size
skView.presentScene(myScene)

Related

SpriteKit scrolling background image showing line between images

I'm learning how to make games in iOS, so I decided to replicate the first level of Mario Bros using my own assets, just to learn how to make them as well.
The issue I'm having right now is that, when creating the scrolling background, using this formula I found on Hacking with Swift, I keep getting a line in between my images.
I've checked that my images have no border in the AI file. (The images are the ones above)
import SpriteKit
import GameplayKit
class GameScene: SKScene {
private var player = SKSpriteNode()
private var bg = SKSpriteNode()
override func didMove(to view: SKView) {
let playerTexture = SKTexture(imageNamed: "player")
player = SKSpriteNode(texture: playerTexture)
player.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
addBackground()
self.addChild(player)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
// MARK: UTILITY FUNCTIONS
func addBackground() {
let bgTexture = SKTexture(imageNamed: "bg")
for i in 0 ... 1 {
bg = SKSpriteNode(texture: bgTexture)
//bg.position = CGPoint(x: (i == 0 ? bgTexture.size().width : bgTexture.size().width - 1) * CGFloat(i), y: self.frame.midY)
bg.position = CGPoint(x: (bgTexture.size().width * CGFloat(i)) - CGFloat(1 * i), y: 0)
bg.size.height = self.frame.height
//bg.anchorPoint = CGPoint.zero
bg.zPosition = -10
self.addChild(bg)
let moveLeft = SKAction.moveBy(x: -bgTexture.size().width, y: 0, duration: 5)
let moveReset = SKAction.moveBy(x: bgTexture.size().width, y: 0, duration: 0)
let moveLoop = SKAction.sequence([moveLeft, moveReset])
let moveForever = SKAction.repeatForever(moveLoop)
bg.run(moveForever)
}
}
}
Just create a new project, copy-paste it and you should see it running
I also changed my GameScene.sks size to: 2688 x 1242
And one last change I made to make the background appear in full screen was to set the LaunchScreen as stated in this answer which seems to be an issue in Xcode 12
I understand that the formula from Hacking with Swift post is making the images overlap by 1px, I've tried removing the 1 * i part from it, yet results are not different.
Other thing I did was to verify the images were "joining" perfectly together, and they do, the lines you can see in the image below are from the AI canvas, both images join in between "cactuses".
I also tried running it into a device in case it might be a bug in the simulator:
Your image has a thin black border on the left handside and along the top, 1 pixel wide. It's black. Remove that and try again.

SKSpriteNode showing non integer position when assigned an SKPhysicsBody

I have some very simple Swift code which places an SKSpriteNode on screen at position x:214, y:200. In the update method, I print the current position of said SKSpriteNode. If I omit all of the SKPhysicsBody code, then the position of my SKSpriteNode is shown in the debug window as (214.0, 200.0) which is as I would expect it. If, however, I run the program with the SKPhysicsBody code present, then the first print returns correct with (214.0, 200.0), but subsequent calls return (214.000015258789, 200.0).
This looks to me like something in the physics world is affecting (albeit in a very small way) the position of the SKSpriteNode. What I cannot fathom for the life of me is what that could be. My code is exactly as shown below, nothing more and nothing less. I think I have disabled everything that could possibly have any effect on the position. I am just hoping that I might have missed something silly.
The problem may seem trivial as the values are only fractionally off, however when I attempt to move the sprite smoothly across the screen (incrementing it's x position + 1 on each update) then it will occasionally miss an entire position, going from 289.0 to 289.99, then 291.0.
Any help would be much appreciated. It feels like a bug to me, but as I am fairly new to the physics side of SpriteKit, I would like to be certain.
import SpriteKit
class GameScene: SKScene {
private var playerSprite : SKSpriteNode!
override func didMoveToView(view: SKView) {
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)//borderBody
self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
playerSprite = SKSpriteNode(color: UIColor.yellowColor(), size: CGSize(width: 100, height: 100))
playerSprite.position = CGPoint(x: 214, y: 200)
playerSprite.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: 100, height: 100))
if let playerSpritePhysicsObject = playerSprite.physicsBody {
playerSpritePhysicsObject.usesPreciseCollisionDetection = true
playerSpritePhysicsObject.affectedByGravity = false
playerSpritePhysicsObject.allowsRotation = false
playerSpritePhysicsObject.dynamic = false
playerSpritePhysicsObject.angularVelocity = 0.0
playerSpritePhysicsObject.friction = 0.0
playerSpritePhysicsObject.linearDamping = 0.0
playerSpritePhysicsObject.angularDamping = 0.0
playerSpritePhysicsObject.velocity = CGVectorMake(0, 0)
playerSpritePhysicsObject.restitution = 0.0
}
self.addChild(playerSprite)
}
override func update(currentTime: NSTimeInterval) {
print("playerSprite Position = \(playerSprite.position)")
}
}
Here is the debug window output:
playerSprite Position = (214.0, 200.0)
2017-06-19 20:39:47.682 PhysicsTest[9193:2340365] <SKMetalLayer: 0x14fe6e760>: calling -display has no effect.
playerSprite Position = (214.000015258789, 200.0)
playerSprite Position = (214.000030517578, 200.0)
playerSprite Position = (214.000030517578, 200.0)
playerSprite Position = (214.000030517578, 200.0)
playerSprite Position = (214.000030517578, 200.0)
After some time agonising over this problem, I think I may have found a work around. It's nasty but does the trick. Simply reset the position of the SKSpriteNode in the didFinishUpdate() function which occurs after the didSimulatePhysics() function has complete.
override func didFinishUpdate() {
playerSprite.position.x = 214
}
Having tested further, I found this didn't work for iOS 7.1. In order for it work on older devices, you must place the code in the didSimulatePhysics() function instead:
override func didSimulatePhysics() {
playerSprite.position.x = 214
}

iPhone 4, 5, 5s and 6+ simulators misaligning nodes

Apologies if I've already asked this, but Im very stuck; the first question was very unclear. Created a game in SpriteKit on the iPhone 6 simulator and when run the other sized simulators, everything is thrown out of alignment. Im not using any .sks, .xib or storyboarding. Is there a way to programmatically resize the game so that all screen sizes have the correct positioning? Will post code if necessary.
Edit: GameViewController:
import UIKit
import SpriteKit
var sceneSize = UIScreen.mainScreen().bounds
var screenWidth = sceneSize.width
var screenHeight = sceneSize.height
class GameViewController: UIViewController {
var scene: GameScene!
override func viewDidLayoutSubviews() {
super.viewWillLayoutSubviews()
let skView = self.view as! SKView
skView.multipleTouchEnabled = false
scene = GameScene(size: skView.bounds.size)
//scene.scaleMode = .AspectFill
scene.scaleMode = SKSceneScaleMode.ResizeFill
skView.presentScene(scene)
scene.anchorPoint = CGPointMake(0, 0)
skView.showsFPS = false
skView.showsNodeCount = false
skView.ignoresSiblingOrder = true
let completionBlock: () -> Void = {
}
let errorBlock: (NSError!) -> Void = { error in
print("error")
}
RevMobAds.startSessionWithAppID("", withSuccessHandler: completionBlock, andFailHandler: errorBlock);
}
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
}
}
Edit 2 GameScene:
import Foundation
import SpriteKit
import UIKit
var sceneSize = UIScreen.mainScreen().bounds
var screenWidth = sceneSize.width
var screenHeight = sceneSize.height
class GameScene: SKScene {
player.position = CGPointMake(screenWidth / 5, screenHeight / 3)
}
First, set the GameScene SKSceneScalode (this is found pre-programmed into the GameViewController to .ResizeFill
Then copy the following code into GameScene:
Import UIKit
var screenSize = UIScreen.mainscreen().bounds
var screenWidth = screenSize.width
var screenHeight = screenSize.height
This will store the width and height of the screen in the variables screenWidth and screenHeight. Now when you create your spriteNodes and other objects, use these two variables as your basis. Here's an example
someNode.position = CGPoint(x: screenWidth/5 y: screenHeight/3)
Are you the same person that posted on the Swift section of Reddit? Here's my code that works for all iPhone devices, as I answered it well on the Reddit post the same way. Here's the more profound answer.
In your View Controller, enter the following code inside the viewDidLoad().
//Create a scene object with the max size of an iPhone 6 Plus at 1920 x 1080
//That means it can easily scale down to the 6, 5, and 4S.
let mainScene = GameScene(size: CGSize(width: 1920, height: 1080))
//To fill the scene and fit for all sizes
mainScene.scaleMode = .AspectFill
//Prepare the view
let mainView = self.view as! SKView
mainView.showsFPS = true
mainView.showsNodeCount = true
mainView.ignoresSiblingOrder = true
//Move to the required scene
mainView.presentScene(mainScene, transition: SKTransition.fadeWithDuration(1))
In the class scene, add the following variables at the top.
//Will be used to get the ratio of the device
let deviceWidth = UIScreen.mainScreen().bounds.width
let deviceHeight = UIScreen.mainScreen().bounds.height
let maxAspectRatio: CGFloat
let playableArea: CGRect
If you don't already have an override init inside the class scene that the view is moving to, add the following inside the class.
//Overrides the superclasses' SKScene init
override init(size: CGSize) {
//Initializes the maxAspectRatio variable
maxAspectRatio = deviceHeight / deviceWidth
print("The ratio of the device is: \(maxAspectRatio)")
//Prepares the rectangle view that is viewable by the user (ONLY FOR PORTRAIT MODE)
let playableWidth = size.height / maxAspectRatio
let playableMargin = (size.width - playableWidth) / 2.0
playableArea = CGRect(x: playableMargin, y: 0, width: playableWidth, height: size.height)
/*USE THIS CODE IF YOUR APP IS IN LANDSCAPE MODE*****************************************
let playableHeight = size.width / maxAspectRatio
let playableMargin = (size.height - playableHeight) / 2.0
playableArea = CGRect(x: 0, y: playableMargin, width: size.width, height: playableHeight)
*///*************************************************************************************
//Find out what kind of device the user is using
if maxAspectRatio == (4 / 3) {
//The iPhone's 4S ratio is 4:3
print("The user is using an iPhone 4S")
} else {
//That means the ratio is not 4:3, so it probably is 16:9
print("The user is using an iPhone 5, 6, 6S, or 6 Plus.")
}
//Assigns the size of the scene to the superclass init of SKScene
//Must be called after all variables are initialed in this class
super.init(size: size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Add the following function inside the scene class too so you can see the viewable area that is seen by the user. It draws a red rectangle. Then, just call this function inside the didMoveToView() function. Now to position objects, all you have to do is use CGRectGet... properties on the playableArea to position the objects correctly.
//Used to draw the rectangle or the view of the phone
func drawPlayableArea() {
let shape = SKShapeNode()
let path = CGPathCreateMutable()
CGPathAddRect(path, nil, playableArea)
shape.path = path
shape.strokeColor = SKColor.redColor()
shape.lineWidth = 8
addChild(shape)
}
override func didMoveToView(view: SKView) {
drawPlayableArea()
//Examples used to position objects on a screen related to the playable area or viewable area
//This works for all iPhone devices as well as iPods.
let label = SKLabelNode(fontNamed: "Arial")
label.fontColor = SKColor.whiteColor()
label.text = "Hello, Redditor!"
label.position = CGPoint(x: CGRectGetMidX(playableArea), y: CGRectGetMidY(playableArea))
addChild(label)
let score = SKLabelNode(fontNamed: "Arial")
score.verticalAlignmentMode = .Top
score.horizontalAlignmentMode = .Left
score.fontColor = SKColor.whiteColor()
score.text = "Score: 100"
score.position = CGPoint(x: CGRectGetMinX(playableArea) + 20, y: CGRectGetMaxY(playableArea) - 20)
addChild(score)
backgroundColor = SKColor.blackColor()
}
Here are the github files if you need to understand the code more.
Screenshots of positions in an 4S and a 6 Plus. Notice how the objects stay positioned relative to the scene size.
OK I had time to reread this, and I think I know what is going on, I will leave my old answer in here as well.
New Answer:
What is going on here is you are setting your scene size at the time of creation of view, before any auto constraints get applied. So your view size is not the same as your screen size. Thus, your scene becomes the same size regardless of device, then you apply the math based on your screen, and everything gets thrown off. What you need to do is 1) Verify that my theory is right, and that at the time of creating your scene, the size is not the same as screen size, then 2) create the scene at the time the view does get resized for the device.
Old Answer:
The reason everything is thrown out of alignment is because your game scene is being resized based on device. You need to set a single constant size for doing things the way you are doing
let scene = GameScene(size:CGSizeMake(400,300))
Where 400 x 300 can be changed based on your needs. this will give you a play area of fixed size to work with, so your nodes will be placed the same. Then, to handle the screen size, you set your scenes scale mode to aspect fill or aspect fit, depending on if you want to lose information, but have no black bars, or have black bars and show all information. (resize fill will distort your image by stretching if that is what you want). to do this it is just scene.scaleMode = .AspectFill
Now you will have to tweak numbers and images if you are getting undesirable results, but the same logic should be applied to all devices. If you start grabbing things like the screen size again, things will start misaligning.
Inside your scene code, use the scenes width and height to manipulate your nodes, not the screen
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
player.position = CGPointMake(scene.frame.width / 5, scene.frame.height / 3)
}
Best way to think about it is the Game scene gets treated as its own screen, and in the end you want to take this screen and scale it to meet the various device settings.

How to interact with a SKSpriteNode after another one of the same var name has been created?

I appologise for the confusing question title, but I wasn't quite sure how to phrase my question. My ultimate goal is to remove an SKSpriteNode a certain amount of time after it's creation. However, there is a small complication. Using the following code
func remove() {
laser.removeFromParent()
}
runAction(
SKAction.sequence([
SKAction.waitForDuration(5.0),
SKAction.runBlock(remove)
])
)
I'm able to remove the SKSpriteNode I named 'laser'. When I call my function once, fireLasers(), everything runs smoothly and the laser disappears after 5 seconds. The problem is when I call it twice within a period of 5 seconds. When this happens, the first laser will stick around indefinitely and the second disappears earlier than intended. I understand why this happens, but would like to know if there's a way around it. Here's the code for the GameScene which creates the world, background image, player Sprite, and defines the fireLasers function. There's a lot of stuff that goes on in the ViewController that works with the velocities and directions of the sprites, but I hope this is sufficient to find a solution.
import SpriteKit
class GameScene: SKScene {
var player = SKSpriteNode()
var world = SKShapeNode()
var worldTexture = SKSpriteNode()
var laser = SKShapeNode()
override func didMoveToView(view: SKView) {
self.anchorPoint = CGPointMake(0.5, 0.5)
self.size = CGSizeMake(view.bounds.size.width, view.bounds.size.height)
// Add world
world = SKShapeNode(rectOfSize: CGSizeMake(7000, 7000))
world.physicsBody = SKPhysicsBody(edgeLoopFromPath: world.path!)
world.fillColor = SKColor.blackColor()
self.addChild(world)
// Add Background Image
worldTexture = SKSpriteNode(imageNamed: "grid")
worldTexture.size = CGSize(width: 7000, height: 7000)
world.addChild(worldTexture)
// Add player
player = SKSpriteNode(imageNamed: "Spaceship")
player.size = CGSize(width: 50, height: 50)
player.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: 50, height: 50))
player.physicsBody?.affectedByGravity = false
player.physicsBody?.dynamic = true
world.addChild(player)
}
override func update(currentTime: CFTimeInterval) {
world.position.x = -player.position.x
world.position.y = -player.position.y
}
func fireLasers() {
laser = SKShapeNode(rectOfSize: CGSizeMake(10, 50))
laser.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: 10, height: 50))
laser.position.x = player.position.x
laser.position.y = player.position.y + CGFloat(50)
laser.fillColor = SKColor.redColor()
laser.physicsBody?.dynamic = true
laser.physicsBody?.affectedByGravity = false
world.addChild(laser)
func remove() {
laser.removeFromParent()
}
runAction(
SKAction.sequence([
SKAction.waitForDuration(5.0),
SKAction.runBlock(remove)
])
)
}
}
Instead of keeping a reference to the laser (which will, as you noticed, force you to only have one around at a time), why not run an action on the created laser nodes themselves? This will allow you to use the handy SKAction class method removeFromParent():
let newLaser = makeNewLaser()
let laserAction = SKAction.sequence([SKAction.waitForDuration(5), SKAction.removeFromParent()])
newLaser.runAction(laserAction)

SpriteKit & Swift: Creating nodes via didBeginContact messes up the positioning

I don't know if this is a weird bug in Xcode or there's something about the SpriteKit's coordinate system I don't understand.
The premise is that the position of a node is always relative to its parent. However, whenever I call a block that creates and positions a node with a physics body from "didBeginContact" of SKPhysicsContactDelegate, the node is always positioned relative to the scene (instead of its parent). Note that calling this same block works as intended when triggered anywhere but "didBeginContact". Another thing is that, if I remove the physics body of the said node, the block will now work as intended even when called from "didBeginContact".
I've been stuck with this issue for two days and it would be too draggy to give other details about my actual code. So I've made a very simple project demonstrating this anomaly. Just create a new project in Xcode 6 with Spritekit Template, and replace GameViewController.swift and GameSwift.swift with the codes posted below. Just run in iPad Air and everything else should be self explanatory.
Final Notes:
As you press the first button and makes contact to the second
button, look at the lower left corner of the screen. You will see
that the boxes are being "wrongly" positioned there.
Try to remove the physics body of the box in "AddBox". It will now work as intended.
Please let me know if you think this is a bug or there's something in the coordinates system or physics world that I don't understand
GameViewController.swift:
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scene = GameScene()
scene.size = view.frame.size
let skView = self.view as SKView
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
}
GameScene.swift:
import SpriteKit
let kButtonSize = CGSize(width: 500, height: 100)
let kContainerSize = CGSize(width: 500, height: 300)
let kBoxSize = CGSize(width: 25, height: 25)
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMoveToView(view: SKView) {
physicsWorld.contactDelegate = self
addFirstButton()
addSecondButton()
addContainer()
}
func addFirstButton() {
let button = SKSpriteNode(color: SKColor.blueColor(), size: kButtonSize)
let label = SKLabelNode(text: "Call 'addBox' from didBeginContact")
button.name = "firstButton"
label.name = "firstLabel"
button.position = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame))
button.physicsBody = SKPhysicsBody(rectangleOfSize: button.size)
button.physicsBody.allowsRotation = false
button.physicsBody.affectedByGravity = false
button.physicsBody.categoryBitMask = 0x1
button.physicsBody.contactTestBitMask = 0x1
button.addChild(label)
addChild(button)
}
func addSecondButton() {
let button = SKSpriteNode(color: SKColor.blueColor(), size: kButtonSize)
let label = SKLabelNode(text: "Call 'addBox' from touchesBegan")
button.name = "secondButton"
label.name = "secondLabel"
button.position = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame)-200)
button.physicsBody = SKPhysicsBody(rectangleOfSize: button.size)
button.physicsBody.dynamic = false
button.physicsBody.categoryBitMask = 0x1
button.physicsBody.contactTestBitMask = 0x1
button.addChild(label)
addChild(button)
}
func addContainer() {
let container = SKSpriteNode(color: SKColor.greenColor(), size:kContainerSize)
let label = SKLabelNode(text: "Created node should fall here")
label.fontColor = SKColor.blackColor()
container.name = "container"
container.physicsBody = SKPhysicsBody(edgeLoopFromRect: container.frame)
container.position = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame)+300)
container.addChild(label)
addChild(container)
}
func addBox() {
let container = childNodeWithName("container")
let box = SKSpriteNode(color: SKColor.blueColor(), size: kBoxSize)
box.physicsBody = SKPhysicsBody(rectangleOfSize: box.size)
box.position = CGPointMake(0, 100)
container.addChild(box)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.anyObject() as UITouch
let point = touch.locationInNode(self)
let node = nodeAtPoint(point)
if node.name == nil {return}
switch node.name! {
case "firstButton", "firstLabel":
let button = childNodeWithName("firstButton") as SKSpriteNode
button.physicsBody.applyImpulse(CGVectorMake(0, -500))
case "secondButton", "secondLabel":
addBox()
default:
break
}
}
func didBeginContact(contact: SKPhysicsContact!) {
addBox()
}
}
I have submitted a ticket regardomg this issue with Apple Bug Report. I hope this helps anyone who is encountering the same issue. Here is their response:
Apple Developer Relations24-Sep-2014 04:40 AM
Engineering has determined that this is an issue for you to resolve
based on the following:
You can't modify a tree which it is being simulated, and this is
clearly stated in Programming Guide.
It seems like you can't create physics bodies in the contact delegate (this is similar to other engines, eg. AndEngine). Instead of creting new body straight into the delegate, mark some bool flag there and do your creation in the didSimulatePhysics or didFinishUpdate - works for me.

Resources