How to limit the movement of two anchored lines so they swing continually like a pendulum

Viewed 470

I have created two lines anchored to a sprite, which are 30˚ apart. I want both lines to swing left and right like a pendulum, always swinging from end to end (in such a way that they are swinging 45˚ to the left and right of their initial position). Please see the image below of what I am trying to achieve:

enter image description here

Below is the code for what I've been able to achieve:

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

From the code above I set lower and upper angle limits and tried running an action, but this just makes the lines inch a bit sideways in a very unrealistic manner. I also tried applying angular velocity on the physics body, but this just made it swing briefly at an inconsistent speed (I need it to swing consistently from one end to the other).

NB

Since I need it to swing from end to end each time, I need the swing cycle to be consistent each time, not necessarily constant. A cycle would generally swing faster as the lines move to the center and slow down a bit when changing from one direction to another. That is the kind of feel I want for movement.

2 Answers

A swift 5 extension version for the good 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])
    }
}

Usage:

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