サーチ…


備考

スプライトキットの衝突と接触イベント処理の決定要因は、相互作用するオブジェクトタイプごとにcategoryBitMask contactTestBitMaskcollisionBitMask 、およびcontactTestBitMaskの関係設定です。コンタクトとコリジョンからの希望の結果を合理的に設定することで、どのタイプが衝突して他の人とコンタクトできるかを判断し、望ましくない衝突、接触、物理処理のオーバーヘッドを回避できます。

それぞれのタイプの 'エンティティ'に対して、3つすべて設定できます:

  1. categoryBitMask :このタイプのノードに固有のカテゴリ
  2. collisionBitMask :衝突微分器は、上と異なることがあります
  3. contactTestBitMask :連絡先の差別化要因は、上記とは異なる場合があります

衝突と連絡先を実装する一般的な手順は次のとおりです。

  1. 物理的な体の大きさ、形、(時には)質量を設定する
  2. あなたのノードタイプに必要なBitMaskをカテゴリ、衝突、および連絡先から追加してください
  3. シーンを連絡先デリゲートとして設定し、衝突や連絡先の確認と通知を可能にする
  4. 連絡先ハンドラとその他の物理的事象に関する適切なロジックを実装する

物理ワールドを有効にする

// World physics
    self.physicsWorld.gravity         = CGVectorMake(0, -9.8);

ノードの衝突を有効にする

まず、ノードカテゴリを設定します

let groundBody: UInt32 = 0x1 << 0
let boxBody: UInt32 = 0x1 << 1

次に、Ground型ノードとBox型ノードを追加します。

let ground = SKSpriteNode(color: UIColor.cyanColor(), size: CGSizeMake(self.frame.width, 50))
ground.position = CGPointMake(CGRectGetMidX(self.frame), 100)
ground.physicsBody = SKPhysicsBody(rectangleOfSize: ground.size)
ground.physicsBody?.dynamic = false
ground.physicsBody?.categoryBitMask = groundBody
ground.physicsBody?.collisionBitMask = boxBody
ground.physicsBody?.contactTestBitMask = boxBody
    
addChild(ground)

// Add box type node

let box = SKSpriteNode(color: UIColor.yellowColor(), size: CGSizeMake(20, 20))
box.position = location
box.physicsBody = SKPhysicsBody(rectangleOfSize: box.size)
box.physicsBody?.dynamic = true
box.physicsBody?.categoryBitMask = boxBody
box.physicsBody?.collisionBitMask = groundBody | boxBody
box.physicsBody?.contactTestBitMask = boxBody
box.name = boxId
            
let action = SKAction.rotateByAngle(CGFloat(M_PI), duration:1)
            
box.runAction(SKAction.repeatActionForever(action))
            
self.addChild(box)

連絡先を処理する

デリゲートとしてシーンを設定する

//set your scene as SKPhysicsContactDelegate

class yourScene: SKScene, SKPhysicsContactDelegate

self.physicsWorld.contactDelegate = self;

次に、連絡機能のいずれかを実装する必要があります。オプションのfunc didBegin(連絡先:)および/またはオプションの資金didEnd(連絡先::)連絡先ロジックを記入する方法

//order

let bodies = (contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask) ? (A:contact.bodyA,B:contact.bodyB) : (A:contact.bodyB,B:contact.bodyA)
    
    
//real handler
if ((bodies.B.categoryBitMask & boxBody) == boxBody){
   if ((bodies.A.categoryBitMask & groundBody) == groundBody) {
       let vector = bodies.B.velocity
       bodies.B.velocity = CGVectorMake(vector.dx, vector.dy * 4)

   }else{
       let vector = bodies.A.velocity
       bodies.A.velocity = CGVectorMake(vector.dx, vector.dy * 10)

   }
}

代替のdidBeginContact

単純なカテゴリを使用している場合、それぞれの物理的ボディが1つのカテゴリにのみ属している場合、この代替の形式のdidBeginContactを読みやすくすることができます。

func didBeginContact(contact: SKPhysicsContact) {
    let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
    
switch contactMask {

case categoryBitMask.player | categoryBitMask.enemy:
    print("Collision between player and enemy")
    let enemyNode = contact.bodyA.categoryBitMask == categoryBitMask.enemy ? contact.bodyA.node! : contact.bodyB.node!
    enemyNode.explode()
    score += 10

case categoryBitMask.enemy | categoryBitMask.enemy:
    print("Collision between enemy and enemy")
    contact.bodyA.node.explode()
    contact.bodyB.node.explode()

default :
    //Some other contact has occurred
    print("Some other contact")
}
}

衝突、連絡先、タッチイベントを示すSimple Sprite Kitプロジェクト。

ここには単純なSprite-Kit GameScene.swiftがあります。新しい空のSpriteKitプロジェクトを作成し、これとGameScene.swiftを置き換えます。その後、ビルドして実行します。

画面上の任意のオブジェクトをクリックすると、それらのオブジェクトを移動させることができます。ログとコメントをチェックして、どのログが衝突し、どのログが接触しているかを確認します。

//
//  GameScene.swift
//  bounceTest
//
//  Created by Stephen Ives on 05/04/2016.
//  Copyright (c) 2016 Stephen Ives. All rights reserved.
//

import SpriteKit



class GameScene: SKScene, SKPhysicsContactDelegate {
    
    let objectSize = 150
    let initialImpulse: UInt32 = 300  // Needs to be proportional to objectSize
    
    //Physics categories
    let purpleSquareCategory:   UInt32 = 1 << 0
    let redCircleCategory:      UInt32 = 1 << 1
    let blueSquareCategory:     UInt32 = 1 << 2
    let edgeCategory:           UInt32 = 1 << 31
    
    let purpleSquare = SKSpriteNode()
    let blueSquare = SKSpriteNode()
    let redCircle = SKSpriteNode()
    
    override func didMove(to view: SKView) {
        
        physicsWorld.gravity = CGVector(dx: 0, dy: 0)
        
        //Create an boundary else everything will fly off-screen
        let edge = frame.insetBy(dx: 0, dy: 0)
        physicsBody = SKPhysicsBody(edgeLoopFrom: edge)
        physicsBody?.isDynamic = false  //This won't move
        name = "Screen_edge"
        
        scene?.backgroundColor = SKColor.black
        
        //        Give our 3 objects their attributes
        
        blueSquare.color = SKColor.blue
        blueSquare.size = CGSize(width: objectSize, height: objectSize)
        blueSquare.name = "shape_blueSquare"
        blueSquare.position = CGPoint(x: size.width * -0.25, y: size.height * 0.2)
        
        let circleShape = SKShapeNode(circleOfRadius: CGFloat(objectSize))
        circleShape.fillColor = SKColor.red
        redCircle.texture = view.texture(from: circleShape)
        redCircle.size = CGSize(width: objectSize, height: objectSize)
        redCircle.name = "shape_redCircle"
        redCircle.position = CGPoint(x: size.width * 0.4, y: size.height * -0.4)
        
        purpleSquare.color = SKColor.purple
        purpleSquare.size = CGSize(width: objectSize, height: objectSize)
        purpleSquare.name = "shape_purpleSquare"
        purpleSquare.position = CGPoint(x: size.width * -0.35, y: size.height * 0.4)
        
        addChild(blueSquare)
        addChild(redCircle)
        addChild(purpleSquare)
        
        redCircle.physicsBody = SKPhysicsBody(circleOfRadius: redCircle.size.width/2)
        blueSquare.physicsBody = SKPhysicsBody(rectangleOf: blueSquare.frame.size)
        purpleSquare.physicsBody = SKPhysicsBody(rectangleOf: purpleSquare.frame.size)
        
        setUpCollisions()
        
        checkPhysics()
        
    }
    
    
    func setUpCollisions() {
        
        //Assign our category bit masks to our physics bodies
        purpleSquare.physicsBody?.categoryBitMask = purpleSquareCategory
        redCircle.physicsBody?.categoryBitMask = redCircleCategory
        blueSquare.physicsBody?.categoryBitMask = blueSquareCategory
        physicsBody?.categoryBitMask = edgeCategory  // This is the edge for the scene itself
        
        // Set up the collisions. By default, everything collides with everything.
        
        redCircle.physicsBody?.collisionBitMask &= ~purpleSquareCategory  // Circle doesn't collide with purple square
        purpleSquare.physicsBody?.collisionBitMask = 0   // purpleSquare collides with nothing
        //        purpleSquare.physicsBody?.collisionBitMask |= (redCircleCategory | blueSquareCategory)  // Add collisions with red circle and blue square
        purpleSquare.physicsBody?.collisionBitMask = (redCircleCategory)  // Add collisions with red circle
        blueSquare.physicsBody?.collisionBitMask = (redCircleCategory)  // Add collisions with red circle
        
        
        // Set up the contact notifications. By default, nothing contacts anything.
        redCircle.physicsBody?.contactTestBitMask |= purpleSquareCategory   // Notify when red circle and purple square contact
        blueSquare.physicsBody?.contactTestBitMask |= redCircleCategory     // Notify when blue square and red circle contact
        
        // Make sure everything collides with the screen edge and make everything really 'bouncy'
        enumerateChildNodes(withName: "//shape*") { node, _ in
            node.physicsBody?.collisionBitMask |= self.edgeCategory  //Add edgeCategory to the collision bit mask
            node.physicsBody?.restitution = 0.9 // Nice and bouncy...
            node.physicsBody?.linearDamping = 0.1 // Nice and bouncy...
        }
             
        //Lastly, set ourselves as the contact delegate
        physicsWorld.contactDelegate = self
    }
    
    func didBegin(_ contact: SKPhysicsContact) {
        let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
        
        switch contactMask {
        case purpleSquareCategory | blueSquareCategory:
            print("Purple square and Blue square have touched")
        case redCircleCategory | blueSquareCategory:
            print("Red circle and Blue square have touched")
        case redCircleCategory | purpleSquareCategory:
            print("Red circle and purple Square have touched")
        default: print("Unknown contact detected")
        }
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        for touch in touches {
            let touchedNode = selectNodeForTouch(touch.location(in: self))
            
            if let node = touchedNode {
                node.physicsBody?.applyImpulse(CGVector(dx: CGFloat(arc4random_uniform(initialImpulse)) - CGFloat(initialImpulse/2), dy: CGFloat(arc4random_uniform(initialImpulse)) - CGFloat(initialImpulse/2)))
                node.physicsBody?.applyTorque(CGFloat(arc4random_uniform(20)) - CGFloat(10))
            }
            
        }
    }
    
    // Return the sprite where the user touched the screen
    func selectNodeForTouch(_ touchLocation: CGPoint) -> SKSpriteNode? {
        
        let touchedNode = self.atPoint(touchLocation)
        print("Touched node is \(touchedNode.name)")
        //        let touchedColor = getPixelColorAtPoint(touchLocation)
        //        print("Touched colour is \(touchedColor)")
        
        if touchedNode is SKSpriteNode {
            return (touchedNode as! SKSpriteNode)
        } else {
            return nil
        }
    }
    
    //MARK: - Analyse the collision/contact set up.
    func checkPhysics() {
        
        // Create an array of all the nodes with physicsBodies
        var physicsNodes = [SKNode]()
        
        //Get all physics bodies
        enumerateChildNodes(withName: "//.") { node, _ in
            if let _ = node.physicsBody {
                physicsNodes.append(node)
            } else {
                print("\(node.name) does not have a physics body so cannot collide or be involved in contacts.")
            }
        }
        
        //For each node, check it's category against every other node's collion and contctTest bit mask
        for node in physicsNodes {
            let category = node.physicsBody!.categoryBitMask
            // Identify the node by its category if the name is blank
            let name = node.name != nil ? node.name! : "Category \(category)"
            
            let collisionMask = node.physicsBody!.collisionBitMask
            let contactMask = node.physicsBody!.contactTestBitMask
            
            // If all bits of the collisonmask set, just say it collides with everything.
            if collisionMask == UInt32.max {
                print("\(name) collides with everything")
            }
            
            for otherNode in physicsNodes {
            if (node.physicsBody?.dynamic == false) {
                print("This node \(name) is not dynamic")
            }
                if (node != otherNode) && (node.physicsBody?.isDynamic == true) {
                    let otherCategory = otherNode.physicsBody!.categoryBitMask
                    // Identify the node by its category if the name is blank
                    let otherName = otherNode.name != nil ? otherNode.name! : "Category \(otherCategory)"
                    
                    // If the collisonmask and category match, they will collide
                    if ((collisionMask & otherCategory) != 0) && (collisionMask != UInt32.max) {
                        print("\(name) collides with \(otherName)")
                    }
                    // If the contactMAsk and category match, they will contact
                    if (contactMask & otherCategory) != 0 {print("\(name) notifies when contacting \(otherName)")}
                }
            }
        }
    }
}

複数のカテゴリのスプライトを扱う際にコンタクトを処理する代わりの方法

let bodies = (contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask) ? (A:contact.bodyA,B:contact.bodyB) : (A:contact.bodyB,B:contact.bodyA)
    

switch (bodies.A.categoryBitMask,bodies.B.categoryBitMask)
{
  case let (a, _) where (a && superPower): //All we care about is if category a has a super power
        //do super power effect
        fallthrough //continue onto check if we hit anything else 
  case let (_, b) where (b && superPower): //All we care about is if category b has a super power
        //do super power effect
        fallthrough //continue onto check if we hit anything else 
  case let (a, b) where  (a && groundBody) && (b && boxBody): //Check if box hit ground
       //boxBody hit ground
  case let (b, _) where  (b && boxBody): //Check if box hit anything else
       //box body hit anything else 
   default:()
  
}

接触と衝突の違い

Sprite-Kitには衝突の概念があります。これは、物理学オブジェクトが衝突したときに物理オブジェクトがどのように相互作用するかを処理するSK物理エンジンを指します。

また、2つの物理オブジェクトが交差したときにプログラムに通知されるメカニズムである連絡先という概念もあります。

オブジェクトは衝突する可能性がありますが、連絡先は生成されず、衝突せずに連絡先が生成されたり、連絡先が衝突したり生成されたりすることはありません。

衝突は片側にすることもできます。つまり、オブジェクトAはオブジェクトBに衝突(跳ね返り)することができますが、オブジェクトBは何も起こらなかったかのように継続します。 2つのオブジェクトがお互いに跳ね返るようにするには、2つのオブジェクトが互いに衝突するように指示する必要があります。

ただし、連絡先は片側ではありません。オブジェクトAがオブジェクトBをタッチした(接触した)ときを知りたければ、オブジェクトBに関してオブジェクトA上の接触検出を設定すれば十分です。オブジェクトAのオブジェクトBに対して接触検出を設定する必要はありません。

contactTestとcollisonビットマスクを操作して、特定の連絡先と衝突を有効/無効にします。

この例では、4つのボディを使用し、簡単にするためにビットマスクの最後の8ビットのみを表示します。 4つのボディは3つのSKSpriteNodeで、物理ボディと境界を持っています。

    let edge = frame.insetBy(dx: 0, dy: 0)
    physicsBody = SKPhysicsBody(edgeLoopFrom: edge)

「エッジ」フィジックスボディは、ノードではなくシーンの物理ボディです。

4つのユニークなカテゴリを定義します

let purpleSquareCategory:   UInt32 = 1 << 0  // bitmask is ...00000001
let redCircleCategory:      UInt32 = 1 << 1  // bitmask is ...00000010
let blueSquareCategory:     UInt32 = 1 << 2  // bitmask is ...00000100
let edgeCategory:           UInt32 = 1 << 31  // bitmask is 10000...00000000

各フィジックスボディには、それが属するカテゴリが割り当てられます。

        //Assign our category bit masks to our physics bodies
        purpleSquare.physicsBody?.categoryBitMask = purpleSquareCategory
        redCircle.physicsBody?.categoryBitMask = redCircleCategory
        blueSquare.physicsBody?.categoryBitMask = blueSquareCategory
        physicsBody?.categoryBitMask = edgeCategory  // This is the edge for the scene itself

ボディのcollisionBitMaskのビットが1に設定されている場合、categoryBitMask内の同じ位置に '1'を持つボディが衝突します(バウンドオフ)。 contactTestBitMaskの場合も同様です。

他に指定しない限り、すべてがすべて他のものと衝突し、連絡先は生成されません(あなたのコードは何か他に連絡があったときに通知されません)。

purpleSquare.physicsBody.collisonBitMask = 11111111111111111111111111111111 // 32 '1's.

すべての位置の各ビットは「1」なので、他のカテゴリBitMaskと比較すると、Sprite Kitは「1」を検出して衝突が発生します。このボディが特定のカテゴリと衝突しないようにするには、collisonBitMaskの正しいビットを '0'に設定する必要があります。

そのcontactTestbitMaskはすべて0設定されてい0

redCircle.physicsBody.contactTestBitMask = 00000000000000000000000000000000  // 32 '0's

collisionBitMaskと同じですが、逆の場合を除きます。

車体間の接触または衝突をオフにすることができます(既存の接触または衝突は変わりません)。

nodeA.physicsBody?.collisionBitMask &= ~nodeB.category

我々は論理的にnodeAのコリジョンビットマスクとnodeBのカテゴリビットマスクの逆(論理NOT、〜演算子)を使用して、そのビットnodeAのビットマスクをオフにする。たとえば赤い円が紫色の四角形に衝突するのを止めるには:

redCircle.physicsBody?.collisionBitMask = redCircle.physicsBody?.collisionBitMask & ~purpleSquareCategory

以下のように短縮することができます。

redCircle.physicsBody?.collisionBitMask &= ~purpleSquareCategory

説明:

redCircle.physicsBody.collisonBitMask = 11111111111111111111111111111111
purpleSquareCategory  = 00000000000000000000000000000001
~purpleSquareCategory = 11111111111111111111111111111110 
11111111111111111111111111111111 & 11111111111111111111111111111110 = 11111111111111111111111111111110 
redCircle.physicsBody.collisonBitMask now equals 11111111111111111111111111111110 

redCircleは、カテゴリが....のボディに衝突しなくなりました。0001(purpleSquare)

collsionsbitMask内の個々のビットをオフにする代わりに、直接設定することができます。

blueSquare.physicsBody?.collisionBitMask = (redCircleCategory | purpleSquareCategory)

すなわち、 blueSquare.physicsBody?.collisionBitMask = (....00000010 OR ....00000001)

これはblueSquare.physicsBody?.collisionBitMask = ....00000011等しいblueSquare.physicsBody?.collisionBitMask = ....00000011

blueSquareは、カテゴリまたは..01または..10のボディにのみ衝突します

2つのボディ間のコンタクトまたはコリジョンは、(既存のコンタクトまたはコリジョンに影響を与えずに)いつでもオンにすることができます。

redCircle.physicsBody?.contactTestBitMask |= purpleSquareCategory

論理的にredCircleのbitMaskをpurpleSquareのカテゴリビットマスクで設定すると、redcircleのbitMaskのそのビットがオンになります。これによりredCircelのbitMasの他のビットは影響を受けません。

次のように、すべての図形が画面の端からはじき出されることを確認できます。

// Make sure everything collides with the screen edge
enumerateChildNodes(withName: "//*") { node, _ in
    node.physicsBody?.collisionBitMask |= self.edgeCategory  //Add edgeCategory to the collision bit mask
}

注意:

衝突は片側にすることができます。つまり、オブジェクトAはオブジェクトBに衝突(跳ね返り)することができますが、オブジェクトBは何も起こらなかったかのように継続します。 2つのオブジェクトが互いに跳ね返るようにするには、2つのオブジェクトが互いに衝突するように指示する必要があります。

blueSquare.physicsBody?.collisionBitMask = redCircleCategory
redcircle.physicsBody?.collisionBitMask = blueSquareCategory

ただし、連絡先は片側ではありません。オブジェクトAがオブジェクトBをタッチした(接触した)ときを知りたければ、オブジェクトBに関してオブジェクトA上の接触検出を設定すれば十分です。オブジェクトAのオブジェクトBに対して接触検出を設定する必要はありません。

blueSquare.physicsBody?.contactTestBitMask = redCircleCategory

redcircle.physicsBody?.contactTestBitMask= blueSquareCategoryは必要ありredcircle.physicsBody?.contactTestBitMask= blueSquareCategory

高度な使い方:

ここでは扱いませんが、物理機関は複数のカテゴリに属する​​ことができます。たとえば、ゲームを次のように設定できます。

let squareCategory:   UInt32 = 1 << 0   // bitmask is ...00000001
let circleCategory:   UInt32 = 1 << 1   // bitmask is ...00000010
let blueCategory:     UInt32 = 1 << 2   // bitmask is ...00000100
let redCategory:      UInt32 = 1 << 3   // bitmask is ...00001000
let purpleCategory:   UInt32 = 1 << 4   // bitmask is ...00010000
let edgeCategory:     UInt32 = 1 << 31  // bitmask is 10000...0000000

各フィジックスボディには、それが属するカテゴリが割り当てられます。

        //Assign our category bit masks to our physics bodies
        purpleSquare.physicsBody?.categoryBitMask = squareCategory | purpleCategory
        redCircle.physicsBody?.categoryBitMask = circleCategory | redCategory
        blueSquare.physicsBody?.categoryBitMask = squareCategory | blueCategory

それらのcategorybitMasksは次のようになりました:

purpleSquare.physicsBody?.categoryBitMask = ...00010001
redCircle.physicsBody?.categoryBitMask    = ...00001010
blueSquare.physicsBody?.categoryBitMask   = ...00000101

これは、ビットフィールドの操作方法に影響します。物理的なボディ(例えば爆弾)が何らかの形で変わったことを示すこと(例えば、それは別のカテゴリーである「スーパー」能力を得ているかもしれないし、特定のオブジェクト(エイリアン・マザーズ



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow