swift sprite-kit gameplay-kit bit-masks swift-dictionary

Cómo interpretar este código de ejemplo de Swift SpriteKit de un sistema de máscara de cuerpo de física



sprite-kit gameplay-kit (1)

collisionMask es la propiedad calculada que devuelve un valor de UInt32 que se puede usar como una máscara de bits de colisión del cuerpo de física. Es más fácil entender cómo funciona esta propiedad calculada si se divide en sus partes funcionales.

Pero primero, agreguemos una matriz de los objetos RPColliderType que el PlayerBot debería colisionar con el diccionario de colisiones definedCollisions :

RPColliderType.definedCollisions[.PlayerBot] = [.Obstacle, .TaskBot]

En este punto, el diccionario definedCollisions contiene un solo elemento con PlayerBot y [.Obstacle, .TaskBot] como clave y valor, respectivamente. Piense en esto ya que las categorías que pueden colisionar con un PlayerBot son Obstacle y TaskBot .

Ahora podemos usar .PlayerBot para recuperar el valor (es decir, la matriz) del diccionario:

let array = RPColliderType.definedCollisions[.PlayerBot]

Como collisionMask se define en RPColliderType , self se usa como la clave del diccionario. Además, array es opcional ya que un valor correspondiente a la clave puede no existir en el diccionario.

El código combina la matriz de objetos RPColliderType en un solo objeto RPColliderType utilizando el método de reduce . reduce toma dos argumentos: un valor inicial (con el mismo tipo que los elementos de la matriz) y una función (o cierre) que toma un valor como argumento y devuelve un valor. En este caso, el valor inicial es un nuevo objeto RPColliderType y el argumento del cierre y el valor devuelto son también objetos RPColliderType :

array?.reduce(RPColliderType(), aFunction)

El código de Apple usa un cierre posterior en lugar de pasar una función para reduce . De los documentos,

Si necesita pasar una expresión de cierre a una función como argumento final de la función y la expresión de cierre es larga, puede ser útil escribirla como un cierre final. Un cierre posterior es una expresión de cierre que se escribe fuera (y después) de los paréntesis de la llamada de función que admite.

reduce itera sobre la matriz y llama al cierre con el valor inicial y cada elemento de la matriz como argumentos y el valor devuelto se utiliza como el valor inicial para la siguiente iteración:

let mask = array?.reduce(RPColliderType()) { initial, colliderType in return initial.union(colliderType) }

donde initial mantiene la unión intermedia de los elementos de la matriz RPColliderType y RPColliderType es el elemento actual de la array .

En este punto, la mask es un objeto RPColliderType que podemos convertir a un UInt32 con

mask?.rawValue

que es el valor devuelto de la propiedad calculada de collisionMask .

Estaba echando un vistazo profundo al código de ejemplo Apples SpriteKit & GameplayKit y encontré un proyecto llamado ''DemoBots'' escrito en Swift. Hay algunos conceptos muy interesantes utilizados en los proyectos que quería adaptar a mis proyectos.

Ya estaba trabajando encapsulando el manejo de colisiones en una clase de manejador, algo que es muy similar a la forma en que se manejan las colisiones en ese código de ejemplo.

En este proyecto encontré el siguiente código para una estructura llamada RPColliderType :

struct RPColliderType: OptionSetType, Hashable, CustomDebugStringConvertible { // MARK: Static properties /// A dictionary to specify which `ColliderType`s should be notified of contacts with other `ColliderType`s. static var requestedContactNotifications = [RPColliderType: [RPColliderType]]() /// A dictionary of which `ColliderType`s should collide with other `ColliderType`s. static var definedCollisions = [RPColliderType: [RPColliderType]]() // MARK: Properties let rawValue: UInt32 // MARK: Options static var Obstacle: RPColliderType { return self.init(rawValue: 1 << 0) } static var PlayerBot: RPColliderType { return self.init(rawValue: 1 << 1) } static var TaskBot: RPColliderType { return self.init(rawValue: 1 << 2) } // MARK: Hashable var hashValue: Int { return Int(rawValue) } // MARK: SpriteKit Physics Convenience /// A value that can be assigned to a ''SKPhysicsBody`''s `categoryMask` property. var categoryMask: UInt32 { return rawValue } /// A value that can be assigned to a ''SKPhysicsBody`''s `collisionMask` property. var collisionMask: UInt32 { // Combine all of the collision requests for this type using a bitwise or. let mask = RPColliderType.definedCollisions[self]?.reduce(RPColliderType()) { initial, colliderType in return initial.union(colliderType) } // Provide the rawValue of the resulting mask or 0 (so the object doesn''t collide with anything). return mask?.rawValue ?? 0 } /// A value that can be assigned to a ''SKPhysicsBody`''s `contactMask` property. var contactMask: UInt32 { // Combine all of the contact requests for this type using a bitwise or. let mask = RPColliderType.requestedContactNotifications[self]?.reduce(RPColliderType()) { initial, colliderType in return initial.union(colliderType) } // Provide the rawValue of the resulting mask or 0 (so the object doesn''t need contact callbacks). return mask?.rawValue ?? 0 } // MARK: ContactNotifiableType Convenience /** Returns `true` if the `ContactNotifiableType` associated with this `ColliderType` should be notified of contact with the passed `ColliderType`. */ func notifyOnContactWithColliderType(colliderType: RPColliderType) -> Bool { if let requestedContacts = RPColliderType.requestedContactNotifications[self] { return requestedContacts.contains(colliderType) } return false } }

Esta estructura se usa cada vez que establece la .collisionBitmask / .contactBitmask / .categoryBitmask de un SKPhysicsBody siguiente manera: (Lo he implementado usando la guía de diseño de componentes y entidades)

class RPPhysicsComponent: GKComponent { var physicsBody: SKPhysicsBody init(physicsBody: SKPhysicsBody, colliderType: RPColliderType) { self.physicsBody = physicsBody self.physicsBody.categoryBitMask = colliderType.categoryMask self.physicsBody.collisionBitMask = colliderType.collisionMask self.physicsBody.contactTestBitMask = colliderType.contactMask } }

Hasta aquí todo bien. Procedente de Objective-C mi problema es que no entiendo completamente qué hacen las siguientes líneas de código de RPColliderType Struct:

/// A value that can be assigned to a ''SKPhysicsBody`''s `collisionMask` property. var collisionMask: UInt32 { // Combine all of the collision requests for this type using a bitwise or. let mask = RPColliderType.definedCollisions[self]?.reduce(RPColliderType()) { initial, colliderType in return initial.union(colliderType) } // Provide the rawValue of the resulting mask or 0 (so the object doesn''t collide with anything). return mask?.rawValue ?? 0 }

¿Eso significa que cada vez que llamo a esa propiedad calculada (así se llaman en forma rápida, ¿verdad?) - Lo hago cuando la asigno a un SKPhysicsBody - lo agrega a esos diccionarios de clase estáticos. Pero tengo problemas para interpretar los comandos '' mask '' / '' reduce '' / '' union ''.

¿Qué hace realmente eso?