如何解释这个物理体位掩码系统的Swift SpriteKit示例代码

Sim*_*per 5 bit-masks sprite-kit swift swift-dictionary gameplay-kit

我正在深入研究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
    }
}
Run Code Online (Sandbox Code Playgroud)

每次设置这样的.collisionBitmask/ .contactBitmask/ .categoryBitmaskproperty 时都会使用这个结构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
    }
}
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.来自Objective-C我的问题是我不完全理解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
}
Run Code Online (Sandbox Code Playgroud)

这是否意味着每次我调用计算(这是他们在swift中调用的,对吧?)属性 - 当我将它分配给它时我会这样做SKPhysicsBody- 它将它添加到那些静态类字典中.但我在解释' mask'/' reduce'/' union'命令时遇到问题.

这到底是做什么的?

0x1*_*41E 2

collisionMask是计算属性,它返回一个UInt32可用作物理体碰撞位掩码的值。如果将这个计算属性分解为其功能部分,就更容易理解它是如何工作的。

\n\n

但首先,让我们将应该碰撞的RPColliderType对象数组添加到字典中:PlayerBotdefinedCollisions

\n\n
RPColliderType.definedCollisions[.PlayerBot] = [.Obstacle, .TaskBot]\n
Run Code Online (Sandbox Code Playgroud)\n\n

此时,definedCollisions字典包含一个项目,其中PlayerBot[.Obstacle, .TaskBot]分别作为键和值。将此视为可以与 a 和 发生冲突PlayerBotObstacle类别TaskBot

\n\n

我们现在可以用来.PlayerBot从字典中检索值(即数组):

\n\n
let array = RPColliderType.definedCollisions[.PlayerBot]\n
Run Code Online (Sandbox Code Playgroud)\n\n

由于collisionMask是在 中定义的RPColliderTypeself因此用作字典键。此外,array是可选的,因为与该键对应的值可能不存在于字典中。

\n\n

然后,代码使用该方法将对象数组组合RPColliderType成单个对象。接受两个参数:一个初始值(与数组元素的类型相同)和一个将值作为参数并返回值的函数(或闭包)。在这种情况下,初始值是一个新对象,闭包的参数和返回值也是对象:RPColliderTypereducereduceRPColliderTypeRPColliderType

\n\n
array?.reduce(RPColliderType(), aFunction)\n
Run Code Online (Sandbox Code Playgroud)\n\n

Apple 的代码使用尾随闭包而不是将函数传递给reduce. 从文档中,

\n\n
\n

如果需要将闭包表达式作为函数\xe2\x80\x99s 最终参数传递给函数,并且闭包表达式\n 很长,则将其写为尾随闭包可能会很有用。尾随闭包是一个闭包表达式,\n 写在它支持的函数调用的括号之外(及其之后)。

\n
\n\n

reduce迭代数组并使用初始值和每个数组元素作为参数调用闭包,并且返回的值用作下一次迭代的初始值:

\n\n
let mask = array?.reduce(RPColliderType()) {\n    initial, colliderType in\n    return initial.union(colliderType)\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

其中initial保留数组元素的中间并集RPColliderType,并且colliderType是 的当前元素array

\n\n

此时,mask是一个RPColliderType我们可以转换为 a 的UInt32对象

\n\n
mask?.rawValue\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是计算属性的返回值collisionMask

\n