5

如何用一根手指“触摸并拖动”旋转 SpriteNode,以便:

  • 它不会晃来晃去
  • 它在一个圆圈中移动(我已经成功完成了这部分几次 - 使用纯代码解决方案和使用 SKS 文件)
  • 它产生一个有意义的值(就像物理控制旋钮一样)
  • 当我的手指在它上面时它会移动,但当我的手指不在它上面时它不会移动

我尝试过的事情:

使用 CGAffineTransform 的 CGAffineTransformMakeRotation来实现 SpriteKit 中旋钮的旋转。但我无法弄清楚如何在 SpriteNode 上使用 CGAffineTransformMakeRotation。我可以将不同类型的对象放入我的场景中或在其之上,但这是不对的。

例如,Matthijs Hollemans 的 MHRotaryKnob https://github.com/hollance/MHRotaryKnob 。我将 Hollemans 旋钮从 Objective C 翻译成 Swift 4,但在 SpriteKit 中使用它来旋转精灵时遇到了麻烦。我没有明白这一点,因为我不知道如何knobImageView.transform = CGAffineTransformMakeRotation (newAngle * M_PI/180.0);在 Swift 中使用 SpriteKit。我知道我可以使用 Hollemans Objective C 类并将 UIImage 推到我的场景顶部,但这似乎不是最好的也不是最优雅的解决方案。

我还将Wex的解决方案从 Objective C 翻译为 Swift Rotate image on center using one finger touch 使用Allan Weir关于处理代码的 CGAffineTransform 部分的建议https://stackoverflow.com/a/41724075/1678060 但那不起作用。

我尝试直接在我的精灵上设置zRotation而不使用 .physicalBody 无济于事。它具有相同的生涩运动,并且不会在您希望它停止的地方停止。并以与您的手指拖动相反的方向移动 - 即使您将“-”放在弧度角的前面。

我还在Stack Overflow 上 尝试了 0x141E的解决方案:围绕固定点拖动旋转节点 这是下面使用 .sks 文件发布的解决方案(稍作修改-我尝试了未修改的版本,但效果并不好)。该解决方案会晃动,无法顺利跟随我的手指,无法始终将旋钮移动到特定点。如果我设置physicsBody 属性以创建摩擦、质量或angularDamping 和linearDamping 或降低SKSpriteNode 的速度,这无关紧要。

我还在互联网上搜索使用 SpriteKit 在 Swift 4 中寻找一个好的解决方案,但到目前为止无济于事。

import Foundation
import SpriteKit

class Knob: SKSpriteNode
{
    var startingAngle: CGFloat?
    var currentAngle: CGFloat?
    var startingTime: TimeInterval?
    var startingTouchPoint: CGPoint?

    override init(texture: SKTexture?, color: UIColor, size: CGSize) {
        super.init(texture: texture, color: color, size: size)
        self.setupKnob()
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.setupKnob()
    }
    func setupKnob() {
        self.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(self.size.height))
        self.physicsBody?.pinned = true
        self.physicsBody?.isDynamic = true
        self.physicsBody?.affectedByGravity = false
        self.physicsBody?.allowsRotation = true
        self.physicsBody?.mass = 30.0
        //self.physicsBody?.friction = 0.8
        //self.physicsBody?.angularDamping = 0.8
        //self.physicsBody?.linearDamping = 0.9
        //self.speed = 0.1

        self.isUserInteractionEnabled = true
    }
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in touches {
            let location = touch.location(in:self)
            let node = atPoint(location)
            startingTouchPoint = CGPoint(x: location.x, y: location.y)
            if node.name == "knobHandle" {
                let dx = location.x - node.position.x
                let dy = location.y - node.position.y
                startingAngle = atan2(dy, dx)
            }
        }
    }
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in touches{
            let location = touch.location(in:self)
            let node = atPoint(location)
            guard startingAngle != nil else {return}
            if node.name == "knobHandle" {
                let dx:CGFloat = location.x - node.position.x
                let dy:CGFloat = location.y - node.position.y
                var angle: CGFloat = atan2(dy, dx)

                angle = ((angle) * (180.0 / CGFloat.pi))
                let rotate = SKAction.rotate(toAngle: angle, duration: 2.0, shortestUnitArc: false)
                self.run(rotate)

                startingAngle = angle
            }
        }
    }
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        var touch: UITouch = touches.first!
        var location: CGPoint = touch.location(in: self)

        self.removeAllActions()
        startingAngle = nil
        startingTime = nil
    }
}

编辑:如果我删除转换为度数并将 SKAction 的持续时间更改为 1.0,SKAction.rotate(toAngle:duration: 1.0, shortestUnitArc:)那么它几乎可以工作:不像生涩,但仍然是混蛋;控制杆不会改变方向 - 这意味着有时如果您尝试将其移动到与行进方向相反的方向,它会继续围绕锚点沿旧方向移动,而不是拖动它的新方向。

编辑 2:GreatBigBore和我讨论了 SKAction 旋转和 self.zRotation——上面的代码和下面的代码。

编辑 3:sicvayne为 SKScene 建议了一些代码,我已经适应了 SKSpriteNode(如下)。它不会始终如一地移动,也不会让您停在特定的地方。

import Foundation
import SpriteKit

class Knob: SKSpriteNode {

    var fingerLocation = CGPoint()

    override init(texture: SKTexture?, color: UIColor, size: CGSize) {
        super.init(texture: texture, color: color, size: size)
        self.setupKnob()
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.setupKnob()
    }
    func setupKnob() {
        self.isUserInteractionEnabled = true
    }
    func rotateKnob(){
        let radians = atan2(fingerLocation.x - self.position.x, fingerLocation.y - self.position.y)
        self.zRotation = -radians
    }
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in touches {
        }
    }
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in touches {

            fingerLocation = touch.location(in: self)
        }
        self.rotateKnob()
    }
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    }
    /*override func update(_ currentTime: TimeInterval) { //this is a SKScene function
     rotateKnob()
     }*/
}
4

2 回答 2

3

我通常会做这样的事情,没有任何抖动或抽搐的问题。

var fingerLocation = CGPoint()
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    for touch: AnyObject in touches {
        fingerLocation = touch.location(in: self)


    }
}
func rotatePlayer(){
    let radians = atan2(fingerLocation.x - playerNode.position.x, fingerLocation.y - playerNode.position.y)
    playerNode.zRotation = -radians//this rotates the player
}

override func update(_ currentTime: TimeInterval) {

       rotatePlayer()


}

根据您的图像面对的方式,您可能不得不弄乱弧度。就我而言,我的“玩家”形象朝上。希望这有帮助。

于 2018-03-24T02:33:21.220 回答
3

数学错了。这是我学到的你需要的东西: 求角度的数学公式

这看起来如何:

if point.x.sign == .minus {
    angle = atan(point.y/point.x) + CGFloat.pi/2
} else {
    angle = atan(point.y/point.x) + CGFloat.pi/2 + CGFloat.pi
}

此外,您必须获取场景中另一个对象的坐标,因为整个坐标系会随着对象旋转:

let body = parent?.childNode(withName: "objectInScene")
let point = touch.location(in: body!)
于 2018-05-25T15:57:20.673 回答