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

Я создаю игру со SpriteKit, которая включает мячи как SKShapeNode. Я создаю класс, определяющий мячи и их свойства (включая SKPhysicsBody). шарики должны бегать по экрану, а рамка — это граница экрана (с помощью edgeLoopFrom: self.frame). Я также создал узел пути, который расположен в верхней части экрана. теперь я хочу сделать это, если какой-то шар достигнет верхней границы кадра, поэтому будет выполнена какая-то функция. Я кое-что читал об этом, и я не уверен, как правильно это сделать, если использовать contactBitMask или есть другой и лучший вариант. Если правильный способ - это contactBitMask - мне нужно установить структуру для узла balls или я могу установить ее внутри своего класса? Спасибо!


person omerc    schedule 12.11.2017    source источник


Ответы (1)


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

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

Spritekit

проверьте ссылку выше. Что вам нужно сделать, так это реализовать SKPhysicsContactDelegate. Это позволит вам получить доступ к функциям didBegin() и didEnd(). Эти функции вызываются при установлении контакта в физическом мире.

class YourClass: SKScene, SKPhysicsContactDelegate {
    func didBegin(_ contact: SKPhysicsContact) {
    }

    func didEnd(_ contact: SKPhysicsContact) {
    }
}

Чтобы эти функции вызывались, вам нужно установить contactDelegate физического мира в класс, который будет обрабатывать вызовы. Это будет ваша сцена, и хорошим местом для ее установки является функция didMove().

class YourClass: SKScene, SKPhysicsContactDelegate {
    func didMove(to view: SKView) {
        physicsWorld.contactDelegate = self
    }

Теперь, когда происходит обнаружение контакта, будет вызываться метод didBegin(), а когда контакт завершается, будет вызываться didEnd().

Теперь нам нужно дать нашим узлам несколько физических тел, и мы можем установить для них разные битовые маски, чтобы обнаруживать столкновения/контакты. Это 3 битовые маски, о которых мы беспокоимся:

categoryTestBitMask
collisionTestBitMask
contactTestBitMask

categoryTestBitMask: вы можете присвоить узлам похожего типа категорию, например, "мяч" может быть категорией. Все ваши различные объекты мяча могут иметь эту же категорию. Я использую категорию «noCollision», когда хочу обнаружить контакт, но не хочу, чтобы произошло столкновение. Вы ограничены 32 различными категориями, так что не сходите с ума с кучей разных.

collisionTestBitMask: присвойте своему «мячу» категорию, с которой вы хотите, чтобы произошло столкновение. Пример: установите маску теста столкновения для вашего «мяча» на битовую маску категории «стена». Столкновение — это когда 2 объекта физически сталкиваются друг с другом; так что ваш мяч будет отскакивать от стен.

contactTestBitMask: контакт — это когда 2 узла перекрываются. Таким образом, вместо того, чтобы мяч отскочил от чего-либо, он вызовет метод contact для нашего делегата. Обратите внимание, что вы можете установить и коллизию, и контактную битовую маску на одно и то же.

Теперь, как мы устанавливаем эти маски. Я использую Struct, чтобы присваивать имена битовым маскам и устанавливать эти 3 разные маски с помощью кода. Что-то вроде этого:

struct Mask {
static var ball: UInt32 = 0b10 //2
static var wall: UInt32 = 0b100 //4
static var pathNode: UInt32 = 0b1000 //8
}

теперь в коде вы можете установить маски:

let ball = SKSpriteNode()
ball.name = "ball"
ball.physicsBody = SKPhysicsBody()
ball.physicsBody.categoryTestBitMask = Mask.ball
ball.physicsBody.collisionTestBitMask = Mask.wall
ball.physicsBody.contactTestBitMask = Mask.pathNode | Mask.wall

let pathNode = SKSpriteNode()
pathNode.name = "pathNode"
pathNode.physicsBody = SKPhysicsBody()
pathNode.physicsBody.categoryTestBitMask = Mask.pathNode
pathNode.physicsBody.collisionTestBitMask = 0
pathNode.physicsBody.contactTestBitMask = Mask.pathNode

Давайте посмотрим, что мы говорим здесь. Мы создаем объект-мяч и устанавливаем его категорию «мяч». Мы говорим, что хотим, чтобы он сталкивался с объектами «стена», и мы хотим, чтобы наши функции делегата контакта срабатывали с объектами «pathNode» ИЛИ объекты «стены». Наш объект pathNode не будет иметь столкновений и будет иметь контакт с мячом.

В основном мяч отскакивает от стен и проходит через pathNode. Он вызовет функции делегата контакта didbegin() и didend() как с объектами pathNode, так и с объектами стены.

Еще не закончено... Итак, когда функция вызывается, как мы с этим справимся? Когда вызывается функция didbegin или didend, она имеет параметр «contact». этот параметр контакта имеет 2 тела для работы, и это тела, которые контактировали друг с другом. Есть несколько способов справиться с этим, но пока я покажу вам простой способ.

func didBegin(_ contact: SKPhysicsContact) {
    if contact.bodyA!.node!.name == "ball" {
    // bodyA is our ball
        switch contact.bodyB!.node!.name {
        case "pathNode":
            thisIsMyBallHitPathNodeFunction()
        case "wall":
            thisIsMyBallHitWallFunction()
        default:
            break
        }
    }
    else if contact.bodyB!.node!.name == "ball" {
    // bodyB is our ball
        switch contact.bodyA!.node!.name {
        case "pathNode":
            thisIsMyBallHitPathNodeFunction()
        case "wall":
            thisIsMyBallHitWallFunction()
        default:
            break
        }

    }

}

Обновление: здесь мы выясняем тип bodyA и bodyB. Итак, все начинается с bodyA. Если bodyA — это «мяч», то мы знаем, что bodyA — это «мяч», а bodyB — это то, с чем мяч соприкасался. Затем мы используем оператор switch, чтобы выяснить, что такое bodyB. зная, что такое bodyB, мы вызываем функцию, которую нам нужно вызвать для этого конкретного контакта между этими двумя узлами.

Затем вы просто помещаете свой код в указанные функции того, что вы хотите сделать.

Это может быть много, чтобы понять сразу. Если вы новичок, я бы посоветовал попробовать это и попытаться заставить его работать. После этого я бы разместил на YouTube несколько видеороликов о том, как это сделать. Это хорошая идея — посмотреть, как разные люди справляются с одним и тем же, а затем решить для себя, как это сделать. Возможно, это не самый элегантный способ обращения с контактами, но он хорошо работает и с некоторой практикой станет вашей второй натурой. Удачи!

person Discoveringmypath    schedule 14.11.2017
comment
большое спасибо. это работает! продолжая этот вопрос, я задаю еще один [ссылка] stackoverflow.com/questions/47271850/ [ссылка] решит ли этот ответ мой второй? - person omerc; 14.11.2017
comment
Посмотрю, рад, что смог помочь! - person Discoveringmypath; 14.11.2017
comment
Можете ли вы объяснить мне, как работает функция didBegin? Я не понимаю смысла и использования bodyA и bodyB. - person omerc; 15.11.2017
comment
Если вы прочитаете эту ссылку в ответе, она должна пройти по ней. Кроме того, посмотрите несколько видеороликов на YouTube, если у вас возникли проблемы с пониманием. Но в основном, когда в вашей игре есть контакт между двумя узлами, срабатывает didBegin. bodyA и bodyB — это два узла physicsBodies, которые вступили в контакт. Функция не знает, к какому типу узлов они относятся, поэтому вам нужно проверить (что я сделал с моими операторами if). - person Discoveringmypath; 15.11.2017