Я создал подкласс 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
?