Как интерпретировать этот пример кода 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 компьютерная собственность.

Другие вопросы по тегам