Swift 3.0 - Sprite 套件 - 多点触控

Swift 3.0 - Sprite Kit - Multitouch

我是 Swift SpriteKit 的新手,我想制作一个像虚拟操纵杆和两个按钮(两个节点)这样的游戏,我已经启用了多点触控。但是,每当我移动虚拟操纵杆并攻击 Spritenode 时,按钮的虚拟操纵杆似乎是锯齿状的。我如何将虚拟操纵杆的触摸与触摸 attackbutton

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

您面临的问题是您正在混合触摸的上下文。这让事情变得比他们需要的更加困难和复杂。

最简单的做法是让您的虚拟操纵杆成为一个单独的 SKSpriteNode class,它可以跟踪自己的触摸并报告它们。与按钮相同 - 它们跟踪自己的触摸并报告它们的状态。

但是,如果您想继续使用当前让高级对象跟踪多个触摸的方法,您需要做的是在 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 subclass,它只跟踪操纵杆触摸并将其状态报告给某个更高级别的管理器 class。您已经知道执行此操作所需知道的一切 - 这就像您拥有的一样,无需额外检查是否按下了其他按钮。与按钮相同。唯一的新部分是向经理发送消息 "up the chain",这很容易处理。