如何限制两条锚定线的运动,使它们像钟摆一样连续摆动

时间:2017-05-29 11:31:34

标签: swift swift3 sprite-kit

我创建了两条固定在精灵上的线,它们相距30˚。我希望两条线像摆锤一样左右摆动,总是从一端到另一端摆动(这样它们在它们的初始位置左右摆动45°)。请看下面我想要实现的目标:

enter image description here

以下是我能够实现的代码:

extension Int {
  var degreesToRadians: Double { return Double(self) * .pi / 180 }
}
extension FloatingPoint {
  var degreesToRadians: Self { return self * .pi / 180 }
  var radiansToDegrees: Self { return self * 180 / .pi }
}

class GameScene: SKScene, SKPhysicsContactDelegate {

var anchorSprite = SKSpriteNode(imageNamed: "swingPin")
var armLeft = SKSpriteNode(imageNamed: "swingArm")
var armRight = SKSpriteNode(imageNamed: "swingArm")

override func didMove(to view: SKView) {


    self.physicsWorld.gravity = CGVector(dx: 0, dy: -1.8)
    self.physicsWorld.contactDelegate = self

    var tealBg = SKSpriteNode(imageNamed: "tealBg")
    tealBg.position = CGPoint(x: frame.midX, y: frame.midY)
    tealBg.zPosition = 10
    addChild(tealBg)

    anchorSprite.position = CGPoint(x: frame.midX, y: frame.midY + frame.midY/2)
    anchorSprite.zPosition = 20

    anchorSprite.physicsBody = SKPhysicsBody(rectangleOf: anchorSprite.frame.size)
    anchorSprite.physicsBody?.categoryBitMask = pinCategory
    anchorSprite.physicsBody?.isDynamic = false
    addChild(anchorSprite)

    armRight.anchorPoint = CGPoint(x: 0.5, y: 1)
    armRight.position = anchorSprite.position
    armRight.zPosition = 20
    armRight.physicsBody = SKPhysicsBody(rectangleOf: armRight.frame.size)
    armRight.zRotation = CGFloat(Double(15).degreesToRadians)//CGFloat(Double.pi/6)
    armRight.physicsBody!.isDynamic = true

    addChild(armRight)

    armLeft.anchorPoint = CGPoint(x: 0.5, y: 1)
    armLeft.position = anchorSprite.position
    armLeft.zPosition = 20
    armLeft.physicsBody = SKPhysicsBody(rectangleOf: armRight.frame.size)
    armLeft.zRotation = CGFloat(Double(-15).degreesToRadians)//CGFloat(-Double.pi/6)
    armLeft.physicsBody!.isDynamic = true
    addChild(armLeft)

    // Create joint between two objects
    //Pin joint
    var pinAndRightArmJoint = SKPhysicsJointPin.joint(withBodyA: anchorSprite.physicsBody!, bodyB: armRight.physicsBody!, anchor: CGPoint(x: anchorSprite.position.x, y: self.armRight.frame.maxY))
    self.physicsWorld.add(pinAndRightArmJoint)

    var pinAndLeftArmJoint = SKPhysicsJointPin.joint(withBodyA: anchorSprite.physicsBody!, bodyB: armLeft.physicsBody!, anchor: CGPoint(x: anchorSprite.position.x, y: self.armLeft.frame.maxY))
    self.physicsWorld.add(pinAndLeftArmJoint)

    var fixArms = SKPhysicsJointFixed.joint(withBodyA: armLeft.physicsBody!, bodyB: armRight.physicsBody!, anchor: CGPoint.zero)
    self.physicsWorld.add(fixArms)

    pinAndRightArmJoint.shouldEnableLimits = true
    pinAndRightArmJoint.lowerAngleLimit = CGFloat(Double(-60).degreesToRadians)
    pinAndRightArmJoint.upperAngleLimit = CGFloat(Double(60).degreesToRadians)

 }


override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    //armRight.physicsBody?.angularVelocity = -100.0

     let seq = SKAction.sequence([
        SKAction.rotate(byAngle: CGFloat(Double(45).degreesToRadians), duration: 0.5),
        SKAction.rotate(byAngle: CGFloat(Double(-45).degreesToRadians), duration: 1.0),
        SKAction.rotate(byAngle: CGFloat(Double(45).degreesToRadians), duration: 1.0),
        SKAction.rotate(byAngle: CGFloat(Double(-45).degreesToRadians), duration: 1.0),
        SKAction.rotate(byAngle: CGFloat(Double(45).degreesToRadians), duration: 1.0),
        SKAction.rotate(byAngle: CGFloat(Double(-45).degreesToRadians), duration: 1.0)
])
    armRight.run(seq)
}

从上面的代码中我设置了下限和上限角度,并尝试运行一个动作,但这只是使线条以一种非常不现实的方式向侧面倾斜。我也尝试在物理体上应用角速度,但这只是让它以不一致的速度短暂摆动(我需要它从一端到另一端一直摆动)。

NB

由于我每次都需要它从头到尾摆动,我需要摆动周期每次都保持一致,不一定是恒定的。当线条移动到中心时,一个周期通常会更快地摆动,当从一个方向改变到另一个方向时,一个周期会减慢一点。这就是我想要运动的感觉。

2 个答案:

答案 0 :(得分:7)

以下是实际答案:

addChild(armRight)替换为anchorSprite.addChild(armRight)。将addChild(armLeft)替换为anchorSprite.addChild(armLeft)。删除armRight.position = anchorSprite.position并删除armLeft.position = anchorSprite.position。此外,除非您在代码中使用物理关节进行其他移动,否则请删除所有这些,因为我的解决方案不需要关节。

现在你的手臂是anchorSprite的孩子,并受到其'坐标系统'的影响。如果要同时在同一方向旋转两个手臂,可以在anchorSprite上运行旋转动作。如果您希望手臂朝不同方向旋转,则必须分别在每个手臂上执行旋转动作。对于这两种情况,你可以使用我在这个问题上为赏金做的这个方便的功能:-P

func runPendulumRotationOnNode(_ node:SKNode, withAngle angle:CGFloat, period:TimeInterval, key:String) {
    let initialRotate = SKAction.rotate(byAngle: angle/2, duration: period/2)
    initialRotate.timingMode = .easeOut
    let rotate = SKAction.rotate(byAngle: angle, duration: period)
    rotate.timingMode = .easeInEaseOut
    let rotateForever = SKAction.repeatForever(SKAction.sequence([rotate.reversed(), rotate]))
    let rotateSequence = SKAction.sequence([initialRotate, rotateForever])
    node.run(rotateSequence, withKey:key)
}

我测试了它,效果很好!您可以像这样调用它来将两个手臂旋转在一起:

runPendulumRotationOnNode(anchorSprite, withAngle:CGFloat.pi/2, period:0.5, key:"")

或者以相反方向旋转手臂,您可以像这样使用它:

runPendulumRotationOnNode(armRight, withAngle:CGFloat.pi/2, period:0.5, key:"")
runPendulumRotationOnNode(armLeft, withAngle:-CGFloat.pi/2, period:0.5, key:"")

一些小注,请注意我如何使用CGFloat.pi,这是π的简单常量。此功能还假设摆锤在旋转的中点处开始,因此π/ 2(90度)将使臂π/ 4(45度)向任一方向旋转。

答案 1 :(得分:0)

快速的5 扩展版本,用于优质的mogelbuster code

extension SKAction {
    class func pendulum(withAngle angle:CGFloat, period:TimeInterval, key:String) -> SKAction {
        let initialRotate = SKAction.rotate(byAngle: angle/2, duration: period/2)
        initialRotate.timingMode = .easeOut
        let rotate = SKAction.rotate(byAngle: angle, duration: period)
        rotate.timingMode = .easeInEaseOut
        let rotateForever = SKAction.repeatForever(SKAction.sequence([rotate.reversed(), rotate]))
        return SKAction.sequence([initialRotate, rotateForever])
    }
}

用法

let pendulum = SKAction.pendulum(withAngle: CGFloat.pi/2, period: 0.5, key: "pendulum")
self.mynode.run(pendulum)