Add SKReferenceNode programmatically
Asked Answered
R

4

5

I have long 2d level so I split it into more .sks files. I have "GameScene" where I can join them in Scene editor with drag and drop. It creates SKReferenceNodes. I've done that with success. But I would like to load these parts lazily when it's needed in code (it's said to be good practice). I'm failing on that.

This is particular part of code:

if let nextPart = SKReferenceNode(fileNamed: "Scene2_city2") {
        if self.childNodeWithName("Scene2_city2") == nil {
            self.addChild(nextPart)
        }
    }

"Scene2_city2" is name of .sks file and also name of Scene inside the file.

Running the code I get an error:

*** Terminating app due to uncaught exception 'Cant add body, already exists in a world', reason: 'Cant add body type: representedObject:[ name:'Scene2_city2' frame:{{-0, -0}, {1024, 768}} anchor:{0, 0}], already exists in a world'

This is very strange, because I first check it before I added.

Question: How should I add SKReferenceNode into SKScene programmatically?

EDIT: Here is simple example project on bitbucket. Reference scene is added for tap.

Reddish answered 3/4, 2016 at 17:47 Comment(5)
If you by any chance are able to make minimal example which can reproduce the issue you are talking about, and upload the repo to Github, you will probably get the better answer.Tulley
Also these are not the same as what you are experiencing, but it could be worth of reading because it is related to the same error you are seeing : forums.developer.apple.com/thread/19583, https://mcmap.net/q/2029604/-skphysicsbody-malloc-error... Probably there are some more threads, I've seen few more on StackOverflow about this.Tulley
Thanks for info. I don't think i'm doing same mistakes. Here is simple example project on bitbucket.Reddish
Does this even compile for you if let nextPart = SKReferenceNode(fileNamed: "Scene2_city2") ? I think you don't need optional binding there...Tulley
Yes, it compiles. Optional isn't needed there but I put it into optional to check it is initialised correctly.Reddish
H
9

I've found a workaround, it turns out there are no issues when creating a SKReferenceNode using the URL method. For example:

let path = NSBundle.mainBundle().pathForResource("SpaceShip", ofType: "sks")
let spaceShip = SKReferenceNode (URL: NSURL (fileURLWithPath: path!))
addChild(spaceShip)

I did a little digging and it appears in 7.3.1 vs 7.2.1 when creating a SKReferenceNode using fileNamed it now returns an optional. When unwrapped you get the SKScene which will of course throw out errors if you try and add it as a child to an existing scene.

Added a quick 7.3 test project https://github.com/cocojoe/SKReferenceNodeIssue/tree/workaround

Notice you referenced my post on the Apple forum. I filled a bug report about a month ago, from past experience these can take a while to be looked at.

Helton answered 1/5, 2016 at 20:41 Comment(0)
T
1

You have checked if the node is in scene's hierarchy. This error is related to the physics world and it has nothing to do with the fact that a node is already added to the scene.

Personally I couldn't reproduce what you are saying using SKReferenceNode, but that is probably because I am not fully aware of your current setup.

This error can happen if two nodes point to the same SKPhysicsBody instance, like this:

override func didMoveToView(view: SKView) {

        let sprite = SKSpriteNode(color: .purpleColor(), size: CGSize(width: 20, height: 20))
        let sprite2 = SKSpriteNode(color: .purpleColor(), size: CGSize(width: 20, height: 20))

        sprite.position =  CGPoint(x:frame.midX, y:frame.midY)
        sprite2.position = CGPoint(x:frame.midX, y:frame.midY + 100)

        let sharedPhysicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: -10, height: -10))

        sprite.physicsBody = sharedPhysicsBody
        sprite2.physicsBody = sharedPhysicsBody

        addChild(sprite)
        addChild(sprite2)
    }

This code will crash with the error you are getting currently. So check if you are re-using the same SKPhysicsBody somewhere or something like that and you will solve this.

Tulley answered 3/4, 2016 at 19:54 Comment(6)
Thanks for answer. See my simple project.Reddish
@Reddish This project doesn't crashing for me.Tulley
SKReferenceNode is added on tap. Instead of grey area on the right should appear greenish. If it doesn't crash for you when you tap, which versions of Xcode are you using? I found this thread.Reddish
@Reddish Yeah, that is exactly what I do, and what is happening and it doesn't crash. I am using Xcode 7.1.1.Tulley
So, it seems I'm tapping some bad way. Or I'm using bad Xcode.Reddish
Could you please update to newest Xcode to confirm the issue. It seems to me strange that I'm only one who is using latest Xcode with SKReferenceNode.Reddish
A
1

I'm having the same problem. If I use SKReferenceNode(fileNamed:) to create a new sprite from a sprite that exists in a scene.sks file I get the error

'Cant add body, already exists in a world'

This seems to be related to physics, and occurs when the object is added to to the scene with addChild. The error goes away if you set physics body to nil. Something like:

let newBox = SKReferenceNode(fileNamed: "Box")
newBox?.physicsBody = nil
newBox?.position = location
addChild(newBox!)

What's interesting is that the error appears even when the physics option is set to none. The solution above works in this case but you lose your physics body.

What seemed to work for me was creating a object to use as a source object, and use skNode.copy() to create a copy of it.

Almucantar answered 1/5, 2016 at 5:36 Comment(0)
C
0

I found another workaround that doesn't force you to set the physicsBody to nil.

In my case, I substituted the SKReferenceNode with an SKScene.

I first made sure that, in my .sks scene, all SKSpriteNodes were children of one main node. It doesn't have to be a direct link, you can still respect the hierarchies of your scene, but it's important that at the end of the "hierarchy tree" they are linked to one node, which in my case is the background, but it can also be an empty node.

enter image description here

In my GameScene file I then wrote the following code:

class GameScene: SKScene {
    var mainNode: SKNode? = nil
    override func didMove(to view: SKView) {
        var mySksScene = SKScene(fileNamed: "Caribbeans")
        mainNode = upperBackground?.childNode(withName: "backgroundCaribbean")
        mainNode?.removeFromParent()
        mySksScene = nil
        addChild(mainNode)
    }
}

I first declared an optional SKNode variable. Then, inside didMove(:to:), I loaded my .sks file in an optional SKScene() variable. I then set SKNode as the main node, the node which is the parent of all the other nodes (in my case: backgroundCaribbean). I then removed my mainNode from its parent scene, THEN set to nil the variable that used to contain the .sks scene, and only THEN I added my mainNode as a Child of my GameScene.

Camarena answered 12/1, 2017 at 21:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.