SpriteKit From Scratch Física y colisiones

Introducción

En este tutorial, la tercera entrega de la serie SpriteKit From Scratch, analizamos detalladamente la funcionalidad de simulación física de SpriteKit y cómo se puede utilizar en sus juegos 2D SpriteKit..

Este tutorial requiere que estés ejecutando Xcode 7.3 o superior, que incluye Swift 2.2 y iOS 9.3, tvOS 9.2 y OS X 10.11.4 SDK.

Para continuar, puede usar el proyecto que creó en el tutorial anterior o descargar una copia nueva de GitHub.

Los gráficos utilizados para el juego en esta serie se pueden encontrar en GraphicRiver. GraphicRiver es una gran fuente para encontrar ilustraciones y gráficos para tus juegos.

1. Mundos de la física

La primera parte de cualquier simulación física en SpriteKit es la fisica mundo Propiedad de la escena actual. Esta propiedad es una SKPhysicsWorld objeto que define propiedades como la gravedad de su escena, la velocidad de simulación física y el delegado de contacto. Los mundos físicos también pueden definir uniones entre objetos para sujetar varios nodos en puntos específicos.

Gravedad

Para el vista superior juego de estilo que estamos creando en esta serie, queremos cambiar el valor de gravedad predeterminado proporcionado por SpriteKit. La gravedad por defecto está destinada a un vista frontal juego con un valor de (0, -9.8) que simula la gravedad de la tierra, es decir, 0 aceleración horizontal y una aceleración hacia abajo de 9.8m / s². Para nuestro juego, necesitamos 0 gravedad vertical para que el coche no comience a acelerar hacia abajo una vez que configuramos sus propiedades físicas.

Abierto MainScene.sks y haga clic en el fondo gris para seleccionar la escena. A continuación, abra la Inspector de atributos y cambio Gravedad para que tanto el Xlos componentes se establecen en 0.

Es importante tener en cuenta que la gravedad es la única propiedad del mundo de la física que se puede cambiar con el editor de escenas de Xcode. Cualquier otra propiedad necesita ser cambiada programáticamente.

Póngase en contacto con el delegado

Para que el juego detecte colisiones entre objetos, debemos configurar el mundo de la física de la escena. contactDelegate propiedad. Este delegado puede ser cualquier objeto que se ajuste a la SKPhysicsContactDelegate protocolo. Este protocolo define dos métodos., didBeginContact (_ :) y didEndContact (_ :). Puede utilizar estos métodos para realizar acciones basadas en los objetos que están colisionando en la escena..

Para mantener nuestro código juntos, vamos a hacer el MainScene instancia su propio delegado de contacto. Abierto MainScene.swift y editar el MainScene definición de clase para ajustarse a la SKPhysicsContactDelegate protocolo.

importar UIKit importar SpriteKit clase MainScene: SKScene, SKPhysicsContactDelegate …

En didMoveToView (_ :), establecemos el MainScene instancia como el delegado de contacto de la fisica mundo propiedad.

anular la función didMoveToView (ver: SKView) … physicsWorld.contactDelegate = self

Implementaremos los métodos de la SKPhysicsContactDelegate protocolo más tarde. Primero debemos configurar las propiedades físicas de los nodos en la escena..

2. Los cuerpos de la física

Cualquier nodo en SpriteKit con el que desee simular la física de alguna manera debe tener asignado un único SKPhysicsBody objeto. Los cuerpos de física contienen varias propiedades que incluyen:

  • masa
  • densidad
  • zona
  • fricción
  • velocidad

En sus juegos, también puede definir hasta 32 categorías únicas y se puede asignar un cuerpo de física a cualquier número de estas categorías. Las categorías son muy útiles para determinar qué nodos de tu escena pueden interactuar entre sí en términos de colisiones.

En los cuerpos de física, estas categorías están representadas por la categoríaBitMaskcollisionBitMask propiedades, que son dadas las 0xFFFFFFFF Valor por defecto. Esto significa que todos los nodos pertenecen a todas las categorías. Es importante tener en cuenta que en este valor, cada dígito hexadecimal F es una forma abreviada y representa el número 15 en dígitos binarios (1111) cada uno de los cuales corresponde a una de las 32 categorías que puede utilizar.

Cuando dos nodos chocan, una lógica Y la operación se realiza en el collisionBitMask y el categoríaBitMask del primer y segundo cuerpo respectivamente. Si el resultado es un valor distinto de cero, SpriteKit realiza su simulación en los dos nodos a los que pertenecen los cuerpos..

Tenga en cuenta que este Y El cálculo se realiza dos veces con los dos cuerpos intercambiados. Por ejemplo:

  • Cálculo 1: bodyA.collisionBitMask & bodyB.categoryBitMask
  • Cálculo 2: bodyB.collisionBitMask & bodyA.categoryBitMask

Si no sabes cómo Y Operador trabaja, entonces aquí hay un ejemplo muy simple escrito en Swift:

let mask1 = 0x000000FF let mask2 = 0x000000F0 let result = mask1 & mask2 // result = 0x000000F0

los Y el operador determina qué partes de las máscaras de bits son iguales y devuelve un nuevo valor de máscara de bits que contiene las partes coincidentes.

Una cosa importante a tener en cuenta es que estas máscaras de bits solo afectan el lado de SpriteKit de la simulación física y no se le notifica de las colisiones detectadas de esta manera. Esto significa que los cuerpos pueden interactuar entre sí, pero ninguno de los métodos del delegado de contacto se llama.

Para que estos métodos se ejecuten, debe especificar un contactTestBitMask para cada cuerpo, que produce un valor distinto de cero cuando un Y El operador actúa sobre ellos. Para todos los cuerpos de física, esta máscara de bits tiene un valor predeterminado de 0x00000000 lo que significa que no se le notificará ninguna colisión en la que el cuerpo de física participe en.

Los cuerpos de física, incluidas sus diversas máscaras de bits, se pueden configurar en el editor de escenas Xcode. Abierto MainScene.sks, selecciona el coche, y abre el Inspector de atributos a la derecha. Desplácese hasta la Definición de física sección.

Porque Tipo de cuerpo se establece en Ninguna, Ninguna de las propiedades relacionadas con la física es visible. Para cambiar esto, necesitamos establecer Tipo de cuerpo a un valor distinto de Ninguna. Tres tipos de cuerpo están disponibles:

  • rectángulo delimitador
  • círculo delimitador
  • máscara alfa
Lo

Estos tres tipos de cuerpo de física son los más comunes en SpriteKit. Rectángulo delimitador y Circulo delimitador Trabaja creando una barrera alrededor del sprite para ser usado en simulaciones de física. Esto significa que el sprite colisiona con otro nodo cada vez que su forma delimitada golpea el cuerpo físico de otro nodo..

El borde de un rectángulo delimitador es exactamente el mismo que el tamaño del nodo que se muestra en el editor de escenas. Si selecciona Circulo delimitador, sin embargo, verá un círculo azul claro que representa la forma del círculo delimitador.

Máscara alfa funciona de forma un poco diferente y observa la textura de la imagen real del sprite para determinar los bordes del cuerpo de la física. Este tipo de cuerpo es, con mucho, el más preciso en SpriteKit, pero puede tener un gran impacto en el rendimiento de su juego, especialmente cuando se usan sprites con formas complejas..

Para nuestro juego, ya que solo estamos usando un sprite de coche y nuestra escena no es particularmente compleja, vamos a usar el Máscara alfa tipo de cuerpo. Es no Se recomienda utilizar este tipo de cuerpo para todos los sprites en tu escena, aunque sea el más preciso. Cuando selecciona esta opción del menú desplegable, debería ver una línea azul clara alrededor del borde del automóvil..

Es importante tener en cuenta que otros tipos de cuerpos físicos pueden crearse programáticamente, como cuerpos de CGPath Objetos así como círculos y rectángulos de tamaños personalizados..

Todavía en el Inspector de atributos, Ahora debería ver más opciones disponibles para usted en el Definición de física sección. La única propiedad que necesitamos cambiar es la Máscara de contacto. Cambia esto a un valor de 1.

Con el cuerpo físico del auto configurado, podemos comenzar a poner algunos obstáculos en el juego para chocar con el auto.

3. Detectando colisiones

Antes de implementar los métodos de la SKPhysicsContactDelegate Protocolo, hay que añadir algunos obstáculos para que el coche evite. Para hacer esto, vamos a generar un nuevo obstáculo cada tres segundos frente al auto y colocaremos el obstáculo en un carril aleatorio..

Abierto MainScene.swift y agregar una declaración de importación para el Kit de juego marco para que podamos usar los generadores de números aleatorios proporcionados por GameplayKit.

Importar GameplayKit

A continuación, agregue el siguiente método a la MainScene clase:

func spawnObstacle (timer: NSTimer) if player.hidden timer.invalidate () return let spriteGenerator = GKShuffledDistribution (lowestValue: 1, el valor más alto: 2) let obstacle = SKSpriteNode (imageNamed: (Obstacle \ (spriteGenerator) ") obstacle.xScale = 0.3 obstacle.yScale = 0.3 let physicsBody = SKPhysicsBody (circleOfRadius: 15) physicsBody.contactTestBitMask = caso de que se trata, caso de que se trate. diferencia = CGFloat (85.0) var x: CGFloat = 0 let laneGenerator = GKShuffledDistribution (LowestValue: 1, el valor más alto: 3) cambiar laneGenerator.nextInt () caso 1: x = centro - diferencia caso 2: x = centro caso 3: x = centro + diferencia por defecto: fatalError ("Número fuera de [1, 3] generado") obstacle.position = CGPoint (x: x, y: (player.position.y + 800)) addChild (obstáculo)

Este método se invoca cada tres segundos. Vamos a desglosarlo para ver qué está pasando. Si el nodo del jugador actual está oculto, lo cual es cierto cuando el auto golpea un obstáculo, entonces invalidamos el cronómetro y dejamos de generar obstáculos..

Obtenemos un número aleatorio entre 1 y 2, y use esto para crear un nodo de sprite con uno de los dos sprites de obstáculo disponibles en el proyecto. Luego, cambiamos la escala del obstáculo para que sean lo suficientemente pequeños para que el automóvil pueda maniobrar alrededor..

A continuación, creamos un cuerpo de física para este nuevo obstáculo con un círculo con un radio de 15 y una máscara de contacto de 0x00000001. Nosotros fijamos fijado a cierto y permiteRotación a falso Para que el obstáculo permanezca en su lugar y no se mueva. El cuerpo de física es entonces asignado al obstáculo..

Generamos otro número aleatorio entre 1 y 3 para determinar en qué carril debe colocarse el obstáculo y darle al obstáculo su posición calculada, agregándolo a la escena.

Tenga en cuenta que la diferencia horizontal, 85, utilizado en spawnObstacle (_ :) Es diferente al usado cuando se mueve el carro., 70. Hacemos esto para permitir un poco más de espacio para que el automóvil se mueva entre los obstáculos.

Con la lógica de generación de obstáculos en su lugar, podemos agregar el siguiente código al final de la didMoveToView (_ :) método.

override func didMoveToView (ver: SKView) … let timer = NSTimer (timeInterval: 3.0, target: self, selector: #selector (spawnInObstacle (_ :)), userInfo: nil, repeats: true) NSRunLoop.mainRunLoop (). addTimer (timer, forMode: NSRunLoopCommonModes) let camera = SKCameraNode () self.camera = camera camera.position = CGPoint (x: center, y: player.position.y + 200) let moveForward = SKAction.moveBy (CGVectorMake (0, 100 ), duración: 1.0) camera.runAction (SKAction.repeatActionForever (moveForward)) addChild (camera) player.xScale = 0.4; player.yScale = 0.4 // Hace que el auto sea más pequeño para que quepa mejor entre los obstáculos

Creamos un temporizador para ejecutar. spawnObstacle (_ :) cada tres segundos y agregarlo al bucle de ejecución principal. También creamos un SKCameraNode actuar como la cámara para la escena y asignarla a la cámara Propiedad de la escena. Esto hace que la escena se renderice desde el punto de vista de este nodo de cámara. Tenga en cuenta que la cámara está centrada horizontalmente y ligeramente por encima del coche.

También agregamos una acción idéntica a la cámara para que permanezca alineada con el automóvil. Añadimos la cámara como un nodo secundario de la escena como cualquier otro nodo regular. Por último, pero no menos importante, reducimos el tamaño del auto para que pueda caber entre los obstáculos un poco mejor..

La última parte para la detección de colisiones es implementar uno de los SKPhysicsContactDelegate Métodos de protocolo. En nuestro caso, vamos a implementar el didBeginContact (_ :) Método, ya que queremos ser notificados tan pronto como el automóvil golpee un obstáculo. Agregue el siguiente método a la MainScene clase.

func didBeginContact (contact: SKPhysicsContact) if contact.bodyA.node == player || contact.bodyB.node == player player.hidden = true player.removeAllActions () camera? .removeAllActions ()

Se puede ver que este método tiene uno. SKPhysicsContact parámetro pasado a él. Este objeto contiene información clave sobre la colisión, incluida su dirección, impulso y los objetos involucrados..

En este código, solo nos preocupan los nodos involucrados en la colisión. Verificamos si alguno de ellos era el auto y, si es verdad, escondemos el auto en la escena y terminamos el movimiento del auto y la cámara..

Crea y ejecuta tu aplicación y juega tu juego. Verás que, cuando te topas con un obstáculo, el coche desaparece y la escena deja de moverse..

Conclusión

Ahora debería saber cómo configurar cuerpos de física para los nodos de su escena y usarlos para simular una física realista. También debe sentirse cómodo configurando la detección de colisiones en su escena definiendo un delegado de contacto y asignando máscaras de bits de prueba de contacto para los nodos que espera que colisionen entre sí.

En el siguiente tutorial de SpriteKit From Scratch, veremos la funcionalidad visual más avanzada en SpriteKit, que incluye sistemas de partículas, luces y filtros..

Como siempre, asegúrese de dejar sus comentarios y sugerencias en los comentarios a continuación.