Recherche…


Remarques

Les déterminants de la collision Sprite Kit et du traitement des événements de contact sont les paramètres de relation que vous avez créés, categoryBitMask , collisionBitMask et contactTestBitMask pour chacun de vos types d'objet en interaction. En les définissant rationnellement en fonction des résultats escomptés des contacts et des collisions, vous déterminez quels types peuvent entrer en collision et informer des contacts avec d’autres, et éviter les collisions, les contacts et le traitement physique indésirables.

Pour chaque type d'entité, vous pouvez définir tous les trois:

  1. categoryBitMask : une catégorie spécifique à ce type de noeud
  2. collisionBitMask : un différenciateur de collision, peut être différent du précédent
  3. contactTestBitMask : un différenciateur de contact, peut être différent des deux contactTestBitMask

Les étapes générales pour implémenter des collisions et des contacts sont les suivantes:

  1. définir la taille physique du corps, la forme et (parfois) la masse
  2. ajouter les BitMasks nécessaires pour votre type de noeud à partir de la catégorie, de la collision et du contact ci-dessus
  3. mettre en scène en tant que délégué de contact lui permettant de vérifier et d'informer des collisions et des contacts
  4. mettre en œuvre des gestionnaires de contacts et toute autre logique pertinente pour les événements de physique

Activer le monde de la physique

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

Activer le nœud pour entrer en collision

Tout d'abord, nous définissons la catégorie de nœud

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

Ajoutez ensuite le nœud du type de sol et le nœud du type de boîte.

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)

Gérer les contacts

Définir la scène en tant que délégué

//set your scene as SKPhysicsContactDelegate

class yourScene: SKScene, SKPhysicsContactDelegate

self.physicsWorld.contactDelegate = self;

Ensuite, vous devez implémenter l'une ou l'autre des fonctions de contact: option func didBegin (contact :) et / ou facultatif fund didEnd (contact :) méthode pour remplir votre logique de contact, par exemple comme

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

   }
}

Alternative aBeginContact

Si vous utilisez des catégories simples, avec chaque corps physique appartenant à une seule catégorie, alors cette forme alternative de didBeginContact peut être plus lisible:

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

Projet Simple Sprite Kit montrant des collisions, des contacts et des événements tactiles.

Voici un simple Sprite-Kit GameScene.swift. Créez un nouveau projet SpriteKit vide et remplacez-le par GameScene.swift. Ensuite, construisez et exécutez.

Cliquez sur l'un des objets à l'écran pour les faire bouger. Vérifiez les journaux et les commentaires pour voir ceux qui entrent en collision et ceux qui entrent en contact.

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

Alternative au traitement des contacts avec les sprites multi-catégories

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:()
  
}

Différence entre contacts et collisions

Dans Sprite-Kit, il y a le concept de collision qui fait référence au moteur physique SK qui interagit avec la façon dont les objets physiques interagissent lorsqu'ils entrent en collision, par exemple ceux qui rebondissent sur les autres.

Il a également le concept de contacts , qui est le mécanisme par lequel votre programme est informé lorsque 2 objets physiques se croisent.

Les objets peuvent entrer en collision mais ne pas générer de contacts, générer des contacts sans entrer en collision, ou entrer en collision et générer un contact (ou ne pas interagir ni interagir du tout)

Les collisions peuvent également être unilatérales, c'est-à-dire que l'objet A peut entrer en collision (rebondir) avec l'objet B, tandis que l'objet B continue comme si rien ne s'était passé. Si vous voulez que deux objets se renversent, ils doivent tous les deux être contraints d'entrer en collision avec l'autre.

Les contacts ne sont toutefois pas à sens unique; si vous voulez savoir quand l'objet A a touché (contacté) l'objet B, il suffit de configurer la détection de contact sur l'objet A par rapport à l'objet B. Il n'est pas nécessaire de configurer la détection de contact sur l'objet B pour l'objet A.

Manipulation des masques de bit contactTest et collison pour activer / désactiver le contact et les collisions spécifiques.

Pour cet exemple, nous utiliserons 4 corps et ne montrerons que les 8 derniers bits des masques de bits pour plus de simplicité. Les 4 corps sont 3 SKSpriteNodes, chacun avec un corps physique et une limite:

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

Notez que le corps physique "bord" est le corps physique de la scène, pas un nœud.

Nous définissons 4 catégories uniques

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

Chaque corps de physique se voit attribuer les catégories auxquelles il appartient:

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

Si un bit de la propriété collisionBitMask d'un corps est défini sur 1, alors il se heurte (rebondit) à tout corps ayant un "1" à la même position dans son objet categoryBitMask. De même pour contactTestBitMask.

À moins d'indication contraire, tout se heurte à tout le reste et aucun contact n'est généré (votre code ne sera pas notifié lorsque quelque chose entrera en contact avec quelque chose d'autre):

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

Chaque bit dans chaque position est '1', donc quand on le compare à n'importe quel autre categoryBitMask, Sprite Kit trouvera un '1' et une collision se produira. Si vous ne voulez pas que ce corps entre en collision avec une certaine catégorie, vous devrez définir le bon bit dans le collisonBitMask sur '0'

et son contactTestbitMask est défini sur tous les 0 s:

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

Identique à collisionBitMask, sauf inversé.

Les contacts ou les collisions entre les corps peuvent être désactivés (laissant le contact ou la collision existants inchangés) en utilisant:

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

Nous avons logiquement le masque de bit de collision de ET nodeA avec l'inverse (l'opérateur logique, l'opérateur ~) du masque de catégorie de nodeB pour "désactiver" ce bitMask du bit nodeA. par exemple pour empêcher le cercle rouge d'entrer en collision avec le carré violet:

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

qui peut être raccourci à:

redCircle.physicsBody?.collisionBitMask &= ~purpleSquareCategory

Explication:

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

redCircle n'entre plus en collision avec des corps avec une catégorie de .... 0001 (purpleSquare)

Au lieu de désactiver les bits individuels dans le collsionsbitMask, vous pouvez le définir directement:

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

c'est-à-dire blueSquare.physicsBody?.collisionBitMask = (....00000010 OR ....00000001)

qui équivaut à blueSquare.physicsBody?.collisionBitMask = ....00000011

blueSquare n'entrera en collision qu'avec des corps avec une catégorie ou ..01 ou ..10

Les contacts ou les collisions entre les 2 corps peuvent être activés (sans affecter les contacts existants ou collisions) à tout moment en utilisant:

redCircle.physicsBody?.contactTestBitMask |= purpleSquareCategory

Nous avons logiquement le bitMask de AND redCircle avec le masque de bits de la catégorie purpleSquare pour "activer" ce bit dans le bitMask de redcircle. Cela laisse les autres bits du bitMas de redCircel non affectés.

Vous pouvez vous assurer que chaque forme «rebondit» sur un bord de l'écran comme suit:

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

Remarque:

Les collisions peuvent être unilatérales, c'est-à-dire que l'objet A peut entrer en collision (rebondir) avec l'objet B, tandis que l'objet B continue comme si rien ne s'était passé. Si vous voulez que deux objets se rejoignent, ils doivent tous les deux se heurter à l'autre:

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

Les contacts ne sont toutefois pas à sens unique; si vous voulez savoir quand l'objet A a touché (contacté) l'objet B, il suffit de configurer la détection de contact sur l'objet A par rapport à l'objet B. Il n'est pas nécessaire de configurer la détection de contact sur l'objet B pour l'objet A.

blueSquare.physicsBody?.contactTestBitMask = redCircleCategory

Nous n'avons pas besoin de redcircle.physicsBody?.contactTestBitMask= blueSquareCategory

Utilisation avancée:

Non couvert ici, mais les corps physiques peuvent appartenir à plusieurs catégories. Par exemple, nous pourrions configurer notre jeu comme suit:

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

Chaque corps de physique se voit attribuer les catégories auxquelles il appartient:

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

leurs categorybitMasks sont maintenant:

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

Cela affectera la façon dont vous manipulez les champs de bits. Cela peut être utile (par exemple) d’indiquer qu’un corps physique (par exemple une bombe) a changé (par exemple, il pourrait avoir la capacité «super» qui est une autre catégorie, et vous pouvez vérifier qu’un certain objet



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow