是否可以在委托类中的 ApplyConstraints 之后处理计算?

Posted

技术标签:

【中文标题】是否可以在委托类中的 ApplyConstraints 之后处理计算?【英文标题】:Is it possible to handle calculations after didApplyConstraints inside of a Delegating class? 【发布时间】:2020-04-16 02:03:56 【问题描述】:

我创建了一个名为 JoyStickSKSpriteNode 的子类,它遵循委托模式,其中它的委托是 GameScene。为了进一步清理GameSceneJoyStick 类还实现了自己的touchesBegantouchesMovedtouchesEnded 方法。

问题是我在JoySticktouchesMoved方法中有一些位置计算需要在将某些SKConstraints应用于操纵杆后进行(例如将操纵杆手柄绑定到底座) .我知道SKScene 有一个didApplyConstraints 实例方法,并且文档表明它应该被覆盖而不是调用,因为它已经每帧调用一次。

但是,我不知道如何集成 didApplyConstraints 到我的委派操纵杆类的touchesMoved 方法中,而无需将逻辑移回我的GameScenedidApplyConstraints

这是我目前使用委托模式实现的简化示例。请注意,以下代码允许变量 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 类中,但同时也放弃了委托模式。相反,我通过getter 方法访问GameScene 内的JoyStick handlePosition(作为向量),并将其作为GameScenedidApplyConstraints 方法内的速度应用。以下是添加代码的两个 sn-ps 以使其工作:

添加到 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()

虽然这实现了仅应用受操纵杆半径限制的速度的预期行为,但它似乎远没有那么优雅,并且大大减少了我对委托模式的封装/通用性 - 随着游戏的发展,这种情况变得很明显.

最终,是否有可能避免将后约束逻辑堆积到 GameScene 中的单个 didApplyConstraints 定义中以实现此功能?为什么UIResponder 触摸事件似乎总是在:didApplyConstraints 之前处理——即使它们似乎在SKScene Frame-Cycle 之外?

【问题讨论】:

如果您查看 SpriteKit 生命周期,并且如果我理解您的问题,也许您可​​以在 :update 上进行“距离计算”?那是在 didApplyConstraints 之后,当然是在下一次渲染时。我没有看到那里的问题。 @Maetschl 我想我会遇到同样的问题 - 我在操纵杆的 touchesMoved 方法(GameScene 之外)中有计算,所以我不确定如何确保它被调用更新?我想我对 touchesMoved 究竟在哪里适合更广泛的 :update 或 :didApplyConstraints 等帧周期事件感到困惑? 如果您使用 GameplayKit,您可以直接调用场景的覆盖更新或 GKComponent 的更新时间间隔。 @Maetschl 你能详细说明一下吗?我可以调用 SKScene 代表的覆盖 :update 吗?我认为不应直接调用此方法,因为它已经每帧调用一次?另外,从委派班级的touchesMoved 调用它会做什么?这会确保在执行touchesMoved 代码时调用didApplyConstraints 吗? 也许如果您编辑您的问题并添加一些代码,我可以为您提供更多帮助(请不要粘贴所有代码,为此目的做一个简单的示例) 【参考方案1】:

你有两个对象,里面有不同的方法。您不能将这两种方法直接移动到一个类中。 但是可以这样做:

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)持有带有计算逻辑的闭包,实际上是在第二个(JoyStick)中计算出来的,第二个将其详细介绍给第一个使用委托方法。

【讨论】:

谢谢,但这是一个混淆解决方案,实际上并不需要使用委托模式,因为它可以简单地引用self.joyStick?.getPositionVelocity(),如我的第二个解决方案所示。目标是避免将这种逻辑放在一个集中的 didApplyConstraints()GameScene 中,而是寻找一种方法将其完全封装在给定的 SKSpriteNode 中,例如使用委托模式的 joyStick。跨度>

以上是关于是否可以在委托类中的 ApplyConstraints 之后处理计算?的主要内容,如果未能解决你的问题,请参考以下文章

UIView 类中的 Swift 集合视图委托

通过委托为另一个类中的手势识别器调用选择器

是否可以在委托方法中使用完成处理程序 - Swift

iOS - 无法访问 UITableViewCell 类中的 UITextView 委托

NSObject 类中的委托

如何在 TableViewCell 类中的 TextField 委托方法上获取单元格索引