Moving SKSpriteNode to location of the touch

后端 未结 1 364
没有蜡笔的小新
没有蜡笔的小新 2021-01-07 15:20

Above is an image of my game. A top-down game. Wherever on the screen the player touches, I want the bullets to go to that location with a duration. I also want the

相关标签:
1条回答
  • 2021-01-07 15:55

    Here you go - a simple app with a ship you can drag around the screen and missiles that shoot towards the location of a touch.

    If you touch the ship you can drag it around; touch outside the ship and a missile will shoot from the ship to the touch location.

    import SpriteKit
    
    class GameScene: SKScene {
    
    var ship = SKSpriteNode()
    var shipIsTouched = false
    var missileDestination = CGPoint()
    let missileSpeed: CGFloat = 800 // Points per second)
    let missileFireRate : TimeInterval = 0.2 // Seconds between each missile
    
    override func didMove(to view: SKView) {
        missileDestination = CGPoint(x: 0, y: (self.size.height / 2))
        createPlayerShip()
        let fire = SKAction.sequence([SKAction.run(fireMissile), SKAction.wait(forDuration: missileFireRate)])
        run(SKAction.repeatForever(fire))
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first {
            if ship.contains(touch.location(in: self)) {
                shipIsTouched = true
            } else {
                missileDestination = touch.location(in: self)
                ship.zRotation = direction(to: missileDestination, from: ship.position) - CGFloat(Double.pi/2)
            }
        }
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        if (shipIsTouched == true) {
            ship.position = (touches.first?.location(in: self))!
            ship.zRotation = direction(to: missileDestination, from: ship.position) - CGFloat(Double.pi/2)
        }
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        if shipIsTouched {
            shipIsTouched = false
        }
    }
    
    override func update(_ currentTime: TimeInterval) {
        // Called before each frame is rendered
    }
    
    func createPlayerShip() {
        ship = SKSpriteNode(imageNamed: "Spaceship")
        ship.zRotation = CGFloat(-Double.pi/2.0)
        ship.scale(to: CGSize(width: 150, height: 150))
        ship.position = CGPoint(x: -size.width/2 + 200, y: 0)
    
        addChild(ship)
    }
    
    
    func fireMissile() {
        let missile = SKSpriteNode(color: .white, size: CGSize(width: 50, height: 10))
        missile.position = ship.position
    
        let missileFlightTime = travelTime(to: missileDestination, from: ship.position, atSpeed: missileSpeed)
        missile.zRotation = direction(to: missileDestination, from: missile.position)
    
        addChild(missile)
    
        let missileMove = SKAction.move(to: missileDestination,
                                        duration: TimeInterval(missileFlightTime))
        let missileRemove = SKAction.removeFromParent()
        missile.run(SKAction.sequence([missileMove, missileRemove]))
    }
    
    func travelTime(to target : CGPoint, from : CGPoint, atSpeed speed : CGFloat) -> TimeInterval {
        let distance = sqrt(pow(abs(target.x - from.x),2) +
            pow(abs(target.y - from.y),2))
        return TimeInterval(distance/speed)
    }
    
    
    func direction(to target : CGPoint, from: CGPoint) -> CGFloat {
        let x = target.x - from.x
        let y = target.y - from.y
        var angle = atan(y / x)
        if x < 0 {
            angle = angle + CGFloat.pi
        }
        return angle
    }
    }
    

    There's a bit of extra trickery to make the missiles speed consistent (since moveTo takes a time, not a speed, so if the destination was close the missiles would move slowly, and if further away they'd move faster) and to make the missiles rotate to face the destination.

    You could create a curved path for the missiles to follow to the destination, which would look cool but may not be appropriate for your app.

    EDIT:

    If you want the ship stationary, and the missiles to follow your finger, replace all the code down to createPlayerShip with this (yes, we've lost touchesEnded() and update():

    import SpriteKit
    
    class GameScene: SKScene {
    
    var ship = SKSpriteNode()
    var missileDestination = CGPoint()
    let missileSpeed: CGFloat = 800 // Points per second)
    let missileFireRate : TimeInterval = 0.2 // Seconds between each missile
    
    override func didMove(to view: SKView) {
        missileDestination = CGPoint(x: size.height/2, y: 0)
        createPlayerShip()
        let fire = SKAction.sequence([SKAction.run(fireMissile), SKAction.wait(forDuration: missileFireRate)])
        run(SKAction.repeatForever(fire))
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first {
                missileDestination = touch.location(in: self)
        }
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
            missileDestination = (touches.first?.location(in: self))!
    } 
    
    0 讨论(0)
提交回复
热议问题