как обнаружить прикосновение к узлу

У меня есть приложение, которое вызывает мяч на экране каждую секунду. теперь я хочу, чтобы пользователь коснулся этих шаров, чтобы они исчезли (removeFromParent()). я так понимаю надо настроить сенсорную функцию через touchesBegan и я так и делаю, вот мой код:

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    for touch: AnyObject in touches{
        let positionOfTouch = touch.location(in: self)
        enumerateChildNodes(withName: "BALL") { (node: SKNode, nil) in
            if positionOfTouch == node.position {
                print("just touched the ball")
            }
            else{
                print("error")
            }
        }
    }

проблема в том, что когда я касаюсь экрана/мяча, консоль печатает ошибка вместо просто коснулся мяча, что означает, что мой код не работает. кроме того, консоль выводит сообщение об ошибке как количество шаров в моем представлении. Я не понимаю, что я делаю неправильно и как на самом деле настроить эту функцию. вот моя функция createBall, которая реализуется из моего класса BallNode (тип SKShapeNode):

    func createBall(){
    let ball = BallNode(radius: 65)
    print(ball.Name)
    print(ball._subName!)

    ball.position.y = ((frame.size.height) - 200)
    let ballXPosition = arc4random_uniform(UInt32(frame.size.width)) // set the ball a randon position from the top of the screen
    ball.position.x = CGFloat(ballXPosition)

    ball.physicsBody?.categoryBitMask = PhysicsCategory.ball // ball's category bitMask
    ball.physicsBody?.collisionBitMask = PhysicsCategory.ball // prevent objects from intersecting
    ball.physicsBody?.contactTestBitMask = PhysicsCategory.topBorder // when need to know if two objects touch each other

    addChild(ball)

}

Вы можете помочь мне с этим? потому что я новичок в быстром, я также хотел бы получить некоторые объяснения об этом обнаружении касания (и касаниях в целом - документ Apple плохой).


person omerc    schedule 20.11.2017    source источник
comment
Некоторые альтернативные варианты перечислены здесь: stackoverflow.com/q/27922198/6658553   -  person nathangitter    schedule 20.11.2017


Ответы (3)


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

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

Объяснение протокола (в меру своих возможностей) сейчас это может показаться немного чрезмерным, но чем быстрее вы изучите и поймете протоколы, тем лучше для вас будет (ИМО).

В этом примере мы будем использовать протокол для установки делегата класса BallNode. Протокол — это набор определенных пользователем «правил», которым должен следовать любой класс, который вы назначаете совместимым с этим протоколом. В моем примере я утверждаю, что для соответствия класса протоколу BallNodeDelegate он должен содержать функцию didClick. Когда вы добавляете BallNodeDelegate после GameScene, вы заявляете, что этот класс будет соответствовать этому протоколу. Поэтому, если в GameScene у вас не было функции didClick, это вызовет ошибку. Все это сделано для того, чтобы у вас был простой способ связи между вашими экземплярами BallNode и вашим классом GameScene (без необходимости передавать ссылки на вашу GameScene каждому BallNode). Затем у каждого BallNode есть делегат (GameScene), которому вы можете передать информацию.

внутри вашего класса BallNode убедитесь, что у вас есть isUserInteraction = true

за пределами вашего класса BallNode создайте протокол, который будет отправлять информацию о касании обратно в GameScene.

protocol BallNodeDelegate: class {
    func didClick(ball: BallNode)
}

создайте переменную делегата в вашем классе BallNode

weak var delegate: BallNodeDelegate!

двигать касания начал вам класс BallNode

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    self.delegate?.didClick(ball: self)
}

в GameScene добавить соответствие протоколу BallNode

class GameScene: SKScene, BallNodeDelegate

в GameScene, когда вы создаете мяч, убедитесь, что вы установили его делегата

let ball = BallNode()
ball.delegate = self

в GameScene добавить гнездо. func для обработки кликов

func didClick(ball: BallNode) {
    print("clicked ball")
}
person Ron Myschuk    schedule 20.11.2017
comment
Можете ли вы объяснить мне, почему использовать протокол? Я только что прочитал об этом в Apple doc, но я не совсем понимаю его цель - person omerc; 20.11.2017
comment
уверен, что я добавил и отредактировал свой ответ, чтобы, надеюсь, помочь вам понять протоколы - person Ron Myschuk; 20.11.2017
comment
Вот это да! его Wirkung потрясающий! большое спасибо! у вас есть учебник/объяснение о делегате? - person omerc; 23.11.2017
comment
еще одна вещь, я не очень понимаю, что вы сделали в строке «ball.delegate = self», где это «я» принадлежит? - person omerc; 23.11.2017
comment
self ваша игровая сцена. это то, что связывает 2 класса вместе - person Ron Myschuk; 23.11.2017

Вы сравниваете точную точку касания с точной позицией узла, которые вряд ли когда-либо будут одинаковыми.

if positionOfTouch == node.position {

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

Один из вариантов — использовать функцию contains SKNode, которая сделает это за вас.

if node.contains(positionOfTouch) {

Примечание. Возможно, вы захотите использовать SKSpriteNode вместо SKShapeNode, так как SKShapeNode имеет низкую производительность в SpriteKit.

person nathangitter    schedule 20.11.2017
comment
о вас примечание: что вы подразумеваете под плохой производительностью в SpriteKit? - person omerc; 20.11.2017
comment
Вы получите более низкую частоту кадров на менее мощных устройствах, если у вас будет много узлов формы в сцене одновременно. См. stackoverflow.com/q/23344235/6658553. - person nathangitter; 20.11.2017
comment
На самом деле, я сейчас не перед своим компьютером, я проверю его как можно скорее. Спасибо! - person omerc; 21.11.2017

Взгляните на nodes(at:CGPoint), определенный в SKNode, чтобы получить список узлов в позиции касания. Однако вам нужно будет преобразовать координаты вида и координаты сцены, используя convertPoint(fromView). Документация здесь и здесь.

person Albertsen    schedule 20.11.2017