Как интерпретировать этот пример кода Swift SpriteKit системы битовой маски физического тела
Я внимательно изучил пример кода Apples SpriteKit и GameplayKit и нашел проект под названием "DemoBots", написанный на Swift. В этих проектах используются некоторые очень интересные концепции, которые я хотел адаптировать в своих проектах.
Я уже работал с инкапсуляцией обработки коллизий в класс-обработчик, что очень похоже на способ обработки коллизий в этом примере кода.
В этом проекте я нашел следующий код для структуры под названием 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
}
}
Эта структура используется каждый раз, когда вы устанавливаете .collisionBitmask
/ .contactBitmask
/ .categoryBitmask
свойство SKPhysicsBody
вот так: (Я реализовал это, используя руководство по разработке компонентов и сущностей)
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
}
}
Все идет нормально. Исходя из Objective-C, моя проблема в том, что я не до конца понимаю, что делают следующие строки кода из структуры RPColliderType:
/// 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
}
Означает ли это, что каждый раз, когда я вызываю это вычисляемое (это то, что они называются в swift, верно?) Свойство - я делаю это, когда назначаю его SKPhysicsBody
- это добавляет это к тем статическим словарям классов. Но у меня проблема с интерпретацией этогоmask
'/'reduce
'/'union
Команды.
Что на самом деле это делает?
1 ответ
collisionMask
вычисляемое свойство, которое возвращает UInt32
значение, которое можно использовать в качестве битовой маски столкновения физического тела. Проще понять, как работает это вычисляемое свойство, если оно разбито на функциональные части.
Но сначала давайте добавим массив RPColliderType
объекты, которые PlayerBot
должен столкнуться с definedCollisions
толковый словарь:
RPColliderType.definedCollisions[.PlayerBot] = [.Obstacle, .TaskBot]
На данный момент, definedCollisions
словарь содержит один элемент с PlayerBot
а также [.Obstacle, .TaskBot]
как ключ и значение соответственно. Думайте об этом как о категориях, которые могут столкнуться с PlayerBot
являются Obstacle
а также TaskBot
,
Теперь мы можем использовать .PlayerBot
чтобы получить значение (т. е. массив) из словаря:
let array = RPColliderType.definedCollisions[.PlayerBot]
поскольку collisionMask
определяется в RPColliderType
, self
используется в качестве ключа словаря. Также, array
является необязательным, поскольку значение, соответствующее ключу, может отсутствовать в словаре.
Затем код объединяет массив RPColliderType
объекты в единый RPColliderType
объект с помощью reduce
метод. reduce
принимает два аргумента: начальное значение (того же типа, что и элементы массива) и функцию (или замыкание), которая принимает значение в качестве аргумента и возвращает значение. В этом случае начальное значение является новым RPColliderType
объект и аргумент замыкания и возвращаемое значение также RPColliderType
объекты:
array?.reduce(RPColliderType(), aFunction)
Код Apple использует замыкающее замыкание вместо передачи функции reduce
, Из документов,
Если вам нужно передать выражение замыкания в функцию в качестве последнего аргумента функции, а выражение замыкания является длинным, вместо этого может быть полезно записать его в качестве завершающего замыкания. Завершающее замыкание - это закрывающее выражение, которое пишется вне (и после) скобок вызова функции, который он поддерживает.
reduce
перебирает массив и вызывает замыкание с начальным значением и каждым элементом массива в качестве аргументов, а возвращаемое значение используется в качестве начального значения для следующей итерации:
let mask = array?.reduce(RPColliderType()) {
initial, colliderType in
return initial.union(colliderType)
}
где initial
сохраняет промежуточный союз RPColliderType
элементы массива и colliderType
текущий элемент array
,
С этой точки зрения, mask
является RPColliderType
объект, который мы можем преобразовать в UInt32
с
mask?.rawValue
который является возвращаемым значением collisionMask
компьютерная собственность.