Столкновения Spritekit между массивами узлов Sprite - PullRequest
0 голосов
/ 11 января 2020

Я занимаюсь разработкой игры, которая включает в себя несколько массивов спрайтов, и я хочу обнаруживать коллизии между ними и задавать функции в зависимости от того, что et c.

Итак, скажем, у меня есть массив из 16 шаров ballArray [I] и 16 блоков blockaArray [I], которые я могу легко перебрать, используя номер индекса I.

Я дал шарам категорию физики - Шары и аналогичную для блоков. Тогда у меня есть 16 физических ID-категорий, скажем, ID1, ID2, ID3, ID4

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

Какой самый лучший или самый простой способ сделать это? Я читаю о функции enumerateChildNodes (withName), но не использовал ее. Или я могу создать массив PhysicsCategories, которые я мог бы перебирать вместе с SpriteArray для сравнения и идентификации.

EDIT:

Спасибо всем за помощь. Я наконец взломал это. Удивительно, в конце концов, код намного проще, чем я думал. Все еще не до конца понимаю, где находятся биты в моих категориях, но он работает.

Я постараюсь опубликовать мой окончательный рабочий код - возможно, у вас есть предложения по улучшению. Еще раз большое спасибо и извинения за мой плохой этикет StackFlow - я новичок здесь :-)

Итак, мои физические категории были определены.

struct PhysicsCategories {
    static let BoxCategoryMask = UInt32(1<<7)
    static let BallCategoryMask = UInt32(1<<8)

}

и затем в моей функции для создания массива Спрайты

boxBloqArray[i].physicsBody?.categoryBitMask = PhysicsCategories.BoxCategoryMask | UInt32(i)              
boxBloqArray[i].physicsBody!.contactTestBitMask = PhysicsCategories.BallCategoryMask

и то же самое для массива шаров, но только для категорииBitMask

 ballBloqArray[i].physicsBody?.categoryBitMask = PhysicsCategories.BallCategoryMask | UInt32(i)

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

var body1 = SKPhysicsBody()
   var body2 = SKPhysicsBody()

        if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
            body1 = contact.bodyA
            body2 = contact.bodyB
        }
        else {
            body1 = contact.bodyB
            body2 = contact.bodyA
        }


        // Check node collisions
        for n in 0...15 {
            for i in 0...15 {
                if body2.categoryBitMask == PhysicsCategories.BallCategoryMask | UInt32(n) && body1.categoryBitMask == PhysicsCategories.BoxCategoryMask | UInt32(i) {
                    //if body1.node != nil {
                       print("Ball\(n) hit Box\(i)")
                    //}
                }
            }
        }

и теперь печатаются правильные коллизии .... прелесть! ... вперед к следующему шагу ... еще раз спасибо

Ответы [ 2 ]

0 голосов
/ 11 января 2020

Если у вас есть два узла, участвующих в столкновении, как описано в ответе @Luca Angeletti, вы можете превратить их в индекс различными способами.

  1. Если вы сделали каждый тип узла специализированный подкласс, и у вас есть соответствующие индексы, хранящиеся в качестве членов класса, затем вы можете преобразовать в соответствующий класс и посмотреть на поля индекса, например,
if let block = nodeA as? BlockNode, let ball = nodeB as? BallNode {
  print("block \(block.blockIndex) hit ball \(ball.ballIndex)")
}
Узлы могут быть хэшируемыми, поэтому у вас могут быть словари для сопоставления их с индексами:
if let blockIndex = blockIndexes[nodeA], let ballIndex = ballIndexes[nodeB] {
  print("block \(blockIndex) hit ball \(ballIndex)")
}

Вы можете использовать свойство userData узлов для хранения всего, что вам нравится, включая индексы. Хотя шутить над NS-вещами становится некрасиво. https://developer.apple.com/documentation/spritekit/sknode/1483121-userdata

Вы можете выполнять линейное сканирование каждого массива.

if let blockIndex = blocks.firstIndex(of: nodeA), let ballIndex = balls.firstIndex(of: nodeB) {
  print("block \(blockIndex) hit ball \(ballIndex)")
}
По вашему вопросу похоже, что у вас может быть отдельная битовая маска категории для каждого отдельного блока и каждого отдельного шара. Или, если вы этого не сделаете, это возможно, если их максимум 16. В любом случае, если это так, то вы можете сделать несколько щелчков по битам, чтобы взять categoryBitMask из физических тел, сдвинуть шар / блок на 16 бит (в зависимости от того, какое из старших битов будет сдвинуто), а затем взять log2 бита. маски, чтобы получить ваши индексы. Вы можете найти различные методы стука по битам для log2 здесь: https://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious

Учитывая 16 вещей каждого типа, я бы сказал, просто сделайте # 4. Если у вас уже есть узлы подкласса, # 1 подойдет. Номер 2 немного расширяется, так что я не такой фанат этого. Номер 3 я бы не очень рекомендовал из-за материала NS. Номер 5 слишком хорош для своего блага.

Редактировать: Теперь, когда я снова читаю, звучит так, как будто у вас есть отдельные идентификаторы для категорий 1 ... 16, так что ваши битовые маски категории блоков похожи : blockCategoryMask | ID1, blockCategoryMask | ID2, et c. Это также может работать (в основном вариант № 5). Если вы идете по этому маршруту, вы можете просто вставить индекс непосредственно в маски категории:

let blockCategoryMask = UInt32(1<<4)
let ballCategoryMask = UInt32(1<<5)

Тогда физическое тело для блока получит маску blockCategoryMask | UInt32(index), и аналогично для мяч. В этом случае извлечение индекса составляет всего categoryBitMask & UInt32(0xf). Или, если вы поместите категории блоков и шаров в биты 0 и 1, а индексы в биты 2-5, то сдвиньте вправо на 2, чтобы получить индекс.

Отредактируйте в ответ на комментарий: ОК, так что давайте возьмем случай 6 различных категорий объектов, и каждый объект может попасть в одну из 16 различных подкатегорий. Чтобы иметь возможность контролировать, о каких контактах сообщается, необходимо назначить битовую маску для каждой из 6 основных категорий:

enum Category: UInt32 {
  // Basic categories
  case block =    0b000001
  case ball =     0b000010
  case shot =     0b000100
  case obstacle = 0b001000
  case wizard =   0b010000
  case food =     0b100000
 }

Поскольку вы использовали 6 бит для основной категории, у вас есть 26 бит осталось. Для кодирования 16 подкатегорий необходимо 4 бита. Вы можете поместить их в битовую маску категории над основными 6 битами. Примеры манипуляций:

func encodeObject(category: Category, subcategory: Int) -> UInt32 {
  return category.rawValue | (UInt32(subcategory) << 6)
}

func nodeIsA(node: SKNode, category: Category) -> Bool {
  guard let body = node.physicsBody else { return false }
  return (body.categoryBitMask & category.rawValue) != 0
}

func subcategory(node: SKNode) -> Int {
  guard let body = node.physicsBody else { fatalError("missing physicsbody") }
  return Int(body.categoryBitMask >> 6)
}

Обратите внимание, что подкатегории являются всего лишь меткой для поездки; все ваши contactBitMasks будут иметь дело только с основными категориями.

По сути, вы используете тот факт, что у вас есть некоторые дополнительные биты в битовых масках категории тела тела для хранения случайной информации. Я делал это раньше с простыми спрайтами. Но если необходимая информация станет более сложной, чем простое число или индекс, я бы порекомендовал создавать подклассы узлов, а не пытаться спрятать вещи в неиспользуемые биты.

0 голосов
/ 11 января 2020

Используя contact.bodyA.node и contact.bodyB.node, вы можете получить SKNode(s), которые участвуют в контакте

extension GameScene: SKPhysicsContactDelegate {

    func didBegin(_ contact: SKPhysicsContact) {
        switch (contact.bodyA.node, contact.bodyB.node) {
        case (let ball as Ball, let block as Block):
            didBeginContactBetween(ball: ball, andBlock: block)
        case (let block as Block, let ball as Ball):
            didBeginContactBetween(ball: ball, andBlock: block)
        default:
            break
        }
    }

    func didBeginContactBetween(ball: Ball, andBlock block: Block) {
        // TODO: put your code here
    }

}
...