Moving SKSpriteNode to location of the touch

◇◆丶佛笑我妖孽 提交于 2020-01-11 04:15:28

问题


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 player to be able to drag around his finger on the screen, and the same thing happens. So that the player doesn't have to touch the screen everytime he wants to shoot.

I've tried some different stuff so far but nothing seems to work.

First off, I dont know if I should have a separate function for the bullet. But anyway, this is my bullet function.

func spawnBullets() {
    let bullet = SKSpriteNode(imageNamed: "Bullet")
    bullet.name = "Bullet"
    bullet.zPosition = 4
    bullet.position = CGPoint(x: player.position.x + 19, y: 
    player.position.y)
    self.addChild(bullet)
}

I also have a "timer" for the bullet in the didMove function:

var timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, 
selector: Selector("spawnBullets"), userInfo: nil, repeats: true)

And lastly, this is my touchesBegan function:

override func touchesBegan(_ touches: Set<UITouch>, with event: 
UIEvent?) {        
    for  touch in touches {            
        let location = touch.location(in: self)            
        let moveToPoint = SKAction.move(to: location, duration: 0.5)
        let repeatAction = SKAction.repeatForever(moveToPoint)            
        bullet.run(moveToPoint)
    }

}

回答1:


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


来源:https://stackoverflow.com/questions/45678152/moving-skspritenode-to-location-of-the-touch

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!