Как ограничить движение двух закрепленных линий, чтобы они качались постоянно как маятник

Я создал две строки, привязанные к спрайту, которые находятся на расстоянии друг от друга. Я хочу, чтобы обе линии качались влево и вправо, как маятник, всегда размахивая от конца до конца (таким образом, что они размахивают влево и вправо от своего исходного положения). Посмотрите изображение ниже того, чего я пытаюсь достичь:

введите описание изображения здесь

Ниже приведен код для того, что я смог достичь:

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

Из приведенного выше кода я установил нижний и верхний пределы угла и попытался запустить действие, но это просто делает линии дюймов немного боком в очень нереалистичной манере. Я также попытался применить скорость angular к физическому телу, но это просто заставило его быстро качаться на непоследовательной скорости (мне нужно, чтобы он качался последовательно от одного конца до другого).

NB

Так как мне нужно, чтобы он качался от конца до конца каждый раз, мне нужно, чтобы цикл свинг был последовательным каждый раз, а не обязательно постоянным. Цикл, как правило, качается быстрее, когда линии перемещаются в центр и немного замедляются при переходе от одного направления к другому. Это то чувство, которое я хочу для движения.

Ответы

Ответ 1

Вот практический ответ:

Замените 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 градусов) в любом направлении.

Ответ 2

Версия расширения Swift 5 для хорошего кода Mogelbuster:

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)