2016-07-15 81 views
1

我被要求簡化這個問題,所以這就是我正在做的。SpriteKit關節:跟着身體

我在SpriteKit的物理關節(以及可能的物理屬性)中掙扎。我試過每一個子類和許多配置,但接縫沒有任何作用,或者我做錯了什麼。

我正在開發貪吃蛇遊戲。用戶控制蛇頭前進,並以正向速度向前移動,用戶可以將其順時針或逆時針旋轉。所有剩下的蛇的碎片應該跟隨頭部 - 他們應該旅行完全相同的路徑,頭部是前一段時間。

我認爲對於這個遊戲Pin聯合應該是答案,哪個錨點恰好在元素之間的中心。

不幸的是,結果並不完美。結構應該是完美的圓形,但它不是。我附上了代碼,顯示了當前效果的gif。有沒有人有足夠的經驗給我任何建議物理身體和/或關節的屬性應該在這裏適用於預期的效果?

enter image description here

我的代碼:

class GameScene: SKScene { 
    private var elements = [SKNode]() 

    override func didMove(to view: SKView) { 
     physicsWorld.gravity = CGVector(dx: 0, dy: 0) 

     let dummyTurnNode = SKNode() 
     dummyTurnNode.position = CGPoint(x: size.width/2 - 50, y: size.height/2) 
     let dummyTurnBody = SKPhysicsBody(circleOfRadius: 1) 
     dummyTurnBody.isDynamic = false 
     dummyTurnNode.physicsBody = dummyTurnBody 
     addChild(dummyTurnNode) 

     for index in 0..<5 { 
      let element = SKShapeNode(circleOfRadius: 10) 
      let body = SKPhysicsBody(circleOfRadius: 10) 
      body.linearDamping = 0 
      // body.mass = 0 
      element.physicsBody = body 
      element.position = CGPoint(x: size.width/2, y: size.height/2 - 30 * CGFloat(index)) 
      elements.append(element) 
      addChild(element) 

      let label = SKLabelNode(text: "A") 
      label.fontSize = 10 
      label.fontName = "Helvetica-Bold" 
      element.addChild(label) 

      if index == 0 { 
       element.fillColor = UIColor.blue() 

       body.velocity = CGVector(dx: 0, dy: 30) 

       let dummyTurnJoint = SKPhysicsJointPin.joint(withBodyA: dummyTurnBody, bodyB: body, anchor: dummyTurnNode.position) 
       physicsWorld.add(dummyTurnJoint) 
      } else { 
       body.linearDamping = 1 

       element.fillColor = UIColor.red() 
       let previousElement = elements[index - 1] 
       let connectingJoint = SKPhysicsJointPin.joint(withBodyA: previousElement.physicsBody!, bodyB: body, anchor: CGPoint(x: size.width/2, y: size.height/2 - 30 * CGFloat(index) + CGFloat(15))) 
       physicsWorld.add(connectingJoint) 
      } 
     } 
    } 

    override func update(_ currentTime: TimeInterval) { 
     let head = elements.first!.physicsBody! 
     var velocity = head.velocity 
     velocity.normalize() 
     velocity.multiply(30) 
     head.velocity = velocity 
    } 
} 

extension CGVector { 
    var rwLength: CGFloat { 
     let xSq = pow(dx, 2) 
     let ySq = pow(dy, 2) 
     return sqrt(xSq + ySq) 
    } 

    mutating func normalize() { 
     dx /= rwLength 
     dy /= rwLength 
    } 

    mutating func multiply(_ factor: CGFloat) { 
     dx *= factor 
     dy *= factor 
    } 
} 
+0

你使用的是什麼Swift版本? –

+0

@AlessandroOrnano 3 –

回答

2

「其餘所有的蛇的作品應遵循頭 - 他們應該前往完全相同的路徑頭是前一段時間。」

您應該注意,無論您做什麼,使用物理關節都可能會產生變化。即使你已經接近完美,你會在引擎蓋下發現四捨五入的錯誤,從而導致路徑不準確。

如果所有的尾巴部分是相同的,你也可以使用不同的方法,這是我爲彗星尾部所做的。基本上這個想法是,你有一個尾部對象的數組,每幀移動最後一個尾部對象總是移動到與頭部對象相同的位置。如果頭部物體具有較高的z位置,則尾部將被拉到其下方。

如果您需要保持尾巴順序,您可以通過存儲頭部位置數組(每幀路徑)來改變方法,然後在每幀更新調用中沿着該路徑放置尾部對象蛇。

見我下面的代碼,例如:

這些都是你的頭對象變量:

var tails = [SKEmitterNode]() 
var tailIndex = 0 

在你的腦袋初始化函數實例尾部對象:

for _ in 0...MAX_TAIL_INDEX 
     { 
      if let remnant = SKEmitterNode(fileNamed: "FireTail.sks") 
      { 
       p.tails.append(remnant) 
      } 
     } 

呼叫以下每幀:

func drawTail() 
{ 
    if tails.count > tailIndex 
    { 
     tails[tailIndex].resetSimulation() 
     tails[tailIndex].particleSpeed = velocity()/4 
     tails[tailIndex].emissionAngle = zRotation - CGFloat(M_PI_2) // opposite direction 
     tails[tailIndex].position = position 
     tailIndex = tailIndex < MAX_TAIL_INDEX ? tailIndex + 1 : 0 
    } 
} 

從場景update()函數調用它時,所得效果實際上非常流暢。

+0

保持陣列點和頭旋轉,然後將其應用到其他節點是可能的,但是我想盡可能多地利用遊戲框架。該解決方案還需要對初始位置進行一些預先計算。順便說一句:我認爲舍入誤差是可以確切的。我希望蛇體跟隨頭部,幾乎看不見的圓角誤差對我來說是可以接受的。 –