2016-01-27 8 views
5

私はApples SpriteKit & GameplayKitのサンプルコードをよく見て、Swiftで書かれた 'DemoBots'というプロジェクトを見つけました。私が自分のプロジェクトに適応させたいと思っていたプロジェクトには、非常に興味深い概念がいくつかあります。このSwift SpriteKitのフィジックスボディビットマスクシステムのコード例

私はすでに衝突ハンドリングをハンドラクラスにカプセル化しています。これは、そのコード例で衝突が処理される方法に非常に似ています。この構造体は、あなたがこのようなSKPhysicsBody.collisionBitmask/.contactBitmask/.categoryBitmaskプロパティを設定するたびに使用されて

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

:このプロジェクトで

私は構造体の次のコードは、 RPColliderTypeと呼ばれた(私が実施していますこのコンポーネント&実体設計ガイドを使用して)これまで

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

とても良いです。それは私が呼ぶたびに計算されたことをことを意味してい

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

(つまり、彼らは呼ばれているものです:私の問題は、私は完全にRPColliderType構造体のうち、コードのものを次の行は何をすべきか理解していないということであるObjective-Cのから来ます私はこれをSKPhysicsBodyに割り当てると、静的なクラス辞書に追加されます。しかし、私は 'mask'/'reduce'/'union'のコマンドを解釈する際に問題があります。

本当にそれは何ですか?

答えて

2

collisionMaskは、物理ボディの衝突ビットマスクとして使用できるUInt32の値を返すcalculateプロパティです。この計算されたプロパティが機能的な部分に分解されている場合、この計算されたプロパティがどのように機能するかを理解することは簡単です。

しかし、最初のはPlayerBotdefinedCollisions辞書に衝突しなければならないRPColliderTypeオブジェクトの配列を追加してみましょう。この時点で

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

を、definedCollisions辞書はPlayerBot[.Obstacle, .TaskBot]などで単一の項目が含まれていますキーと値をそれぞれ示します。 PlayerBotと衝突する可能性のあるカテゴリは、ObstacleTaskBotです。

我々は今辞書からの値(即ち、配列)を取得するために.PlayerBotを使用することができます:collisionMaskので

let array = RPColliderType.definedCollisions[.PlayerBot] 

RPColliderTypeで定義され、selfは、辞書のキーとして使用されます。また、arrayは、キーに対応する値が辞書に存在しない可能性があるため、オプションです。

コードはRPColliderTypeオブジェクトの配列をreduceメソッドを使用して単一のRPColliderTypeオブジェクトに結合します。 reduceは、初期値(配列の要素と同じ型)と、引数として値をとり、値を返す関数(またはクロージャ)の2つの引数をとります。この場合、初期値は新しいRPColliderTypeオブジェクトやクロージャの引数であり、値を返さもRPColliderTypeオブジェクトです:

array?.reduce(RPColliderType(), aFunction) 

Appleのコードではなくreduceに関数を渡すの末尾の閉鎖を使用しています。ドキュメントから、

あなたは、関数の最後の引数としての機能と が長い閉鎖式に閉鎖式を渡す必要がある場合は

、代わりに、末尾のクロージャとしてそれを書くために役立つことがあります。末尾のクロージャは、それがサポートする関数呼び出しのカッコの外側(および後)に書かれたクロージャ式です( )。

reduce配列を反復処理し、引数として初期値と各配列要素とクロージャを呼び出し、戻り値は、次の反復の初期値として使用されている:

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

RPColliderType配列要素とcolliderTypeの中間和は、現在の要素のarrayです。この時点で

maskは、我々はcollisionMask計算プロパティの戻り値である

mask?.rawValue 

UInt32に変換することができRPColliderTypeオブジェクトです。

+0

このような詳細な説明ありがとうございます。ほとんどあなたのようなビットは、このコードを書いています!しかしながら。私の理解から、これは非常にエレガントなコードですね。 –

+0

ようこそ。このコードは、 'OptionSetType'を使用してカテゴリ、衝突、およびコンタクトビットマスクを管理する方法の良い例です。 – 0x141E

関連する問題