2016-01-27 17 views
5

Ich habe einen tiefen Blick in Apples SpriteKit & GameplayKit Beispielcode und fand ein Projekt namens "DemoBots" in Swift geschrieben. In diesen Projekten wurden einige sehr interessante Konzepte verwendet, die ich in meine Projekte integrieren wollte.Wie man diesen Swift SpriteKit Beispielcode eines Physikkörper Bitmaskensystems interpretiert

Ich arbeitete bereits mit der Kapselung der Kollisions-Behandlung in eine Handler-Klasse etwas, das sehr ähnlich zu der Art ist, wie Kollisionen in diesem Beispiel-Code behandelt werden.

In diesem Projekt fand ich den folgenden Code für eine Struktur RPColliderType genannt:

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

Diese Struktur verwendet wird, jedes Mal, wenn die .collisionBitmask/.contactBitmask/.categoryBitmask Eigenschaft eines SKPhysicsBody wie folgt festgelegt: (Ich habe umgesetzt diese mit Komponente & Einheit 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 weit so gut. Von Objective-C ist mein Problem, dass ich nicht vollständig verstehen, was die folgenden Codezeilen aus dem RPColliderType Struct tun:

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

das bedeutet, dass jedes Mal, wenn ich nenne das berechnet (das ist, was sie genannt werden in swift, richtig?) Eigentum - ich tue das, wenn ich es einer SKPhysicsBody zuweisen - es fügt diese zu diesen statischen Klassenwörterbüchern hinzu. Aber ich habe ein Problem bei der Interpretation der Befehle 'mask'/'reduce'/'union'.

Was macht das wirklich?

Antwort

2

collisionMask ist eine berechnete Eigenschaft, die einen Wert UInt32 zurückgibt, der als Kollisions-Bitmaske eines Physikkörpers verwendet werden kann. Es ist einfacher zu verstehen, wie diese berechnete Eigenschaft funktioniert, wenn sie in ihre funktionalen Teile zerlegt wird.

Aber lassen Sie uns zuerst ein Array der RPColliderType Objekte hinzufügen, die die PlayerBot mit zum definedCollisions Wörterbuch kollidieren sollte:

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

An diesem Punkt das definedCollisions Wörterbuch enthält ein einzelnes Element mit PlayerBot und [.Obstacle, .TaskBot] als die Schlüssel bzw. Wert. Stellen Sie sich Folgendes vor: Die Kategorien, die mit PlayerBot kollidieren können, sind Obstacle und TaskBot.

wir jetzt .PlayerBot verwenden, um den Wert (das heißt, das Array) aus dem Wörterbuch zu erhalten:

let array = RPColliderType.definedCollisions[.PlayerBot] 

Da collisionMask im RPColliderType definiert ist, wird als self Wörterbuch Schlüssel verwendet. Auch array ist optional, da ein dem Schlüssel entsprechender Wert möglicherweise nicht im Wörterbuch vorhanden ist.

Der Code kombiniert dann das Array von RPColliderType Objekte in einem einzigen RPColliderType Objekt mit der reduce Methode. reduce benötigt zwei Argumente: einen Anfangswert (mit dem gleichen Typ wie die Elemente des Arrays) und eine Funktion (oder einen Abschluss), die einen Wert als Argument akzeptiert und einen Wert zurückgibt.In diesem Fall ist der Anfangswert ein neues RPColliderType Objekt und das Argument der Schließung und Rückgabewert sind auch RPColliderType Objekte:

array?.reduce(RPColliderType(), aFunction) 

Apples Code verwendet einen hinteren Verschluss stattdessen eine Funktion reduce übergeben. Aus den Dokumenten,

Wenn Sie eine Schließung Ausdruck eine Funktion als die Funktion der letzten Argument und die Schließung Ausdruck passieren lang ist, kann es nützlich sein, ihn als Schlepp Verschluss statt zu schreiben. Ein abschließender Abschluss ist ein Abschlussausdruck, der außerhalb (und nach) den Klammern des von ihm unterstützten Funktionsaufrufs geschrieben ist.

reduce iteriert über dem Array und ruft den Verschluß mit dem Anfangswert und jedes Array-Element als Argumente und den zurückgegebenen Wert wird als Anfangswert für die nächste Iteration verwendet:

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

wo initial hält Die Zwischenverbindung der RPColliderType Array-Elemente und colliderType ist das aktuelle Element von array.

An diesem Punkt ist ein maskRPColliderType Objekt, das wir zu einem UInt32 mit

mask?.rawValue 

, die dem zurückgegebenen Wert der berechneten collisionMask Eigenschaft ist umwandeln können.

+0

Vielen Dank für solch eine detaillierte Erklärung. Fast ein bisschen wie du dieses Stück Beispielcode geschrieben hast !? Jedoch. Aus meiner Sicht ist dies ein sehr elegantes Stück Code, nicht wahr? –

+0

Gern geschehen. Der Code ist ein gutes Beispiel für die Verwendung von 'OptionSetType' zum Verwalten von Kategorien-, Kollisions und Kontaktbitmasken. – 0x141E

Verwandte Themen