Остановить объекты от столкновения с помощью SpriteKit

Я тестирую особенности SpriteKit, и у меня возникла проблема. Я читал бит-маски, столкновение, категорию и контакт. Я получаю то, что они есть, по большей части, по крайней мере, я не понимаю точку битмакса, но у меня возникают встречные битмаски, которые мне нужны для решения моей проблемы.

Итак, моя проблема в том, что у меня есть два разных типа спрайтов: объект и второй. Названия на самом деле не имеют большого смысла, но это просто для тестирования. Я хочу, чтобы у второго был импульс, и я хочу, чтобы объект имел силу. Я смог применить соответствующие векторы на спрайтах, но я не хочу, чтобы они сталкивались друг с другом. Я хочу, чтобы они прошли прямо и проигнорировали существование друг друга.

Я попытался решить эту проблему, назначив разные битмаски для столкновений друг другу:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    let texture = SKTexture(imageNamed: "pokeball")
    let object = SKSpriteNode(texture: texture)

    object.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: object.size.width,height: object.size.height))
    object.physicsBody?.affectedByGravity = false
    object.yScale = 0.5
    object.xScale = 0.5

    for t in touches {
        object.position = t.location(in: self)
    }
    self.addChild(object)
    object.physicsBody?.collisionBitMask = UInt32(4)
    object.physicsBody?.applyForce(CGVector(dx: 0, dy: 10))

}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    let texture = SKTexture(imageNamed: "purple")
    let second = SKSpriteNode(texture: texture)
    let impulse : Double = 20
    let x = (impulse * Double(cosf(45)))
    let y = Double(impulse * Double(sinf(45)))
    let vector = CGVector(dx: x, dy: y)
    second.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: second.size.width,height: second.size.height))

    second.yScale = 1.5
    second.xScale = 1.5
    second.physicsBody?.isDynamic = true

    for t in touches {
        second.position = t.location(in: self)
    }
    self.addChild(second)
    second.physicsBody?.collisionBitMask = UInt32(1)
    second.physicsBody?.applyImpulse(vector)
}

Итак, у объекта есть битовая маска 4:

object.physicsBody?.collisionBitMask = UInt32(4)

И вторая имеет битовую маску 1:

second.physicsBody?.collisionBitMask = UInt32(1)

Я запустил симулятор, и они все еще сталкиваются друг с другом, поэтому я пошел в Интернет и попытался найти ответы на некоторые вопросы: я нашел тот, который говорит, что я должен использовать такие числа, как:

это битмаски, вы не можете использовать произвольные числа 1,2,3,4,5 - вы должны использовать 1,2,4,8,16 и так далее -

Может кто-нибудь объяснить, почему? Однако это не было проблемой, потому что я использовал 1 и 4

Следующий вопрос, с которым я столкнулся, сказал, что мне пришлось использовать двоичные числа (0100) и (0010), я попробовал их, одну и ту же проблему: все еще сталкиваюсь.

Я оставлю изображение столкновений: Коллизии

Кто-нибудь знает, почему это происходит? Мои извинения заранее, если это действительно глупая ошибка или что-то, что уже было задано, я просто не мог ее найти.

Ответ 1

Существует много документации по этим темам, но вот практический пример.

Сила категорииBitMasks

Представьте, что у вас есть коллекция из трех узлов pool, basketball и bowlingball. Теперь, очевидно, мы хотим, чтобы теги basketball и bowlingball сталкивались друг с другом. Таким образом, вы устанавливаете collisionBitMasks следующим образом:

basketball.physicsBody?.collisionBitMask    = UInt32(2)
bowlingball.physicsBody?.collisionBitMask   = UInt32(2)

Великий. Теперь мы хотим, чтобы bowlingball опустился до нижней части pool, а basketball столкнулся с pool (может быть, больше всплеск, но нести со мной). Как мы это сделаем? Мы могли бы попробовать:

pool.physicsBody?.collisionBitMask = UInt32(2) // ?

Но подождите, это приведет к тому, что basketball И bowlingball столкнется с pool. Мы хотим, чтобы basketball сталкивался с пулом, тогда как мы хотим, чтобы bowlingball игнорировал pool и погружался прямо вниз, без столкновений. Здесь categoryBitMask пригодится:

let basketballBitMask     = UInt32(1)
let bowlingballBitMask    = UInt32(2)
let poolBitMask           = UInt32(4)  // Why 4? See next section

basketball.physicsBody?.categoryBitMask    = basketballBitMask
bowlingball.physicsBody?.categoryBitMask   = bowlingballBitMask
pool.physicsBody?.categoryBitMask          = poolBitMask

Поскольку каждому объекту присваивается уникальный номер, вы можете выбрать, с какими объектами вы хотите столкнуться с другим объектом:

// basketball physics body collides with bowlingball(2) OR pool(4)
basketball.physicsBody?.collisionBitMask    = bowlingballBitMask | poolBitMask
// ( '|' = logical OR operator)

// bowlingball physics body only collides with basketball(1)
bowlingball.physicsBody?.collisionBitMask   = basketballBitMask

// pool physics body only collides with basketball(1)
pool.physicsBody?.collisionBitMask          = basketballBitmask

Если вы не уверены, что странное '|' символ, я настоятельно рекомендую оперативную документацию для продвинутых операторов, чтобы помочь вам понять, что происходит здесь.

Почему бы просто не использовать collisionBitMasks?

Итак, мы установили несколько бит-масок. Но как они используются? Если у нас есть только два объекта, почему мы не можем просто сравнить collisionBitMasks?

Проще говоря, это просто не так, как это работает. Когда a bowlingball входит в контакт с pool, физический движок SpriteKit будет И ('&') вместе bowlingball categoryBitMask с pool collisionBitMask (или наоборот; результат тот же):

objectsShouldCollide = (bowlingball.physicsBody?.categoryBitMask & 
                        pool.physicsBody?.collisionBitMask)
// objectsShouldCollide = (ob010 & 0b100) = 0b000 

Так как bowlingball categoryBitMask и pool collisionBitMask имеют нулевые биты, то objectsShouldCollide равно нулю, а SpriteKit останавливает объекты от столкновения.

Но в вашем случае вы не устанавливаете свои объекты categoryBitMask s. Таким образом, они имеют значение по умолчанию 2 ^ 32 или 0xFFFFFFFF (шестнадцатеричное представление) или в двоичном формате, 0b1111111111111111111111111111111111. Поэтому, когда "объект" попадает в "второй" объект, SpriteKit делает это:

objectsShouldCollide = (0b11111111111111111111111111111111 & // Default categoryBitMask for "object" 
                        0b00000000000000000000000000000001)  // collisionBitMask for "second" object
//                   =  0b00000000000000000000000000000001

Итак, если вы не определили object categoryBitMask, независимо от того, что вы установили как объект second collisionBitMask, объектыShouldCollide никогда не будут равны нулю, и они всегда будут сталкиваться.

Примечание: вы можете установить для объекта collisionBitMask значение 0; но тогда этот объект никогда не сможет столкнуться ни с чем.

Использование степеней 2 (0,1,2,4,8 и т.д.) для категорииBitMasks

Теперь предположим, что мы хотели включить несколько bowlingball, которые столкнулись друг с другом. Легко:

bowlingball.physicsBody?.collisionBitMask  = basketballBitMask | bowlingballBitMask
// bowlingball collision bit mask (in binary)  = 0b10 | 0b01 = 0b11
// bowlingball collision bit mask (in decimal)  = 2 | 1 = 3 

Здесь вы можете видеть, что если бы мы установили pool physicalCategory в UInt32 (3), это уже не могло бы отличаться от bowlingball или basketball.

Дополнительные предложения

Научитесь имя переменным с целью, даже если вы просто используете их для тестирования (хотя, по совпадению, "object и second объект" работал достаточно хорошо".

Используйте структуру для битмаски, чтобы упростить код и улучшить читаемость:

struct PhysicsCategory {
    static let Obj1         : UInt32 = 0b1 << 0
    static let Obj2         : UInt32 = 0b1 << 1
    static let Obj3         : UInt32 = 0b1 << 2
    static let Obj4         : UInt32 = 0b1 << 3
}

obj1.physicsBody?.categoryBitmask = PhysicsCategory.Obj1 // etc

Ответ 2

Почему вы не загружаете и не играете с этим простым проектом Sprite-Kit? Он создает различные геометрические фигуры, заставляет некоторые из них сталкиваться, некоторые из них контактируют, использует функцию checkPhysics(), чтобы показать, что произойдет, а затем позволяет вам перемещать фигуры вокруг.

Кнопка атаки в SpriteKit

Любые вопросы относительно его работы, и я буду более чем счастлив попробовать и объяснить.