Zoeken…


Opmerkingen

De bepalende factoren voor de botsing van Sprite Kit en de verwerking van contactgebeurtenissen zijn de door u gemaakte relatie-instellingen van categoryBitMask , collisionBitMask en contactTestBitMask voor elk van uw interactieve objecttypen. Door deze rationeel in te stellen ten behoeve van uw gewenste resultaten van contacten en botsingen, bepaalt u welke typen kunnen botsen en informeren over contacten met anderen, en voorkomt u ongewenste overhead van botsingen, contact en fysica.

Voor elk type 'entiteit' kunt u alle drie instellen:

  1. categoryBitMask : een categorie specifiek voor dit type knooppunt
  2. collisionBitMask : een collision differentiator, kan van bovenaf verschillen
  3. contactTestBitMask : een contactdifferentiator, kan verschillen van beide hierboven

De algemene stappen om botsingen en contacten te implementeren zijn:

  1. fysieke lichaamsgrootte, vorm en (soms) massa instellen
  2. voeg de benodigde BitMasks toe voor uw type knooppunt uit categorie, botsing en contact hierboven
  3. scène instellen als een contactafgevaardigde waarmee deze botsingen en contacten kan controleren en informeren
  4. contacthandlers en andere relevante logica voor natuurkundige gebeurtenissen implementeren

Schakel Physics World in

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

Schakel knooppunt in botsen in

Eerst stellen we de knooppuntcategorie in

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

Voeg vervolgens het knooppunt Aardtype en het knooppunt Boxtype toe.

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)

Contacten afhandelen

Stel scène in als gemachtigde

//set your scene as SKPhysicsContactDelegate

class yourScene: SKScene, SKPhysicsContactDelegate

self.physicsWorld.contactDelegate = self;

Vervolgens moet u een van de contactfuncties implementeren: optionele func didBegin (contact :) en / of optionele fonds didEnd (contact :) methode om uw contactlogica in te vullen, bijvoorbeeld

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

   }
}

Alternatieve didBeginContact

ALS je eenvoudige categorieën gebruikt, waarbij elke fysica-instantie tot slechts één categorie behoort, dan is deze alternatieve vorm van didBeginContact misschien beter leesbaar:

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

Eenvoudig Sprite Kit-project met botsingen, contacten en aanraakgebeurtenissen.

Hier is een eenvoudige Sprite-Kit GameScene.swift. Maak een nieuw, leeg SpriteKit-project en vervang de GameScene.swift hiermee. Bouw en ren dan.

Klik op een van de objecten op het scherm om ze te laten bewegen. Controleer de logboeken en de opmerkingen om te zien welke botsen en welke contact maken.

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

Alternatief voor contactafhandeling bij het omgaan met sprites met meerdere categorieën

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

Verschil tussen contacten en botsingen

In Sprite-Kit is er het concept van botsingen dat verwijst naar de SK physics-engine die handelt hoe fysica-objecten op elkaar inwerken wanneer ze botsen, dwz welke tegen welke andere botsen.

Het heeft ook het concept van contacten , dat is het mechanisme waarmee uw programma wordt geïnformeerd wanneer twee fysische objecten elkaar kruisen.

Objecten kunnen botsen maar geen contacten genereren, contacten genereren zonder in botsing te komen, of botsen en een contact genereren (of geen van beide en helemaal niet communiceren)

Botsingen kunnen ook eenzijdig zijn, dat wil zeggen object A kan botsen op object B, terwijl object B doorgaat alsof er niets is gebeurd. Als u wilt dat 2 objecten van elkaar stuiteren, moeten beide worden verteld dat ze tegen elkaar moeten botsen.

Contacten zijn echter niet eenzijdig; als u wilt weten wanneer object A aangeraakt (gecontacteerd) object B, volstaat het om contactdetectie in te stellen op object A met betrekking tot object B. U hoeft geen contactdetectie in te stellen op object B voor object A.

Contact manipuleren Test en test bitmaskers om specifiek contact en botsingen in / uit te schakelen.

Voor dit voorbeeld zullen we 4 lichamen gebruiken en zullen voor de eenvoud alleen de laatste 8 bits van de bitmaskers tonen. De 4 lichamen zijn 3 SKSpriteNodes, elk met een fysicalichaam en een grens:

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

Merk op dat het 'rand'-fysicalichaam het fysicalichaam van de scène is, geen knooppunt.

We definiëren 4 unieke categorieën

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

Aan elk natuurkundelichaam worden de categorieën toegewezen waartoe het behoort:

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

Als een beetje in de botsing van een lichaam BitMask is ingesteld op 1, dan botst (stuitert) elk lichaam dat een '1' heeft in dezelfde positie in zijn categorie BitMask. Evenzo voor contactTestBitMask.

Tenzij u anders aangeeft, komt alles in botsing met al het andere en worden er geen contacten gegenereerd (uw code wordt niet op de hoogte gebracht wanneer iets anders contact maakt):

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

Elk bit in elke positie is '1', dus in vergelijking met elke andere categorie BitMask zal Sprite Kit een '1' vinden zodat een botsing zal optreden. Als u niet wilt dat dit lichaam tegen een bepaalde categorie botst, moet u het juiste bit in de collisonBitMask op '0' zetten

en zijn contactTestbitMask is ingesteld op alle 0 s:

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

Hetzelfde als voor collisionBitMask, behalve omgekeerd.

Contacten of botsingen tussen lichamen kunnen worden uitgeschakeld (bestaande contacten of botsingen ongewijzigd laten) met behulp van:

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

We logisch EN nodeA's collision bit mask met de inverse (logisch NIET, de ~ operator) van nodeB's categorie bitmasker om bitMask van dat bit nodeA te 'uitschakelen'. bijv. om te voorkomen dat de rode cirkel op het paarse vierkant botst:

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

die kan worden ingekort tot:

redCircle.physicsBody?.collisionBitMask &= ~purpleSquareCategory

Uitleg:

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

redCircle botst niet langer met lichamen met een categorie van ... 0001 (purpleSquare)

In plaats van afzonderlijke bits in de collsionsbitMask uit te schakelen, kunt u deze rechtstreeks instellen:

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

ie blueSquare.physicsBody?.collisionBitMask = (....00000010 OR ....00000001)

welke gelijk is aan blueSquare.physicsBody?.collisionBitMask = ....00000011

blueSquare zal alleen botsen met lichamen met een categorie of ..01 of ..10

Contacten of botsingen tussen 2 lichamen kunnen worden ingeschakeld (zonder dat bestaande contacten of botsingen) op elk punt met:

redCircle.physicsBody?.contactTestBitMask |= purpleSquareCategory

We logisch EN redCircle's bitMask met purpleSquare's categorie bitmasker om dat bit in te schakelen in redcircle's bitMask. Dit laat alle andere bits in de bitMas van redCircel onverlet.

Je kunt ervoor zorgen dat elke vorm als volgt een schermrand 'stuitert':

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

Notitie:

Botsingen kunnen eenzijdig zijn, dwz object A kan botsen op object B, terwijl object B doorgaat alsof er niets is gebeurd. Als u wilt dat 2 objecten van elkaar stuiteren, moeten beide worden verteld dat ze tegen elkaar moeten botsen:

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

Contacten zijn echter niet eenzijdig; als u wilt weten wanneer object A aangeraakt (gecontacteerd) object B, volstaat het om contactdetectie in te stellen op object A met betrekking tot object B. U hoeft geen contactdetectie in te stellen op object B voor object A.

blueSquare.physicsBody?.contactTestBitMask = redCircleCategory

We hebben geen redcircle.physicsBody?.contactTestBitMask= blueSquareCategory nodig redcircle.physicsBody?.contactTestBitMask= blueSquareCategory

Geavanceerd gebruik:

Hier niet behandeld, maar fysische lichamen kunnen tot meer dan één categorie behoren. We kunnen ons spel bijvoorbeeld als volgt instellen:

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

Aan elk natuurkundelichaam worden de categorieën toegewezen waartoe het behoort:

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

hun categorybitMasks zijn nu:

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

Dit heeft invloed op de manier waarop u de bitvelden manipuleert. Het kan handig zijn (bijvoorbeeld) om aan te geven dat een natuurkundig lichaam (bijvoorbeeld een bom) op de een of andere manier is veranderd (bijvoorbeeld dat het de 'super'-vaardigheid heeft verkregen die een andere categorie is, en je zou kunnen controleren dat een bepaald object (een buitenaardse moeder



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow