【问题标题】:Drag Rotate a Node around a fixed point拖动围绕固定点旋转节点
【发布时间】:2015-08-21 14:48:50
【问题描述】:

我正在尝试创建一个类似于this question 中的“奖轮”的可旋转节点。到目前为止,我有投掷能力,使用 UIPanGestureRecognizer 在物理体上添加角度脉冲,效果非常好。我也可以通过触摸停止旋转。

现在我尝试允许使用拖动或滑动手势微调滚轮,因此如果玩家对最终的结果不满意,他们可以手动旋转/拖动/旋转到他们喜欢的旋转。

目前我将触摸的位置保存在 touchesBegan 中,并尝试在更新循环中增加我的节点的 zRotation。

旋转不跟随我的手指并且是生涩的。我不确定我是否对手指运动有足够准确的读数,或者手指的变化位置是否没有准确地转换为弧度。我怀疑检测到触摸然后在更新中处理它不是一个很好的解决方案。

这是我的代码。

    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent?) {
    if let touch = touches.first as? UITouch {
        var location = touch.locationInView(self.view)
        location = self.convertPointFromView(location)

        mostRecentTouchLocation = location

        let node = nodeAtPoint(location)
        if node.name == Optional("left") && node.physicsBody?.angularVelocity != 0
        {
            node.physicsBody = SKPhysicsBody(circleOfRadius:150)
            node.physicsBody?.applyAngularImpulse(0)
            node.physicsBody?.pinned = true
        }
    }
}

    override func update(currentTime: CFTimeInterval) {
        /* Called before each frame is rendered */
        if mostRecentTouchLocation != CGPointZero{
        let node = nodeAtPoint(mostRecentTouchLocation)
        if node.name == Optional("left")
        {
            var positionInScene:CGPoint = mostRecentTouchLocation
            let deltaX:Float = Float(positionInScene.x) - Float(node.position.x)
            let deltaY:Float = Float(positionInScene.y) - Float(node.position.y)
            let angle:CGFloat = CGFloat(atan2f(deltaY, deltaX))
            let maths:CGFloat = angle - (CGFloat(90) * (CGFloat(M_PI) / 180.0))
            node.zRotation += maths
            mostRecentTouchLocation = CGPointZero
        }
    }
}

我在更新中将一些数学分布分布在多行中,以使调试更容易一些。

如果需要,我可以添加 PanGestureRecognizer 代码,但我会尽量保持简短。

编辑 这是我基于 GilderMan 推荐的最新代码。我认为它工作得更好,但旋转远非顺利。它的跳跃幅度很大,并且没有很好地跟随手指。这是否意味着我的角度计算有问题?

    override func didSimulatePhysics() {
    if mostRecentTouchLocation != CGPointZero {
        let node = nodeAtPoint(mostRecentTouchLocation)
        if node.name == Optional("left")
        {
            var positionInScene:CGPoint = mostRecentTouchLocation
            let deltaX:Float = Float(positionInScene.x) - Float(node.position.x)
            let deltaY:Float = Float(positionInScene.y) - Float(node.position.y)
            let angle:CGFloat = CGFloat(atan2f(deltaY, deltaX))
            node.zRotation += angle
            println(angle)
            mostRecentTouchLocation = CGPointZero
        }
    }
}

【问题讨论】:

  • 你原帖的角度转换没有问题
  • 所以 atan2f 结果是以弧度为单位的,这是添加到 zRotation 的正确单位?
  • 是的,zRotationatan2f 的单位是弧度

标签: swift sprite-kit


【解决方案1】:

以下代码模拟了一个基于触摸旋转的奖品轮盘。当用户的手指移动时,滚轮与手指的速度成比例地旋转。当用户在滚轮上滑动时,滚轮将与滑动速度成比例地旋转。您可以更改物理体的 angularDamping 属性以减慢或增加车轮停止的速度。

class GameScene: SKScene {
    var startingAngle:CGFloat?
    var startingTime:TimeInterval?

    override func didMove(to view: SKView) {
        let wheel = SKSpriteNode(imageNamed: "Spaceship")
        wheel.name = "wheel"
        wheel.setScale(0.5)
        wheel.physicsBody = SKPhysicsBody(circleOfRadius: wheel.size.width/2)
        // Change this property as needed (increase it to slow faster)
        wheel.physicsBody!.angularDamping = 0.25
        wheel.physicsBody?.pinned = true
        wheel.physicsBody?.affectedByGravity = false
        addChild(wheel)
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in touches {
            let location = touch.location(in:self)
            let node = atPoint(location)
            if node.name == "wheel" {
                let dx = location.x - node.position.x
                let dy = location.y - node.position.y
                // Store angle and current time
                startingAngle = atan2(dy, dx)
                startingTime = touch.timestamp
                node.physicsBody?.angularVelocity = 0
            }
        }
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in touches{
            let location = touch.location(in:self)
            let node = atPoint(location)
            if node.name == "wheel" {
                let dx = location.x - node.position.x
                let dy = location.y - node.position.y

                let angle = atan2(dy, dx)
                // Calculate angular velocity; handle wrap at pi/-pi
                var deltaAngle = angle - startingAngle!
                if abs(deltaAngle) > CGFloat.pi {
                    if (deltaAngle > 0) {
                        deltaAngle = deltaAngle - CGFloat.pi * 2
                    }
                    else {
                        deltaAngle = deltaAngle + CGFloat.pi * 2
                    }
                }
                let dt = CGFloat(touch.timestamp - startingTime!)
                let velocity = deltaAngle / dt

                node.physicsBody?.angularVelocity = velocity

                // Update angle and time
                startingAngle = angle
                startingTime = touch.timestamp
            }
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        startingAngle = nil
        startingTime = nil
    }
}

【讨论】:

  • 哇,太棒了,非常感谢!大约在 8 点左右,精灵似乎在翻转,你看到过这种行为吗?
  • 是的,我会尽力解决这个问题
  • 效果很好,非常感谢。我将花一些时间仔细阅读以确保我完全理解它。
  • 我添加了一项检查,以确保当触摸从节点开始并继续移动时,touchesmoved 中的起始角度不为零。否则效果很好!
【解决方案2】:

SKNodezRotation 以弧度表示。您可以取消转换为度数。

您可能希望在didSimulatePhysics 中进行角度调整,以便在应用物理后计算zRotation。 (这可能并不直接适用于这种情况,但这是一种很好的做法。)

【讨论】:

  • 谢谢我试试看
  • 感谢您的建议!这比我所取得的要近得多,但仍然不能很好地跟随手指,我认为我的角度计算必须关闭。我也有点担心 mostRecentTouchLocation 可能没有按照我的想法做。有任何想法吗?我已经把最新的代码放在上面了。
  • 好的,我假设您正试图让轮边跟随您的手指。你有一个角度的增量,你需要做的就是设置角度。所以像node.zRotation = angle 而不是node.zRotation += angle。然后它应该跟随你的手指。
  • 这似乎将精灵的旋转设置回接近 0(如果是时钟,则在 12 位置)。这是否意味着我应该使用 += 因为旋转很小并且应该添加?
  • 等等,我们是在计算手指转动方向盘的位置,还是在做其他事情?您的问题似乎是针对用手指控制的。您可能需要将 M_PI_2 (Pi / 2) 添加或减去手指代码的计算角度。
【解决方案3】:

我尝试了下面的两组代码。

代码示例 1

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        for touch in touches {
            let location = touch.location(in: self)
            
            let node = atPoint(location)
            guard let safeNode = node.parent else { break }
            
            if safeNode.name == "wheel" {
                let wheel = node.parent!
                

                let dx = location.x
                let dy = location.y
             
                startingAngle = atan2(dx, dy)
                startingTime = touch.timestamp
                
            }
        }
    }
    
    
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

        for touch in touches {
            let location = touch.location(in: self)
            let node = atPoint(location)
            guard let safeNode = node.parent else { break }
            if safeNode.name == "wheel" {
                let wheel = node.parent!

                if !isRotating {

                    let dx = location.x
                    let dy = location.y

                    let angle = atan2(dx, dy)
                    
                    wheel.zRotation = -angle
                    //update angle and time
                    startingAngle = angle
                    startingTime = touch.timestamp

                    
                }
            }
        }
    }

代码示例 2

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    
    for touch in touches {
        let location = touch.location(in: self)
        
        let node = atPoint(location)
        guard let safeNode = node.parent else { break }
        
        if safeNode.name == "wheel" {
            let wheel = node.parent!
            

            let dx = location.x
            let dy = location.y
            
            startingAngle = atan2(dx, dy)
            startingTime = touch.timestamp
            
        }
    }
}



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

        for touch in touches {
            let location = touch.location(in: self)
            let node = atPoint(location)
            guard let safeNode = node.parent else { break }
            if safeNode.name == "wheel" {
                let wheel = node.parent!


                if !isRotating {

                    let dx = location.x
                    let dy = location.y

                    let angle = atan2(dx, dy)

                    let deltaAngle = angle - startingAngle!
                    
                    wheel.zRotation -= deltaAngle

                    //update angle and time
                    startingAngle = angle
                    startingTime = touch.timestamp
                    
                }
            }
        }
    }

代码示例 1 和 2 之间的唯一区别是查找车轮的zRotation 的方法。

代码示例 1 使用 wheel.zRotation = -angle。它确保滚轮精确地跟随手指位置。但是有一个bug,当你触摸轮子中两个较远的位置然后移动你的手指时,轮子会跳位。我仍然无法修复此错误。

代码示例 2 使用 wheel.zRotation -= deltaAngle 来堆叠每个手指移动的角度变化。它假设生成与代码示例 1 相同的结果,但它并不完全跟随手指移动。

我通过比较代码示例 1 和 2 的 zPosition 与相同的手指移动找到了原因。代码示例 2 中的 deltaAngle 略少(最多 8 位小数),因此轮子不会完全跟随手指运动。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-18
    • 1970-01-01
    • 2015-05-02
    • 2013-05-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多