Swift 3.0:当给定一个非 SKView 父级时,作为 Button 的 SKSpriteNode 不起作用?
Swift 3.0: SKSpriteNode as Button does not work when given a non-SKView parent?
已经有一些关于如何在 SpriteKit 中制作按钮的问题,例如 Swift Spritekit Adding Button Programaticly and Setting up buttons in SKScene。
无论哪种方式,解决方案是为按钮制作一个带有纹理的SKSpriteNode
,然后在函数touchesEnded
中,测试触摸是否落在按钮内-SKSpriteNode
.
然而,有趣的是,如果要使按钮成为子按钮,例如 SKCameraNode
或另一个 SKSpriteNode
,则此方法不再有效。
我的问题是为什么?以及如何克服这个困境。
更新:
关于下面的 post,这里有两个简单的 GameScene.swift 文件的替代版本。主要区别在于,在第一种情况下,sprite2
不是相机的子级,而在版本 2 中是。
注意:在 gif 中,请注意在版本 1 中,单击紫色精灵 (sprite2
) 会导致它打印 "You tapped the purple sprite.",而在版本 2 中它会显示 "blue sprite"。这样问题就清楚了:
点击另一个SKNode的子节点注册为点击最上面的父节点,而不是实际点击的节点!
新问题:如何更正此问题。
附录:在版本 2 中,sprite2
由于成为相机的子项而稍微改变了位置 - 因为它是 M.W.E。且不影响本次演示,我选择不更正。
版本 1
class GameScene: SKScene {
var cam: SKCameraNode!
var sprite = SKSpriteNode(imageNamed: "sprite")
var sprite2 = SKSpriteNode(imageNamed: "sprite2")
override func didMove(to view: SKView) {
backgroundColor = SKColor.white
sprite.position = CGPoint(x: size.width/4,y: size.height/2)
sprite2.position = CGPoint(x: size.width/4 * 3,y: size.height/2)
sprite.anchorPoint = CGPoint(x:0.5, y:0.5)
// Set up the camera
cam = SKCameraNode()
self.camera = cam
cam.setScale(3.0)
cam.position = CGPoint(x: size.width/4, y: 0)
sprite.addChild(cam)
addChild(sprite); addChild(sprite2)
}
func touchDown(atPoint pos : CGPoint) {
}
func touchMoved(toPoint pos : CGPoint) {
}
func touchUp(atPoint pos : CGPoint) {
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchDown(atPoint: t.location(in: self)) }
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchMoved(toPoint: t.location(in: self)) }
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.location(in: self)
if sprite.contains(touchLocation) {
print("You tapped the blue sprite")
}
if sprite2.contains(touchLocation) {
print("You tapped the purple sprite")
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchUp(atPoint: t.location(in: self)) }
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
版本 2
class GameScene: SKScene {
var cam: SKCameraNode!
var sprite = SKSpriteNode(imageNamed: "sprite")
var sprite2 = SKSpriteNode(imageNamed: "sprite2")
override func didMove(to view: SKView) {
backgroundColor = SKColor.white
// Scale Sprites
// sprite.setScale(0.3)
sprite2.setScale(0.3)
sprite.position = CGPoint(x: size.width/4,y: size.height/2)
// sprite2.position = CGPoint(x: size.width/4 * 3,y: size.height/2)
sprite.anchorPoint = CGPoint(x:0.5, y:0.5)
// Set up the camera
cam = SKCameraNode()
self.camera = cam
cam.setScale(3.0)
cam.position = CGPoint(x: size.width/4, y: 0)
sprite.addChild(cam)
addChild(sprite); //addChild(sprite2)
cam.addChild(sprite2)
}
func touchDown(atPoint pos : CGPoint) {
}
func touchMoved(toPoint pos : CGPoint) {
}
func touchUp(atPoint pos : CGPoint) {
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchDown(atPoint: t.location(in: self)) }
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchMoved(toPoint: t.location(in: self)) }
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.location(in: self)
if sprite.contains(touchLocation) {
print("You tapped the blue sprite")
}
if sprite2.contains(touchLocation) {
print("You tapped the purple sprite")
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchUp(atPoint: t.location(in: self)) }
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
我不认为你说的是真的。您可以检测来自子节点的触摸。为了演示,我只是 运行 我的 SpriteKit 项目中的一个小测试代码,我在其中检测到相机节点上的触摸
var cameraNode = SKCameraNode()
然后在 didMove(to:)
:
addChild(cameraNode)
camera = cameraNode
camera?.position = CGPoint(x: size.width/2, y: size.height/2)
使用 touchesEnded
检测 cameraNode
上的触摸:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: cameraNode)
print("LocationX: \(location.x), LocationY: \(location.y)")
}
这是我打印出来的:
LocationX: -13.9129028320312, LocationY: 134.493041992188
进一步解释,如果您添加一个按钮作为 cameraNode
的子按钮,您将执行如下操作:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: cameraNode)
// Check if tap was within button frame (assuming your buttonNode is named button and a child of cameraNode)
if button.frame.contains(location) {
// Tap was within the frame of the button
// Do whatever is necessary
}
}
进一步编辑 -
您的问题源于您请求触摸位置的节点。正如我在回答中提到的,您需要根据您检查精灵框架的节点来推断触摸位置。在您的编辑中,您仅在 self
上检测到触摸,然后它将为您提供相对于 scene
的坐标。如果你想检测像相机节点这样的子视图上的触摸,你需要请求相机节点内的触摸位置。这是我根据您添加的代码所说的内容:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
// This will give you touch location from the camera node 'cam' NOT 'self'
let cameraLocation = touch.location(in: cam)
// This will give you touch location from the scene itself
let sceneLocation = touch.location(in: self)
if sprite.contains(sceneLocation) {
print("You tapped the blue sprite")
}
if sprite2.contains(cameraLocation) {
print("You tapped the purple sprite")
}
}
我刚试过这个,对我来说效果很好。
已经有一些关于如何在 SpriteKit 中制作按钮的问题,例如 Swift Spritekit Adding Button Programaticly and Setting up buttons in SKScene。
无论哪种方式,解决方案是为按钮制作一个带有纹理的SKSpriteNode
,然后在函数touchesEnded
中,测试触摸是否落在按钮内-SKSpriteNode
.
然而,有趣的是,如果要使按钮成为子按钮,例如 SKCameraNode
或另一个 SKSpriteNode
,则此方法不再有效。
我的问题是为什么?以及如何克服这个困境。
更新:
关于下面的 post,这里有两个简单的 GameScene.swift 文件的替代版本。主要区别在于,在第一种情况下,sprite2
不是相机的子级,而在版本 2 中是。
注意:在 gif 中,请注意在版本 1 中,单击紫色精灵 (sprite2
) 会导致它打印 "You tapped the purple sprite.",而在版本 2 中它会显示 "blue sprite"。这样问题就清楚了:
点击另一个SKNode的子节点注册为点击最上面的父节点,而不是实际点击的节点!
新问题:如何更正此问题。
附录:在版本 2 中,sprite2
由于成为相机的子项而稍微改变了位置 - 因为它是 M.W.E。且不影响本次演示,我选择不更正。
版本 1
class GameScene: SKScene {
var cam: SKCameraNode!
var sprite = SKSpriteNode(imageNamed: "sprite")
var sprite2 = SKSpriteNode(imageNamed: "sprite2")
override func didMove(to view: SKView) {
backgroundColor = SKColor.white
sprite.position = CGPoint(x: size.width/4,y: size.height/2)
sprite2.position = CGPoint(x: size.width/4 * 3,y: size.height/2)
sprite.anchorPoint = CGPoint(x:0.5, y:0.5)
// Set up the camera
cam = SKCameraNode()
self.camera = cam
cam.setScale(3.0)
cam.position = CGPoint(x: size.width/4, y: 0)
sprite.addChild(cam)
addChild(sprite); addChild(sprite2)
}
func touchDown(atPoint pos : CGPoint) {
}
func touchMoved(toPoint pos : CGPoint) {
}
func touchUp(atPoint pos : CGPoint) {
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchDown(atPoint: t.location(in: self)) }
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchMoved(toPoint: t.location(in: self)) }
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.location(in: self)
if sprite.contains(touchLocation) {
print("You tapped the blue sprite")
}
if sprite2.contains(touchLocation) {
print("You tapped the purple sprite")
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchUp(atPoint: t.location(in: self)) }
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
版本 2
class GameScene: SKScene {
var cam: SKCameraNode!
var sprite = SKSpriteNode(imageNamed: "sprite")
var sprite2 = SKSpriteNode(imageNamed: "sprite2")
override func didMove(to view: SKView) {
backgroundColor = SKColor.white
// Scale Sprites
// sprite.setScale(0.3)
sprite2.setScale(0.3)
sprite.position = CGPoint(x: size.width/4,y: size.height/2)
// sprite2.position = CGPoint(x: size.width/4 * 3,y: size.height/2)
sprite.anchorPoint = CGPoint(x:0.5, y:0.5)
// Set up the camera
cam = SKCameraNode()
self.camera = cam
cam.setScale(3.0)
cam.position = CGPoint(x: size.width/4, y: 0)
sprite.addChild(cam)
addChild(sprite); //addChild(sprite2)
cam.addChild(sprite2)
}
func touchDown(atPoint pos : CGPoint) {
}
func touchMoved(toPoint pos : CGPoint) {
}
func touchUp(atPoint pos : CGPoint) {
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchDown(atPoint: t.location(in: self)) }
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchMoved(toPoint: t.location(in: self)) }
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.location(in: self)
if sprite.contains(touchLocation) {
print("You tapped the blue sprite")
}
if sprite2.contains(touchLocation) {
print("You tapped the purple sprite")
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchUp(atPoint: t.location(in: self)) }
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
我不认为你说的是真的。您可以检测来自子节点的触摸。为了演示,我只是 运行 我的 SpriteKit 项目中的一个小测试代码,我在其中检测到相机节点上的触摸
var cameraNode = SKCameraNode()
然后在 didMove(to:)
:
addChild(cameraNode)
camera = cameraNode
camera?.position = CGPoint(x: size.width/2, y: size.height/2)
使用 touchesEnded
检测 cameraNode
上的触摸:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: cameraNode)
print("LocationX: \(location.x), LocationY: \(location.y)")
}
这是我打印出来的:
LocationX: -13.9129028320312, LocationY: 134.493041992188
进一步解释,如果您添加一个按钮作为 cameraNode
的子按钮,您将执行如下操作:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: cameraNode)
// Check if tap was within button frame (assuming your buttonNode is named button and a child of cameraNode)
if button.frame.contains(location) {
// Tap was within the frame of the button
// Do whatever is necessary
}
}
进一步编辑 -
您的问题源于您请求触摸位置的节点。正如我在回答中提到的,您需要根据您检查精灵框架的节点来推断触摸位置。在您的编辑中,您仅在 self
上检测到触摸,然后它将为您提供相对于 scene
的坐标。如果你想检测像相机节点这样的子视图上的触摸,你需要请求相机节点内的触摸位置。这是我根据您添加的代码所说的内容:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
// This will give you touch location from the camera node 'cam' NOT 'self'
let cameraLocation = touch.location(in: cam)
// This will give you touch location from the scene itself
let sceneLocation = touch.location(in: self)
if sprite.contains(sceneLocation) {
print("You tapped the blue sprite")
}
if sprite2.contains(cameraLocation) {
print("You tapped the purple sprite")
}
}
我刚试过这个,对我来说效果很好。