swift - Cómo configurar la detección de colisión SceneKit
(1)
Hola, he examinado la documentación y no puedo entender cómo configurar la detección de colisión en el kit de escena. ¿Puede alguien mostrar un ejemplo? Por favor ayuda, estoy muy desesperado por resolver esto. ¡Gracias!
Editar: Hola Muchas gracias, lo siento, olvidé mencionar que mi proyecto está en marcha. No es gran cosa, puedo traducirme en su mayor parte.
Tengo las BitMasks funcionando correctamente ya que los objetos chocan y rebotan entre sí. Sin embargo, parece que no puedo hacer que la función funcione
func physicsWorld(world: SCNPhysicsWorld, didBeginContact contact: SCNPhysicsContact){
let contactMask = contact.nodeA.physicsBody!.categoryBitMask | contact.nodeB.physicsBody!.categoryBitMask
if (contactMask == (CollisionBallCategory | CollisionTerminatorCategory)) {
println("Collided")
}
}
Mirando la documentación parece que necesito asignar de alguna manera el delegado mundial de física de escenas a este método. No estoy seguro de cómo hacerlo.
Lo más importante sobre la detección de colisiones en SceneKit:
- se basa en máscaras de bits, que juntas forman una tabla.
- Un delegado de contacto es cómo responde a las colisiones.
Hacer colisionar objetos
Por ejemplo, puede indicar un poco de diseño del juego en inglés simple como este:
Los asteroides se golpean entre sí (y hacen asteroides más pequeños). Los misiles deben cruzarse entre sí, pero destruyen cohetes y asteroides. Los cohetes no deberían hacer nada a los misiles (solo al revés), pero si uno se acerca demasiado a otro o a un asteroide, tiene un problema grave y no irá al espacio hoy.
El primer paso para darse cuenta de que con la detección de colisión es codificar ese diseño en términos de qué pares interactúan. Puedes hacer esto con una tabla:
| Missile | Rocket | Asteroid
--------------------------------------
Missile | No | Yes | Yes
Rocket | No | Yes | Yes
Asteroid | No | No | Yes
Luego puede convertir los encabezados de la tabla en un conjunto de constantes de categoría para usar en su código.
typedef NS_OPTIONS(NSUInteger, CollisionCategory) {
CollisionCategoryMissile = 1 << 0,
CollisionCategoryRocket = 1 << 1,
CollisionCategoryAsteroid = 1 << 2,
};
missile.physicsBody.categoryBitMask = CollisionCategoryMissile;
rocket.physicsBody.categoryBitMask = CollisionCategoryRocket;
asteroid.physicsBody.categoryBitMask = CollisionCategoryAsteroid;
Use OR bit a bit en estas constantes para crear valores
collisionBitMask
que llenen la tabla.
missile.physicsBody.collisionBitMask =
CollisionCategoryRocket | CollisionCategoryAsteroid;
rocket.physicsBody.collisionBitMask =
CollisionCategoryRocket | CollisionCategoryAsteroid;
asteroid.physicsBody.collisionBitMask = CollisionCategoryAsteroid;
Eso es todo lo que necesita para que SceneKit resuelva las colisiones por usted (es decir, rebotar objetos entre sí).
Respondiendo a colisiones
Si también quieres recibir notificaciones de colisiones (para que puedas hacer explotar misiles y hacer que tu nave se convierta en un asteroide al final del juego), deberás establecer un delegado de contacto en el mundo de física de tu escena e implementar uno o más de los métodos de delegado de contactos que se llaman cuando ocurre un contacto.
En su método de delegado de contactos (digamos,
physicsWorld:didBeginContact:
, necesitará averiguar qué categorías de cuerpos estuvieron involucrados en el contacto y cuál fue cuál, para que pueda acceder a su código que hace lo que su juego hace por la colisión
- (void)physicsWorld:(SCNPhysicsWorld *)world didBeginContact:(SCNPhysicsContact *)contact
{
CollisionCategory contactMask =
contact.nodeA.physicsBody.categoryBitMask | contact.nodeB.physicsBody.categoryBitMask;
// first, sort out what kind of collision
if (contactMask == (CollisionCategoryMissile | CollisionCategoryRocket)) {
// next, sort out which body is the missile and which is the rocket
// and do something about it
if (contact.nodeA.physicsBody.categoryBitMask == CollisionCategoryMissile) {
[self hitRocket:contact.nodeB withMissile:contact.nodeA];
} else {
[self hitRocket:contact.nodeA withMissile:contact.nodeB];
}
} else if (contactMask == (CollisionCategoryMissile | CollisionCategoryAsteroid)) {
// ... and so on ...
}
}
Ponga este código en una de sus clases (un controlador de vista, tal vez, donde sea que mantenga la lógica de su juego es bueno), y haga que esa clase declare conformidad con el protocolo
SCNPhysicsContactDelegate
.
@interface ViewController: UIViewController <SCNPhysicsContactDelegate>
Luego asigne ese objeto al mundo de física de su escena como delegado de contacto:
// in initial setup, where presumably you already have a reference to your scene
scene.physicsWorld.contactDelegate = self
Aprendiendo más
Hay un poco sobre la resolución de colisión en la documentación de referencia de SCNPhysicsBody . Y Apple tiene un código de muestra que utiliza la detección de colisión: es parte de la gran variedad de demostraciones en las slides WWDC y las aplicaciones de muestra de demo , y también en la demostración de física del vehículo .
Más allá de eso, el modelo de manejo de colisiones de SceneKit es casi exactamente el mismo que el de SpriteKit, por lo que casi todo en la guía de programación SpriteKit también es útil para comprender las mismas cosas en SceneKit.