수색…


비고

Sprite Kit 충돌 및 연락처 이벤트 처리의 결정 요소는 상호 작용하는 각 개체 유형에 대한 categoryBitMask , collisionBitMaskcontactTestBitMask 의 관계 설정입니다. 합리적으로 연락처와 충돌로부터 원하는 결과에 대한 서비스를 설정함으로써 충돌 할 수있는 유형을 결정하고 다른 사람과의 연락을 알리고 원하지 않는 충돌, 접촉 및 물리 처리 오버 헤드를 피할 수 있습니다.

'엔터티'의 각 유형에 대해 세 가지를 모두 설정할 수 있습니다.

  1. categoryBitMask :이 노드 유형에 특정한 범주
  2. collisionBitMask : 충돌 차별화 요소는 위에서와 다를 수 있습니다.
  3. contactTestBitMask : 연락처 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

단순 카테고리를 사용하는 경우 각 물리학 본문이 하나의 카테고리에만 속하는 경우이 대체 형태의 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 물리 엔진은 물리 객체가 충돌 할 때 상호 작용하는 방식을 처리합니다. 즉 충돌하는 객체가 다른 객체에서 튀어 나오는 방식을 처리합니다.

또한 연락처 라는 개념이 있습니다.이 개념은 두 개의 물리 객체가 교차 할 때 프로그램에 알리는 메커니즘입니다.

객체가 충돌 할 수는 있지만 컨택을 생성하거나, 충돌없이 컨택을 생성하거나, 충돌을 일으켜 컨택을 생성 할 수는 없습니다 (또는 전혀 수행하지 않고 상호 작용하지 않을 수도 있음)

충돌은 일방적 일 수 있습니다. 즉, 객체 A가 객체 B와 충돌 (바운스) 할 수 있습니다. 객체 B는 아무 일도 없었던 것처럼 계속 진행됩니다. 2 개의 물체가 서로 튀어 나오게하려면 두 물체가 서로 충돌해야한다고 알려야합니다.

그러나 연락처는 일방적이지 않습니다. 객체 A가 객체 B를 건드렸을 때 (접촉 한) 것을 알고 싶다면 객체 B에 관해 객체 A에서 접촉 탐지를 설정하는 것으로 충분합니다. 객체 A에 대해 객체 B에서 접촉 탐지를 설정할 필요는 없습니다.

contactTest 및 collison 비트 마스크를 조작하여 특정 연락처 및 충돌을 활성화 / 비활성화합니다.

이 예에서는 4 개의 본문을 사용하고 단순화를 위해 비트 마스크의 마지막 8 비트 만 표시합니다. 4 개의 바디는 3 개의 SKSpriteNodes이며 ​​각각 물리적 인 바디와 경계를 가지고 있습니다 :

    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와 비교할 때 스프라이트 키트는 '1'을 찾아 충돌이 발생합니다. 이 바디를 특정 카테고리와 충돌시키지 않으려면 collisonBitMask의 올바른 비트를 '0'으로 설정해야합니다.

contactTestbitMask 가 모두 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이 더 이상 .... 00 (purpleSquare) 범주의 본문과 충돌하지 않습니다.

collsionsbitMask에서 개별 비트를 끄는 대신 직접 설정할 수 있습니다.

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

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

which는 blueSquare.physicsBody?.collisionBitMask = ....00000011 과 같습니다 blueSquare.physicsBody?.collisionBitMask = ....00000011

blueSquare는 카테고리 또는 .01 또는 ..10이있는 본문에만 충돌합니다.

두 기관 사이에 연락처 또는 충돌이 사용하는 시점에서 (기존 연락처 또는 충돌에 영향을주지 않고) 수 있습니다 :

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 개의 물체가 서로 튀어 나오게하려면 두 물체가 서로 충돌해야한다고 명령해야합니다.

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

이것은 비트 필드를 조작하는 방법에 영향을 미칩니다. 물리학 자 (예 : 폭탄)가 어떻게 든 변경되었다는 것을 나타낼 수 있습니다 (예 : 다른 범주 인 '수퍼'능력을 얻었을 수도 있습니다. 그리고 특정 대상 (외계인 mothersh



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow