SKCameraNode.position didSet не срабатывает при перемещении с помощью SKAction

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

class InteractiveCameraNode: SKCameraNode {
   // MARK: - Properties
   var enableInteraction = true
   var positionResponders = [(CGPoint, CGPoint) -> Void]()


   /// Calls every closure in the `positionResponders` array
   override var position: CGPoint {
      didSet {
         if enableInteraction {
            for responder in positionResponders {
               responder(oldValue, position)
            }
         }
      }
   }
}

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


person BadgerBadger    schedule 11.01.2017    source источник


Ответы (2)


мой предыдущий ответ был неверным на 100%. Что бы ни привело меня к этому ответу, это была какая-то другая ошибка с моей стороны, и я приношу свои извинения ... Сегодня я тоже узнал кое-что полезное :)

Похоже, что поведение, которое вы видите, является либо А) ошибкой, либо Б) каким-то более глубоким механизмом Swift/SK, который я не понимаю.

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

class Noder: SKSpriteNode {

  var myPosition: CGPoint = CGPoint.zero { didSet { print(position) } }

  func initialize(scene: SKScene) {
    isUserInteractionEnabled = true
    scene.addChild(self)
  }

  override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    position = touches.first!.location(in: self.scene!)
  }

  override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    print(self.position)
    run(.moveBy(x: -35, y: 0, duration: 0.5), completion: {
      self.myPosition = self.position
    })
  }
}

использовать:

// In didmovetoview:
let node = Noder(color: .black, size: CGSize(width: 25, height: 25))
node.initialize(scene: self)

вывод, показывающий, что фактическая позиция была обновлена ​​​​в результате действия и didSet:

(50.0, -118.0)
(14.9999971389771, -118.0)


Но это, вероятно, будет слишком медленным для того, что вы хотите, поэтому вам будет лучше вручную устанавливать position или myPosition в update() каждый кадр (в зависимости от того, какой из них будет работать для didSet, лол ..)

(Спасибо Confused за этот абзац).


Кроме того, мне интересно, будет ли здесь работать KVO.. Я не использовал их, но CameraNode уже является NSObject... что-то, на что стоит обратить внимание, поскольку это, вероятно, какая-то ошибка...

person Fluidity    schedule 12.01.2017
comment
Дорри, что не знал, но... что такое КВО? - person BadgerBadger; 13.01.2017
comment
@BadgerBadger developer.apple.com/library/content/documentation/Cocoa/ - person Fluidity; 13.01.2017
comment
обычно мне не нравятся шаблоны делегата / kvo / messenger / signal (мне нравится делать что-то вручную, сверху вниз), но в этом случае я бы изучил этот вариант, поскольку он звучит именно так, как вы пытаетесь сделать @BadgerBadger - person Fluidity; 13.01.2017
comment
Спасибо! Я посмотрю! - person BadgerBadger; 13.01.2017

Выполнение действия, которое изменяет положение вызывающего подкласса Node, не устанавливает его положение, наблюдатель свойства (в вашем случае didSet) не вызывается, и, таким образом, ваш код никогда не выполняется.

В этом случае решение состоит в том, чтобы использовать SKSceneDelegate и сравнить позицию узла по двум его обратным вызовам. Вы сохраняете начальную позицию узла в update(_:for:), которая вызывается в начале каждого кадра, затем вычисляете любое изменение позиции в didEvaluateActions(for:), которая вызывается после оценки действий.

Подробнее

person Seivan    schedule 02.01.2019