SpriteKit & Swift: Créer des nœuds via didBeginContact bousille le positionnement

Je ne sais pas si c'est un bogue bizarre dans Xcode ou s'il y a quelque chose à propos du système de coordonnées du SpriteKit que je ne comprends pas.

La prémisse est que la position d'un nœud est toujours relative à son parent. Cependant, chaque fois que j'appelle un bloc qui crée et positionne un nœud avec un corps de physique à partir de "didBeginContact" de SKPhysicsContactDelegate, le nœud est toujours positionné par rapport à la scène (au lieu de son parent). Notez que l'appel de ce même bloc fonctionne comme prévu lorsqu'il est déclenché n'importe où sauf "didBeginContact". Une autre chose est que, si je supprime le corps physique du noeud, le bloc fonctionnera comme prévu, même s'il est appelé depuis "didBeginContact".

J'ai été coincé avec ce problème pendant deux jours et il serait trop draggy pour donner d'autres détails sur mon code réel. J'ai donc réalisé un projet très simple démontrant cette anomalie. Créez simplement un nouveau projet dans Xcode 6 avec Spritekit Template, et remplacez GameViewController.swift et GameSwift.swift par les codes postés ci-dessous. Il suffit de courir dans iPad Air et tout le rest devrait être explicite.

Notes finales:

  1. Lorsque vous appuyez sur le premier button et que vous entrez en contact avec le second button, regardez dans le coin inférieur gauche de l'écran. Vous verrez que les boîtes sont "mal" positionnées là.
  2. Essayez de supprimer le corps physique de la boîte dans "AddBox". Cela fonctionnera comme prévu.
  3. S'il vous plaît laissez-moi savoir si vous pensez que c'est un bug ou il y a quelque chose dans le système de coordonnées ou de la physique que je ne comprends pas

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() } } 

J'ai soumis un billet concernant ce problème avec Apple Bug Report. J'espère que cela aidera tous ceux qui rencontrent le même problème. Voici leur réponse:

Apple Developer Relations24-Sep-2014 04:40 AM

Ingénierie a déterminé qu'il s'agit d'un problème que vous devez résoudre en fonction des éléments suivants:

Vous ne pouvez pas modifier un tree dans lequel il est simulé, et cela est clairement indiqué dans le Guide de programmation.

Il semble que vous ne pouvez pas créer de corps physiques dans le délégué de contact (ceci est similaire à d'autres moteurs, par exemple AndEngine). Au lieu de créer un nouveau corps directement dans le délégué, marquez un drapeau bool et faites votre création dans didSimulatePhysics ou didFinishUpdate – ça marche pour moi.