如何解释这个 Swift 物理体位掩码系统的 SpriteKit 示例代码
How to interpret this Swift SpriteKit example code of a physics body bitmask system
我深入研究了 Apples SpriteKit 和 GameplayKit 示例代码,发现了一个用 Swift 编写的名为 'DemoBots' 的项目。该项目中使用了一些非常有趣的概念,我想将它们应用到我的项目中。
我已经在将碰撞处理封装到处理程序中 -class 这与示例代码中处理碰撞的方式非常相似。
在这个项目中,我找到了名为 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
}
}
每次设置 SKPhysicsBody
的 .collisionBitmask
/ .contactBitmask
/ .categoryBitmask
属性 时都会使用此结构:(我已经实现了这使用组件和实体设计指南)
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 中的调用,对吗?)属性 - 我在将它分配给 [=18= 时这样做] - 它将此添加到那些静态 class 词典中。但是我在解释“mask
”/“reduce
”/“union
”命令时遇到问题。
这到底有什么作用?
collisionMask
计算 属性 returns 一个 UInt32
值,可以用作物理体的碰撞位掩码。如果将此计算的 属性 分解为功能部分,则更容易理解其工作原理。
但首先,让我们将 PlayerBot
应该与之碰撞的 RPColliderType
对象的数组添加到 definedCollisions
字典中:
RPColliderType.definedCollisions[.PlayerBot] = [.Obstacle, .TaskBot]
此时,definedCollisions
字典包含一个项目,其中 PlayerBot
和 [.Obstacle, .TaskBot]
分别作为键和值。将此视为可能与 PlayerBot
冲突的类别是 Obstacle
和 TaskBot
.
我们现在可以使用 .PlayerBot
从字典中检索值(即数组):
let array = RPColliderType.definedCollisions[.PlayerBot]
由于 collisionMask
定义在 RPColliderType
中,因此 self
用作字典键。此外,array
是可选的,因为字典中可能不存在对应于该键的值。
代码然后使用 reduce
方法将 RPColliderType
个对象的数组合并为一个 RPColliderType
对象。 reduce
接受两个参数:一个初始值(与数组元素具有相同的类型)和一个函数(或闭包),该函数(或闭包)接受一个值作为参数和 returns 一个值。在这种情况下,初始值是一个新的 RPColliderType
对象,闭包的参数和返回值也是 RPColliderType
个对象:
array?.reduce(RPColliderType(), aFunction)
Apple 的代码使用尾随闭包而不是将函数传递给 reduce
。从文档中,
If you need to pass a closure expression to a function as the function’s final argument and the closure expression
is long, it can be useful to write it as a trailing closure instead. A trailing closure is a closure expression that is
written outside of (and after) the parentheses of the function call it supports.
reduce
遍历数组并以初始值和每个数组元素作为参数调用闭包,返回值用作下一次迭代的初始值:
let mask = array?.reduce(RPColliderType()) {
initial, colliderType in
return initial.union(colliderType)
}
其中 initial
保留 RPColliderType
数组元素的中间并集,colliderType
是 array
的当前元素。
此时,mask
是一个 RPColliderType
对象,我们可以使用
将其转换为 UInt32
mask?.rawValue
这是 collisionMask
计算的 属性 的返回值。
我深入研究了 Apples SpriteKit 和 GameplayKit 示例代码,发现了一个用 Swift 编写的名为 'DemoBots' 的项目。该项目中使用了一些非常有趣的概念,我想将它们应用到我的项目中。
我已经在将碰撞处理封装到处理程序中 -class 这与示例代码中处理碰撞的方式非常相似。
在这个项目中,我找到了名为 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
}
}
每次设置 SKPhysicsBody
的 .collisionBitmask
/ .contactBitmask
/ .categoryBitmask
属性 时都会使用此结构:(我已经实现了这使用组件和实体设计指南)
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 中的调用,对吗?)属性 - 我在将它分配给 [=18= 时这样做] - 它将此添加到那些静态 class 词典中。但是我在解释“mask
”/“reduce
”/“union
”命令时遇到问题。
这到底有什么作用?
collisionMask
计算 属性 returns 一个 UInt32
值,可以用作物理体的碰撞位掩码。如果将此计算的 属性 分解为功能部分,则更容易理解其工作原理。
但首先,让我们将 PlayerBot
应该与之碰撞的 RPColliderType
对象的数组添加到 definedCollisions
字典中:
RPColliderType.definedCollisions[.PlayerBot] = [.Obstacle, .TaskBot]
此时,definedCollisions
字典包含一个项目,其中 PlayerBot
和 [.Obstacle, .TaskBot]
分别作为键和值。将此视为可能与 PlayerBot
冲突的类别是 Obstacle
和 TaskBot
.
我们现在可以使用 .PlayerBot
从字典中检索值(即数组):
let array = RPColliderType.definedCollisions[.PlayerBot]
由于 collisionMask
定义在 RPColliderType
中,因此 self
用作字典键。此外,array
是可选的,因为字典中可能不存在对应于该键的值。
代码然后使用 reduce
方法将 RPColliderType
个对象的数组合并为一个 RPColliderType
对象。 reduce
接受两个参数:一个初始值(与数组元素具有相同的类型)和一个函数(或闭包),该函数(或闭包)接受一个值作为参数和 returns 一个值。在这种情况下,初始值是一个新的 RPColliderType
对象,闭包的参数和返回值也是 RPColliderType
个对象:
array?.reduce(RPColliderType(), aFunction)
Apple 的代码使用尾随闭包而不是将函数传递给 reduce
。从文档中,
If you need to pass a closure expression to a function as the function’s final argument and the closure expression is long, it can be useful to write it as a trailing closure instead. A trailing closure is a closure expression that is written outside of (and after) the parentheses of the function call it supports.
reduce
遍历数组并以初始值和每个数组元素作为参数调用闭包,返回值用作下一次迭代的初始值:
let mask = array?.reduce(RPColliderType()) {
initial, colliderType in
return initial.union(colliderType)
}
其中 initial
保留 RPColliderType
数组元素的中间并集,colliderType
是 array
的当前元素。
此时,mask
是一个 RPColliderType
对象,我们可以使用
UInt32
mask?.rawValue
这是 collisionMask
计算的 属性 的返回值。