2016-01-27 8 views
5

Przyjrzałem się uważnie przykładowi Apples SpriteKit & GameplayKit i znalazłem projekt o nazwie "DemoBots" napisany w języku Swift. Jest kilka bardzo interesujących pojęć używanych w tych projektach, które chciałem dostosować do moich projektów.Jak interpretować ten przykładowy kod Swift SpriteKit systemu maski bitów ciała fizycznego

Już pracowałem z enkapsulacją obsługi kolizji w klasie obsługi czegoś, co jest bardzo podobne do sposobu obsługi kolizji w tym przykładowym kodzie.

W tym projekcie znalazłem następujący kod dla struktury zwane 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 
    } 
} 

Ta struktura jest używana za każdym razem ustawić właściwość danego SKPhysicsBody jak ta .collisionBitmask/.contactBitmask/.categoryBitmask: (i wdrożyły przy użyciu instrukcji projektowania elementu &

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

Jak dotąd tak dobrze. Jadąc z Objective-C, moim problemem jest to, że nie w pełni zrozumieć, co te następujące linie kodu wyjścia z RPColliderType Struct zrobić:

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

Czy to oznacza, że ​​za każdym razem zadzwonię że obliczane (to, co nazywa się w szybkiej, prawej?) właściwości - robię to, gdy przypisuję ją do SKPhysicsBody - dodaje to do tych statycznych słowników klas. Ale mam problem z interpretacją poleceń "mask"/"reduce"/"union".

Co tak naprawdę robi?

Odpowiedz

2

collisionMask to wyliczona właściwość, która zwraca wartość UInt32, która może być używana jako maska ​​bitowa kolizji ciała fizycznego. Łatwiej zrozumieć, jak działa ta obliczona właściwość, jeśli jest podzielona na części funkcjonalne.

Ale najpierw dodajmy tablicę RPColliderType obiektów, które PlayerBot powinna kolidować ze słownika definedCollisions:

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

W tym momencie, słownik definedCollisions zawiera pojedynczy element z PlayerBot i [.Obstacle, .TaskBot] jako klucz i wartość, odpowiednio. Pomyśl o tym, jako kategorii, które mogą kolidować z PlayerBotObstacle i TaskBot.

Możemy teraz używać .PlayerBot pobrać wartość (tj array) ze słownika:

let array = RPColliderType.definedCollisions[.PlayerBot] 

Od collisionMask jest zdefiniowana w RPColliderType, self służy jako klucz słownika. Ponadto, array jest opcjonalne, ponieważ wartość odpowiadająca kluczowi może nie istnieć w słowniku.

Kod następnie łączy tablicę obiektów RPColliderType w pojedynczy obiekt RPColliderType przy użyciu metody reduce. reduce przyjmuje dwa argumenty: wartość początkową (o tym samym typie co elementy tablicy) i funkcję (lub zamknięcie), która przyjmuje wartość jako argument i zwraca wartość.W tym przypadku wartość początkowa jest to także nowy obiekt i RPColliderType argumentem zamknięcia i odsyłane wartość RPColliderType obiekty:

array?.reduce(RPColliderType(), aFunction) 

kod Apple wykorzystuje zamknięcie spływu zamiast przekazując funkcję reduce. Od docs,

Jeśli trzeba przejść wyrażenie zamykającego do funkcji jako ostatecznego argumentu funkcja i wyrażenia zamknięcia jest długa, może być przydatny do napisania go jako zamknięcie zamiast spływu. Zamknięcie końcowe to wyrażenie zamknięcia, które jest zapisane poza (i po) nawiasami funkcji, którą obsługuje.

reduce iteruje tablicy i wywołuje zamknięcie z wartością początkową, a każdy element tablicy jako argumenty i zwróconego wartość jest wykorzystywana jako wartość początkowa dla następnej iteracji:

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

gdzie initial utrzymuje związek pośredni elementów tablicy RPColliderType i colliderType jest bieżącym elementem array.

W tym momencie mask jest obiektem RPColliderType że możemy konwertować do UInt32 z

mask?.rawValue 

która jest zwracana wartość obliczonego nieruchomości w collisionMask.

+0

Dziękuję za tak szczegółowe wyjaśnienie. Prawie tak jak napisałeś ten przykładowy kod !? Jednak. Z mojego rozumienia tego jest bardzo elegancki kawałek kodu, prawda? –

+0

Nie ma za co. Kod jest dobrym przykładem użycia 'OptionSetType' do zarządzania kategoriami, kolizjami i maskami bitów kontaktu. – 0x141E

Powiązane problemy