2016-01-27 7 views
5

Stavo dando uno sguardo approfondito a Mele SpriteKit & GameplayKit codice di esempio e ho trovato un Progetto chiamato 'DemoBots' scritto in Swift. Ci sono alcuni concetti molto interessanti usati in quei progetti che ho voluto adattare ai miei progetti.Come interpretare questo codice di esempio Swift SpriteKit di un corpo fisico sistema bitmask

Stavo già lavorando con l'incapsulamento della gestione delle collisioni in una classe di gestore qualcosa che è molto simile al modo in cui le collisioni vengono gestite in quel codice di esempio.

In questo progetto ho trovato il seguente codice per una struttura chiamata 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 
    } 
} 

Questa struct viene utilizzato ogni volta che si imposta la proprietà .collisionBitmask/.contactBitmask/.categoryBitmask di un SKPhysicsBody come questa: (ho implementato questo componente utilizzando guida alla progettazione & entità)

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

Fin qui tutto bene. Venendo da Objective-C il mio problema è che io non comprendere appieno che cosa quelle seguenti righe di codice fuori del RPColliderType Struct fanno:

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

Questo significa che ogni volta che lo chiamo calcolato (questo è ciò che essi sono chiamati in swift, right?) property - Lo faccio quando lo assegno ad un SKPhysicsBody - lo aggiunge a quei dizionari di classe statici. Ma ho un problema nell'interpretazione dei comandi 'mask'/'reduce'/'union'.

Che cosa fa veramente?

risposta

2

collisionMask è una proprietà calcolata che restituisce un valore UInt32 che può essere utilizzato come maschera di bit di collisione del corpo fisico. È più facile capire come funziona questa proprietà calcolata se è suddivisa in parti funzionali.

Ma prima, aggiungiamo un allineamento degli RPColliderType oggetti che il PlayerBot dovrebbe entrare in collisione con il dizionario definedCollisions:

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

A questo punto, il dizionario definedCollisions contiene un singolo elemento con PlayerBot e [.Obstacle, .TaskBot] come chiave e valore, rispettivamente. Pensa a questo in quanto le categorie che possono scontrarsi con un PlayerBot sono Obstacle e TaskBot.

Possiamo ora utilizzare .PlayerBot per recuperare il valore (cioè, l'array) dal dizionario:

let array = RPColliderType.definedCollisions[.PlayerBot] 

Da collisionMask viene definita nella RPColliderType, self viene utilizzato come il tasto dizionario. Inoltre, array è facoltativo poiché un valore corrispondente alla chiave potrebbe non esistere nel dizionario.

Il codice combina quindi la matrice di oggetti RPColliderType in un singolo oggetto RPColliderType utilizzando il metodo reduce. reduce accetta due argomenti: un valore iniziale (con lo stesso tipo degli elementi dell'array) e una funzione (o chiusura) che accetta un valore come argomento e restituisce un valore.In questo caso, il valore iniziale è un nuovo oggetto RPColliderType e l'argomento della chiusura e valore restituito sono anche RPColliderType oggetti:

array?.reduce(RPColliderType(), aFunction) 

codice di Apple usa una chiusura posteriore invece di passare una funzione reduce. Dalla documentazione,

Se è necessario passare un'espressione di chiusura per una funzione come argomento finale della funzione e l'espressione di chiusura è lungo, può essere utile scrivere come una chiusura finale, invece. Una chiusura finale è un'espressione di chiusura che è scritta al di fuori di (e dopo) le parentesi della chiamata della funzione che supporta.

reduce itera matrice e chiede la chiusura con il valore iniziale e ciascun elemento di matrice come argomenti e il valore restituito viene utilizzato come valore iniziale per la successiva iterazione:

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

dove initial mantiene l'unione intermedia degli elementi dell'array RPColliderType e colliderType è l'elemento corrente di array.

A questo punto, mask è un oggetto RPColliderType che possiamo convertire in UInt32 con

mask?.rawValue 

che è il valore restituito della proprietà calcolata collisionMask.

+0

Grazie per una spiegazione così dettagliata. Quasi un po 'come hai scritto quel pezzo di codice di esempio !? Però. Dalla mia comprensione questo è un pezzo di codice molto elegante, non è vero? –

+0

Prego. Il codice è un buon esempio di come usare 'OptionSetType' per gestire le maschere di bit di categoria, collisione e contatto. – 0x141E

Problemi correlati