extensión para SKPhysicsContact estrellarse

Estoy creando un juego con SpriteKit, que tiene una colisión entre 2 cuerpos. Después de configurar los cuerpos, implementé el didBegin(_contact:) _contact didBegin(_contact:) como se muestra a continuación:

 func didBegin(_ contact: SKPhysicsContact) { if contact.bodyA.categoryBitMask == 0 && contact.bodyB.categoryBitMask == 1 { gameOver() } } 

y funcionó perfectamente.

Más tarde, mientras inspeccionaba la documentation para este método, encontré lo siguiente:

Los dos cuerpos físicos descritos en el parámetro de contacto no se pasan en un order garantizado .

Por lo tanto, para estar seguro, he ampliado la class SKPhysicsContact con una function que intercambia la categoría SKPhysicsContact entre ambos cuerpos, como sigue:

 extension SKPhysicsContact { func bodiesAreFromCategories(_ a: UInt32, and b: UInt32) -> Bool { if self.bodyA.categoryBitMask == a && self.bodyB.categoryBitMask == b { return true } if self.bodyA.categoryBitMask == b && self.bodyB.categoryBitMask == a { return true } return false } } 

El problema es que cuando se llama a la function, la aplicación se bloquea y me sale el siguiente error:

2017-07-18 13: 44: 18.548 iSnake Retro [17606: 735367] – [PKPhysicsContact bodiesAreFromCategories: y:]: selector no reconocido enviado a la instancia 0x60000028b950

2017-07-18 13: 44: 18.563 iSnake Retro [17606: 735367] *** Terminación de la aplicación debido a una exception no detectada 'NSInvalidArgumentException', razón: '- [PKPhysicsContact bodiesAreFromCategories: y:]: selector no reconocido enviado a instancia 0x60000028b950'

Esto aparentemente es un error, como se responde aquí: https://stackoverflow.com/a/33423409/6593818

El problema es que el tipo de contacto es PKPhysicsContact (como ya ha notado), incluso cuando explícitamente se lo indica como SKPhysicsContact y la extensión está en SKPhysicsContact. Tendría que poder hacer una extensión de PKPhysicsContact para que esto funcione. Desde esta lógica, podemos decir que en este momento, los methods de instancia no funcionarán en las extensiones SKPhysicsContact. Diría que es un error con SpriteKit, y deberías archivar un radar. Los methods de class siguen funcionando ya que los llama en la class misma.

Mientras tanto, deberías poder mover ese método a tu escena u otro object y llamarlo allí con éxito.

Para que conste, este no es un problema específico de Swift. Si realiza el mismo método en una categoría Objective-C en SKPhysicsContact, obtendrá el mismo locking.

Puede enviar un informe de error a apple:

https://developer.apple.com/bug-reporting/

Y repórtelo a la comunidad:

https://openradar.appspot.com/search?query=spritekit

Sin embargo, lo que realmente quiere hacer con su código es agregar las máscaras de categoría juntas . Y luego verifique la sum (2 + 4 y 4 + 2 siempre es igual a 6, independientemente de la order bodyA y bodyB).

Así es como obtienes contactos únicos , si configuras tus máscaras correctamente en potencias de dos (2, 4, 8, 16, etc.)

SKPhysicsContact es una class PKPhysicsContact de PKPhysicsContact , está extendiendo SKPhysicsContact pero, en realidad, necesita ampliar PKPhysicsContact (que no puede hacer)

Para preservar el order en sus methods de contacto, simplemente haga lo siguiente:

 let bodyA = contact.bodyA.categoryBitMask <= self.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB let bodyB = contact.bodyA.categoryBitMask > self.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB 

De esta manera, cuando necesita verificar un nodo específico, usted sabe qué nodo debe golpear, por lo que

 func didBegin(_ contact: SKPhysicsContact) { if contact.bodyA.categoryBitMask == 0 && contact.bodyB.categoryBitMask == 1 { gameOver() } } 

Se convierte

 func didBegin(_ contact: SKPhysicsContact) { let bodyA = contact.bodyA.categoryBitMask <= self.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB let bodyB = contact.bodyA.categoryBitMask > self.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB if bodyA.categoryBitMask == 0 && bodyB.categoryBitMask == 1 { gameOver() } } 

A continuación, puede agregar a su código, ya que ahora conoce los cuerpos individuales.

 func didBegin(_ contact: SKPhysicsContact) { let bodyA = contact.bodyA.categoryBitMask <= self.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB let bodyB = contact.bodyA.categoryBitMask > self.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB if bodyA.categoryBitMask == 0 && bodyB.categoryBitMask == 1 { gameOver() //since I know bodyB is 1, let's add an emitter effect on bodyB.node } } 

Por cierto, para las personas que ven esta respuesta, categoryBitMask 0 no debería disparar ningún contacto, necesita algún tipo de valor para que funcione. Este es un error que va más allá del scope de la pregunta de los autores, así que lo dejé en 0 y 1 a, ya que eso es lo que hace su código y está reclamando que funciona.