是否可以在委托类中的 ApplyConstraints 之后处理计算?
Posted
技术标签:
【中文标题】是否可以在委托类中的 ApplyConstraints 之后处理计算?【英文标题】:Is it possible to handle calculations after didApplyConstraints inside of a Delegating class? 【发布时间】:2020-04-16 02:03:56 【问题描述】:我创建了一个名为 JoyStick
的 SKSpriteNode
的子类,它遵循委托模式,其中它的委托是 GameScene
。为了进一步清理GameScene
,JoyStick
类还实现了自己的touchesBegan
、touchesMoved
和touchesEnded
方法。
问题是我在JoyStick
的touchesMoved
方法中有一些位置计算需要在将某些SKConstraints
应用于操纵杆后进行(例如将操纵杆手柄绑定到底座) .我知道SKScene
有一个didApplyConstraints
实例方法,并且文档表明它应该被覆盖而不是调用,因为它已经每帧调用一次。
但是,我不知道如何集成 didApplyConstraints
到我的委派操纵杆类的touchesMoved
方法中,而无需将逻辑移回我的GameScene
的didApplyConstraints
。
这是我目前使用委托模式实现的简化示例。请注意,以下代码允许变量 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
(作为向量),并将其作为GameScene
的didApplyConstraints
方法内的速度应用。以下是添加代码的两个 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 之后处理计算?的主要内容,如果未能解决你的问题,请参考以下文章