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?