Можно ли обрабатывать вычисления после didApplyConstraints внутри делегирующего класса? - PullRequest
1 голос
/ 16 апреля 2020

Я создал подкласс SKSpriteNode с именем JoyStick, который следует шаблону делегата, где его делегат равен GameScene. В целях дальнейшей очистки GameScene класс JoyStick также реализует свои собственные методы touchesBegan, touchesMoved и touchesEnded.

Проблема в том, что у меня есть некоторые вычисления положения в методе JoyStick touchesMoved, который необходимо выполнить после применения определенного SKConstraints к джойстику (например, при привязке ручки джойстика к основанию). Я знаю, что существует didApplyConstraints метод экземпляра SKScene, и что в документации указано, что он должен быть переопределен и не вызываться, поскольку он уже вызывается один раз за кадр.

Однако я не убедитесь, как интегрировать didApplyConstraints в метод touchesMoved моего делегирующего класса джойстика, не прибегая к перемещению logi c обратно в didApplyConstraints моего GameScene.

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

GameScene.swift

import SpriteKit
import GameplayKit

class GameScene: SKScene {

    var shipNode: SKSpriteNode?
    var joyStick: JoyStick?

    override func didMove(to view: SKView) {

        self.view?.isMultipleTouchEnabled = true

        // Init the ship
        guard let shipNode = self.childNode(withName: "ShipNode") as? SKSpriteNode else {
            fatalError("Player node not loaded!")
        }
        self.shipNode = shipNode

        // Init the joystick
        joyStick = JoyStick()
        joyStick?.isUserInteractionEnabled = true
        joyStick?.position = CGPoint(x: -500, y: -200)
        joyStick?.delegate = self
        self.addChild(joyStick!)
    }
}

extension GameScene: JoyStickDelegate{
    var objPhysicsBody: SKPhysicsBody? {
        return self.shipNode?.physicsBody
    }
}

JoyStick.swift

import Foundation
import SpriteKit

protocol JoyStickDelegate: class {
    var objPhysicsBody: SKPhysicsBody? {get}
}

class JoyStick: SKSpriteNode {
    weak var delegate: JoyStickDelegate!

    var joyVector: CGVector = CGVector()
    var handle: SKSpriteNode?

    init () {
        let baseTexture = SKTexture(imageNamed: "joyStickBase")
        super.init(texture: baseTexture, color: UIColor.clear, size: baseTexture.size())

        // Add the joystick handle onto the base
        handle = SKSpriteNode(imageNamed: "joyStickHandle")
        handle?.position = CGPoint(x: 0 , y: 0)

        // Add constraints
        let range = CGFloat(self.size.width/2 - (handle?.size.width)! / 2)
        let moveRange = SKRange(lowerLimit: 0, upperLimit: range)
        let rangeConstraint = SKConstraint.distance(moveRange, to: CGPoint(x: 0,y: 0))
        handle?.constraints = [rangeConstraint]

        self.addChild(handle!)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func moveJoyStick(_ touches: Set<UITouch>) {
        guard let fingerPosition = touches.first?.location(in: self) else { return }
        self.handle!.position = fingerPosition
    }

    func getJoyVector(handlePosition: CGPoint) -> CGVector {
        return CGVector(dx: handlePosition.x, dy: handlePosition.y)
    }

    func resetJoyStick() {
        delegate?.objPhysicsBody?.velocity = CGVector(dx: 0, dy: 0)
        //animation to return the joystick to the center...
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.moveJoyStick(touches)
        joyVector = getJoyVector(handlePosition: self.handle!.position)
        delegate?.objPhysicsBody?.velocity = joyVector
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.moveJoyStick(touches)
        joyVector = getJoyVector(handlePosition: self.handle!.position)
        delegate?.objPhysicsBody?.velocity = joyVector
    }

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

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

Альтернативная реализация, которая работало правильно, чтобы сохранить методы событий прикосновения внутри класса JoyStick, а также удалить шаблон делегирования вместе. Вместо этого я получил доступ к JoyStick handlePosition (как вектор) внутри GameScene с помощью метода getter и применил его как скорость к методу GameScene * didApplyConstraints. Вот два фрагмента добавленного кода, чтобы сделать эту работу:

Добавить в JoyStick.swift:

func getPositionVelocity() -> CGVector {
    guard let handlePosition = self.handle?.position else { return CGVector() }
    return CGVector(dx: handlePosition.x, dy: handlePosition.y)
}

Добавить в GameScene.swift:

...
//joyStick?.delegate = self
...

override func didApplyConstraints() {
    self.shipNode?.physicsBody?.velocity = self.joyStick?.getPositionVelocity() ?? CGVector()
}

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

В конечном счете, возможно ли избежать накопления в логах после ограничения c в одном определении didApplyConstraints в GameScene для достижения этой функциональности? И почему UIResponder события касания, кажется, всегда обрабатываются до :didApplyConstraints - даже если они кажутся вне SKScene Frame-Cycle?

1 Ответ

0 голосов
/ 30 апреля 2020

У вас есть два объекта с разными методами внутри. Вы не можете переместить оба метода в один класс напрямую. Но это можно сделать следующим образом:

class GameScene: SKScene {
    var onJoysticNeedVerticalPosition: (()-> CGVector)? 
    ....
    override func didApplyConstraints() {
        self.shipNode?.physicsBody?.velocity = onJoysticNeedVerticalPosition?() ?? CGVector()
    }
 }

и

protocol JoyStickDelegate: class {
      var objPhysicsBody: SKPhysicsBody? {get}
      var onJoysticNeedVerticalPosition: (()-> CGVector)? {get set}
}

затем

class JoyStick: SKSpriteNode {
    ...
    init () {
    self.delegate?.onJoysticNeedVerticalPosition = {[weak self] in
            self?.getPositionVelocity()
        }
    }
    ...
    func getPositionVelocity() -> CGVector {
        guard let handlePosition = self.handle?.position else { return CGVector() }
        return CGVector(dx: handlePosition.x, dy: handlePosition.y)
    }
}

Так что на самом деле это все еще два разных объекта, но первый (GameScene) содержит закрытие с логами вычислений c, которые фактически вычисляются во втором (JoyStick), а второй подробно передает его первому с использованием метода делегата.

...