绕固定点旋转节点的拖动

9

我正在尝试创建一个可旋转的节点,类似于这个问题中的“奖品轮”。到目前为止,我已经实现了甩动功能,通过使用UIPanGestureRecognizer在物理体上添加角动量来实现,效果非常好。我还可以使用触摸停止旋转。

现在,我正在尝试使用拖动或滑动手势来允许对轮子进行微调,以便如果玩家对最终结果不满意,他们可以手动旋转/拖动/旋转它以达到他们喜欢的旋转位置。

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

旋转不会跟随我的手指移动,并且很卡顿。我不确定是否准确地读取了手指移动的信息,或者手指的位置变化没有被准确地转换成弧度。我怀疑在检测到触摸后在update中处理它并不是一个好的解决方案。

这是我的代码。

    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
        }
    }
}

你原始帖子中的角度转换没有任何问题。 - 0x141E
那么 atan2f 的结果是弧度,这是正确的单位可以添加到 zRotation 中吗? - ThrowingSpoon
1
是的,zRotationatan2f的单位是弧度。 - 0x141E
3个回答

10
以下代码模拟了一个奖轮,根据触摸旋转。当用户的手指移动时,轮子会按照手指的速度成比例地旋转。当用户在轮子上滑动时,轮子会按照滑动的速度成比例地旋转。您可以更改物理体的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
    }
}

哇,太不可思议了,非常感谢!大约在八点左右,精灵似乎会翻转,你看到这种行为了吗? - ThrowingSpoon
1
是的,我会尝试修复它。 - 0x141E
非常好,非常感谢。我会花一些时间仔细阅读这个,确保我完全理解它。 - ThrowingSpoon
我已经添加了一个检查,以确保在触摸从节点上开始并移动时,起始角度不为nil。否则效果很好! - ThrowingSpoon

0

我尝试了下面两组代码。

代码示例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。它确保轮子完全跟随手指位置。但是有一个错误,当您在轮子上触摸两个远离的位置然后移动手指时,轮子会跳跃位置。我仍然无法解决这个问题。

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

通过比较代码示例1和2在相同手指移动下的zPosition,我发现了原因。代码示例2中的deltaAngle略小(高达8个小数位),因此轮子不能完全跟随手指移动。


0

SKNodezRotation 是以弧度为单位的。您可以省略将其转换为角度的步骤。

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


谢谢,我会尝试一下。 - ThrowingSpoon
谢谢您的建议!这比我之前实现的更接近了,但仍然不能很好地跟随手指移动,我认为我的角度计算可能有误。我还稍微担心 mostRecentTouchLocation 明显可能不是我想象中的那样。有什么想法吗?我已经放上了最新的代码。 - ThrowingSpoon
这似乎将精灵的旋转设置回接近0(如果它是时钟,则在12位置)。这不意味着我应该使用+=,因为旋转很小,应该添加吗? - ThrowingSpoon
等一下,我们是在计算手指转动方向盘的位置,还是在做其他事情?你的问题似乎是关于用手指控制的。对于手指代码,你可能需要加上或减去M_PI_2(Pi / 2)来计算角度。 - Gliderman
也许我误读了问题,但您是想直接控制方向盘,当您的手指围绕方向盘中心旋转时,方向盘以与您手指移动相同的速度旋转?还是说您希望方向盘随着手指从中心/垂直位置越来越远而加速? - Gliderman
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接