How to interpret this example Swift SpriteKit physical body bitmask system

I carefully studied the sample examples of Apples SpriteKit and GameplayKit and found a project called DemoBots written in Swift. There are some very interesting concepts used in those projects that I would like to adapt in my projects.

I already worked with encapsulating conflict handling in a handler class, which is very similar to how collisions are handled in this code example.

In this project, I found the following code for a structure named 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` `categoryMask` property. var categoryMask: UInt32 { return rawValue } /// A value that can be assigned to a 'SKPhysicsBody` `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` `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 } } 

This structure is used every time you set the .collisionBitmask / .contactBitmask / .categoryBitmask for SKPhysicsBody as follows: (I implemented this using the component and entity design guide)

 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 } } 

So far so good. Coming from Objective-C, my problem is that I do not quite understand what the following lines of code from RPColliderType Struct do:

 /// A value that can be assigned to a 'SKPhysicsBody` `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 } 

Does this mean that every time I call this computed (what do they call in the fast, correct?) Property - I do this when I assign it to SKPhysicsBody - it adds this to these dictionaries of the static class. But I have a problem interpreting the commands' mask '/' reduce '/' union .

What really does this do?

+5
source share
1 answer

collisionMask is a computed property that returns a UInt32 value that can be used as a collision bit mask for a physical body. It is easier to understand how this computable property works if it is broken down into its functional parts.

But first add an array of RPColliderType objects that the PlayerBot column should encounter with the definedCollisions dictionary:

 RPColliderType.definedCollisions[.PlayerBot] = [.Obstacle, .TaskBot] 

At this point, the definedCollisions dictionary contains one element with PlayerBot and [.Obstacle, .TaskBot] as the key and value, respectively. Think about it, as the categories that may run into PlayerBot are Obstacle and TaskBot .

Now we can use .PlayerBot to retrieve the value (i.e. array) from the dictionary:

 let array = RPColliderType.definedCollisions[.PlayerBot] 

Since collisionMask defined in RPColliderType , self used as a dictionary key. In addition, array is optional, since the value corresponding to the key may not exist in the dictionary.

The code then combines the array of RPColliderType objects into a single RPColliderType object using the reduce method. reduce takes two arguments: the initial value (with the same type as the elements of the array), and the function (or closure) that takes the value as an argument and returns the value. In this case, the initial value is a new RPColliderType object, and the closure argument and return value are also RPColliderType objects:

 array?.reduce(RPColliderType(), aFunction) 

Apple code uses closing closure instead of passing a reduce function. From the documents

If you need to pass a closure expression to a function as a final argument function and the closure expression is long, it may be useful to write it as a closure closure instead. A final closure is a closure expression that is written outside (and after) the parentheses of the called function.

reduce iterates through the array and causes a closure with the initial value and each element of the array as arguments, and the return value is used as the initial value for the next iteration:

 let mask = array?.reduce(RPColliderType()) { initial, colliderType in return initial.union(colliderType) } 

where initial supports the intermediate union of the elements of the RPColliderType array, and colliderType current element of the array .

At this point, mask is an RPColliderType object that we can convert to UInt32 with

 mask?.rawValue 

which is the return value of the computed collisionMask property.

+2
source

Source: https://habr.com/ru/post/1241587/


All Articles