2017-07-26 125 views
0

我是新來的Swift SpriteKit,我想製作一個遊戲,就像一個虛擬遊戲杆和兩個按鈕(兩個節點),我啓用了多點觸控。但是,無論何時我移動虛擬遊戲杆和攻擊Spritenode,按鈕的虛擬遊戲杆似乎都是Jagging。我如何將虛擬操縱桿的觸摸與觸摸進行分離攻擊按鈕Swift 3.0 - Sprite Kit - Multitouch

class GameScene: SKScene { 

var defend : Bool = false 
var attack : Bool = false 
var stickMove : Bool = false 
var stickEnd:Bool = false 
var moveattack:Bool = false 
var movedefend:Bool = false 

var playermovement:Bool = true 

let vj1 = SKSpriteNode(imageNamed: "vj1") 
let vj2 = SKSpriteNode(imageNamed: "vj2") 
let player = SKSpriteNode(imageNamed: "player") 
let rotationSpeed :CGFloat = CGFloat(M_PI) 
let rotationOffSet : CGFloat = -CGFloat(M_PI/2.0) 
let attackvj = SKSpriteNode(imageNamed: "attackvj") 
let defendvj = SKSpriteNode(imageNamed: "defendvj") 


private var touchPosition: CGFloat = 0 
private var targetZRotation: CGFloat = 0 

override func didMove(to view: SKView) { 
    self.view?.isMultipleTouchEnabled = true 
    self.backgroundColor = SKColor.black 

    //position of joystick 
    vj1.zPosition = 1 
    vj1.xScale = 1.5 
    vj1.yScale = 1.5 
    self.addChild(vj1) 


    vj1.position = CGPoint(x: self.size.width*15/100, y:self.size.height*30/100) 

    vj2.zPosition = 1 
    vj2.xScale = 1.5 
    vj2.yScale = 1.5 
    self.addChild(vj2) 


    vj2.position = vj1.position 

    player.zPosition = 0 
    player.physicsBody = SKPhysicsBody(rectangleOf: player.size) 
    player.physicsBody!.affectedByGravity = false 
    player.position = CGPoint(x: self.size.width/2, y:self.size.height/2) 
    self.addChild(player) 

    attackvj.anchorPoint = CGPoint(x: 0.5, y:0.5) 
    attackvj.position = CGPoint(x: self.size.width*80/100, y:self.size.height*30/100) 
    attackvj.xScale = 2.0 
    attackvj.yScale = 2.0 
    self.addChild(attackvj) 

    defendvj.anchorPoint = CGPoint(x: 0.5, y:0.5) 
    defendvj.position = CGPoint(x: self.size.width*90/100, y:self.size.height*50/100) 
    defendvj.xScale = 2.0 
    defendvj.yScale = 2.0 

    self.addChild(defendvj) 


    vj1.alpha = 0.4 
    vj2.alpha = 0.4 
    attackvj.alpha = 0.4 
    defendvj.alpha = 0.4 

} 


override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { 
    for touch in (touches){ 
     let location = touch.location(in: self) 

     if vj2.contains(location){ 

      stickEnd = false 
      stickMove = true 
     } 

     if defendvj.contains(location){ 
      defend = true 
     } 


     if attackvj.contains(location){ 
      attack = true 
      attackvj.xScale = 2.5 
      attackvj.yScale = 2.5 

     } 

     if(stickMove == true && attack == true){ 
      moveattack = true 
     } 
     if(stickMove == true && defend == true){ 

      movedefend = true 
     } 

    } 
} 

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { 
    for touch in (touches){ 
     let location = touch.location(in: self) 
     let previousLocation = touch.previousLocation(in: self) 
     let v = CGVector(dx: location.x - vj1.position.x, dy: location.y - vj1.position.y) 
     print("locationsss" , location , "previouslocationsss", previousLocation) 
     let angle = atan2(v.dy, v.dx) 
     targetZRotation = angle + rotationOffSet 
     let length:CGFloat = vj1.frame.size.height/2 

     let xDist:CGFloat = sin(angle - 1.57079633) * length 
     let yDist:CGFloat = cos(angle - 1.57079633) * length 
     if(stickMove == true){ 

      if(vj1.frame.contains(location)){ 

       vj2.position = location 
      } 
      else{ 

       vj2.position = CGPoint(x: vj1.position.x - xDist, y: vj1.position.y + yDist) 
      } 
      if(attackvj.frame.contains(location)){//How am I gonna make this location in attackvj, not to influence my joystick location? 


      } 

     } 
    } 
} 

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { 

    if(stickMove == true && attack == false && defend == false){ 
     let move:SKAction = SKAction.move(to: vj1.position, duration: 0.2) 
     move.timingMode = .easeOut 
     vj2.run(move) 
     stickEnd = true 
     stickMove = false 
    } 
    if(attack == true){ 
     attack = false 
     attackvj.xScale = 2.0 
     attackvj.yScale = 2.0 
     moveattack = false 
    } 
    if(defend == true){ 
     defend = false 
     movedefend = false 
    } 
} 


override func update(_ currentTime: TimeInterval) { 
    //rotation 
    if (stickEnd == false) { 
    var angularDisplacement = targetZRotation - player.zRotation 
    if angularDisplacement > CGFloat(M_PI) { 
     angularDisplacement = (angularDisplacement - CGFloat(M_PI)*2) 
    } 
    else if angularDisplacement < -CGFloat(M_PI) { 
     angularDisplacement = (angularDisplacement + CGFloat(M_PI)*2) 
    } 

    if abs(angularDisplacement) > rotationSpeed*(1.0/60.0){ 
     let angularVelocity = angularDisplacement < 0 ? -rotationSpeed : rotationSpeed 
     player.physicsBody!.angularVelocity = angularVelocity 

    } else { 
     player.physicsBody!.angularVelocity = 0 

     player.zPosition = targetZRotation 
    } 


    } 
    else{ 
     player.physicsBody!.angularVelocity = 0 
    } 
    //movement but use attack button to testing 
    if (attack == true) 
    { 
     player.position = CGPoint(x:player.position.x + cos(player.zRotation + 1.57079633),y:player.position.y + sin(player.zRotation + 1.57079633)) 
    }  
} 
+0

您遇到的問題是您沒有將您的觸摸與各種控件的行爲聯繫起來。您可以將UITouch對象存儲在touchesBegan和touchesMoved調用之間,以便您可以跟蹤哪個觸摸是哪個,並分別理解它們。使用touchesBegan開始跟蹤各種類型的觸摸,只需使用touchesMoved作爲您正在跟蹤的某個觸摸事件可能已發生變化的信號。 –

+0

對於你想要做的事情,一個(也許)更好的方法是爲你的各種按鈕設置不同的「觸摸板」,並讓它們將遊戲事件通過鏈接傳遞給你的遊戲邏輯,而不是試圖讓一個大的試圖將每個可能的UI元素考慮在內的項目。這樣,您可以按照您在代碼示例中顯示的方式來構建UI元素,但是在多個上下文之間不會有混亂來混淆事物。 –

+0

嗨,cc,我真的很感謝你對這個話題的迴應,我已經開始通過網絡搜索和晚上的試驗和錯誤,仍然沒有滿意的輸出。 – Yuan

回答

0

您面臨的問題是您正在爲觸摸混合上下文。這使事情變得比他們所需要的更加困難和複雜。

最簡單的做法是讓您的虛擬遊戲杆成爲一個單獨的SKSpriteNode類,用於跟蹤自己的觸摸並報告它們。與按鈕相同 - 他們跟蹤他們自己的觸摸並報告他們的狀態。

但是,如果你想繼續使用當前的具有高層次的目標航跡多點觸摸的方式,你想要做的就是抓住每一個接觸與在的touchesBegan相關的上下文,然後只更新的東西放在touchesMoved根據需要,取消touchesEnded中的觸摸。例如,你想將一個特定的觸摸與虛擬手柄關聯起來,因爲如果他們將手指從手指上拖到按鈕上,那麼你就不會感到奇怪。並且您想知道用戶擡起手指時確切觸摸哪個觸摸。

下面是一些示例代碼,應該說明這個過程:

// 
// This scene lets the user drag a red and a blue box 
// around the scene. In the .sks file (or in the didMove 
// function), add two sprites and name them "red" and "blue". 
// 

import SpriteKit 
import GameplayKit 

class GameScene: SKScene { 

    private var redTouch:UITouch? 
    private var blueTouch:UITouch? 

    override func didMove(to view: SKView) { 
     super.didMove(to: view) 
     isUserInteractionEnabled = true 
    } 

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { 

     // Grab some references to the red and blue sprites. 
     // (They must be direct children of the scene, or the 
     // paths must be amended.) 
     guard let redBox = childNode(withName: "red") else { return } 
     guard let blueBox = childNode(withName: "blue") else { return } 

     for touch in touches { 
      // Get the location of the touch in SpriteKit Scene space. 
      let touchLocation = touch.location(in: self) 
      // Check to see if the user is touching one of the boxes. 
      if redBox.contains(touchLocation) { 
       // If we already have a touch in the red box, do nothing. 
       // Otherwise, make this our new red touch. 
       redTouch = touch 
      } else if blueBox.contains(touchLocation) { 
       // If we already have a touch in the blue box, do nothing. 
       // Otherwise, make this our new blue touch. 
       blueTouch = touch 
      } 
     } 

    } 

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { 
     // We have already established which touches are active, 
     // and we have already tied them to the two contexts, so 
     // we just need to read their current location and update 
     // the location of the red and blue boxes for the touches 
     // that are active. 
     if let redTouch = redTouch { 
      guard let redBox = childNode(withName: "red") else { return } 
      let location = redTouch.location(in:self) 
      redBox.position = location 
     } 
     if let blueTouch = blueTouch { 
      guard let blueBox = childNode(withName: "blue") else { return } 
      let location = blueTouch.location(in:self) 
      blueBox.position = location 
     } 
    } 

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { 
     // The parameter touches contains a list of ending touches, 
     // so we check the touches we are currently tracking to 
     // see if they are newly lifted. If so, we cancel them. 
     if let touch = redTouch { 
      if touches.contains(touch) { 
       redTouch = nil 
      } 
     } 
     if let touch = blueTouch { 
      if touches.contains(touch) { 
       blueTouch = nil 
      } 
     } 
    } 

} 

在上面的代碼,我們已經分離出了觸摸紅色框藍框。我們總是知道拖動紅色方框的位置,以及拖動藍色方框的位置(如果有的話)。這是一個簡單的例子,但它可以推廣到您的情況,您可以觸摸虛擬遊戲杆和每個單獨的按鈕。

請注意,這種方法也適用於多點觸控元素。如果你有一張你想要可縮放的地圖,你可以跟蹤兩次觸摸,這樣你就可以比較它們的捏手勢。這樣,如果你的擠壓手勢意外地偏離了按鈕,你已經將它標記爲擠壓手勢的一部分,並且知道不要開始觸發該按鈕。

但是,更好的方法是創建一個單獨的SKSpriteNode子類,該子類只跟蹤操縱桿觸摸並將其狀態報告給某個更高級別的管理員類。你已經知道你需要知道的一切 - 就像你沒有額外的檢查,看看是否有其他按鈕被按下。與按鈕一樣。唯一的新部分是向經理傳遞「上鍊」信息,這很容易處理。

+0

清晰明瞭的解釋。以前,我已經混淆了所有的聯繫。我試過你的編碼,你基本上爲每個節點分配每個觸摸。這使得觸摸位置不會混淆。感謝你的幫助。我已經接受你的答案。 – Yuan