如何在touchesBegan事件中正确检测SKSpriteNode的触摸?
问题描述
我之前一直专注开发游戏手柄控制的应用,没怎么接触iOS触控类应用。现在遇到一个问题:通过addCreditsButton(_:)方法在GameScene左上角附近生成并添加了名为creditsInfo的SKSpriteNode按钮,但在GameViewController的touchesBegan事件中点击该节点时,控制台打印的节点名称却是位置相距甚远的horse,无法触发showCredits()方法。
相关代码
GameViewController 的 touchesBegan 方法
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { if let ourScene = GameScene(fileNamed: "GameScene") { if let touch:UITouch = touches.first { let location = touch.location(in: view) let node:SKNode = ourScene.atPoint(location) print("node.name = \(node.name!)") // <== 输出 "horse" if (node.name == "creditsInfo") { showCredits() } } } // if let ourScene } // touchesBegan
showScene 函数中添加按钮的代码
if let ourScene = GameScene(fileNamed: "GameScene") { #if os(iOS) addCreditsButton(toScene: ourScene) #endif }
addCreditsButton(_:) 方法代码
func addCreditsButton(toScene: SKScene) { if thisSceneName == "GameScene" { itsCreditsNode.name = "creditsInfo" itsCreditsNode.anchorPoint = CGPoint(x: 0.5, y: 0.5) itsCreditsNode.size = CGSize(width: 2*creditsCircleRadius, height: 2*creditsCircleRadius) itsCreditsNode.zPosition = 3 creditsCirclePosY = roomHeight/2 - creditsCircleRadius - creditsCircleOffsetY creditsCirclePosX = -roomWidth/2 + creditsCircleRadius + creditsCircleOffsetX itsCreditsNode.position = CGPoint(x: creditsCirclePosX, y: creditsCirclePosY) toScene.addChild(itsCreditsNode) } // if thisSceneName } // addCreditsButton
解决方案
问题核心是你在touchesBegan里每次触摸都重新创建了一个全新的GameScene实例,这个新场景和当前屏幕上显示的场景完全是两个独立的对象,节点布局自然不匹配,所以取到的节点肯定不对。另外触摸位置的坐标系转换也有问题,需要修正。
步骤1:保存当前显示的GameScene引用
在GameViewController中添加一个属性来保存当前正在显示的场景实例:
class GameViewController: UIViewController { var currentGameScene: GameScene? // 新增属性保存场景引用 // ... 其他代码 }
然后在showScene函数中,将创建好的场景实例赋值给这个属性(同时确保你把场景呈现到view上的代码也存在):
func showScene() { if let ourScene = GameScene(fileNamed: "GameScene") { #if os(iOS) addCreditsButton(toScene: ourScene) #endif currentGameScene = ourScene // 保存当前场景引用 view.presentScene(ourScene) // 确保场景被呈现到view上 } }
步骤2:修正touchesBegan方法
改用保存的currentGameScene实例,同时将触摸位置转换到场景坐标系(而不是view坐标系):
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { // 确保有触摸事件且当前场景存在 guard let touch = touches.first, let ourScene = currentGameScene else { return } // 将触摸位置转换到场景坐标系 let location = touch.location(in: ourScene) let node = ourScene.atPoint(location) print("node.name = \(node.name ?? "unknown")") if node.name == "creditsInfo" { showCredits() } }
内容的提问来源于stack exchange,提问作者user22530416
相关产品推荐
相关产品推荐

