将 SKSpriteNode 移动到触摸的位置
Moving SKSpriteNode to location of the touch
以上是我的游戏图片。一个自上而下的游戏。无论玩家触摸屏幕上的哪个位置,我都希望子弹在一段时间内到达该位置。我还希望玩家能够在屏幕上拖动他的手指,同样的事情也会发生。这样玩家就不必每次都想射击就去触摸屏幕。
到目前为止,我已经尝试了一些不同的方法,但似乎没有任何效果。
首先,我不知道是否应该为子弹设置单独的功能。但无论如何,这是我的子弹功能。
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)
}
我在 didMove 函数中也有一个 "timer" 用于子弹:
var timer = Timer.scheduledTimer(timeInterval: 0.1, target: self,
selector: Selector("spawnBullets"), userInfo: nil, repeats: true)
最后,这是我的 touchesBegan 函数:
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)
}
}
给你 - 一个简单的应用程序,有一艘你可以在屏幕上拖动的飞船和向触摸位置发射的导弹。
如果你触摸船,你可以拖动它;在船外触摸,导弹将从船上发射到触摸位置。
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
}
}
有一些额外的技巧可以使导弹速度保持一致(因为 moveTo 需要时间,而不是速度,所以如果目的地很近,导弹会移动得很慢,如果离得更远,它们会移动得更快)并使导弹旋转朝向目的地。
您可以为导弹创建一条弯曲的路径以到达目的地,这看起来很酷,但可能不适合您的应用程序。
编辑:
如果你想让船静止不动,导弹跟随你的手指,用这个替换所有代码到 createPlayerShip
(是的,我们丢失了 touchesEnded()
和 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))!
}
以上是我的游戏图片。一个自上而下的游戏。无论玩家触摸屏幕上的哪个位置,我都希望子弹在一段时间内到达该位置。我还希望玩家能够在屏幕上拖动他的手指,同样的事情也会发生。这样玩家就不必每次都想射击就去触摸屏幕。
到目前为止,我已经尝试了一些不同的方法,但似乎没有任何效果。
首先,我不知道是否应该为子弹设置单独的功能。但无论如何,这是我的子弹功能。
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)
}
我在 didMove 函数中也有一个 "timer" 用于子弹:
var timer = Timer.scheduledTimer(timeInterval: 0.1, target: self,
selector: Selector("spawnBullets"), userInfo: nil, repeats: true)
最后,这是我的 touchesBegan 函数:
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)
}
}
给你 - 一个简单的应用程序,有一艘你可以在屏幕上拖动的飞船和向触摸位置发射的导弹。
如果你触摸船,你可以拖动它;在船外触摸,导弹将从船上发射到触摸位置。
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
}
}
有一些额外的技巧可以使导弹速度保持一致(因为 moveTo 需要时间,而不是速度,所以如果目的地很近,导弹会移动得很慢,如果离得更远,它们会移动得更快)并使导弹旋转朝向目的地。
您可以为导弹创建一条弯曲的路径以到达目的地,这看起来很酷,但可能不适合您的应用程序。
编辑:
如果你想让船静止不动,导弹跟随你的手指,用这个替换所有代码到 createPlayerShip
(是的,我们丢失了 touchesEnded()
和 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))!
}