There are already a few questions out there regarding how to make a button in SpriteKit such as Swift Spritekit Adding Button Programaticly and Setting up buttons in SKScene.
Either way, the solution is to make an SKSpriteNode
with a texture for the button and then in the function touchesEnded
, test to see if the touch falls within the button-SKSpriteNode
.
Interestingly, however, is that if one were to make the button a child to, say a SKCameraNode
, or another SKSpriteNode
, then this method no longer works.
My question is why? and how to overcome this dilemma.
UPDATE:
In regards to the post below here are two alternate version of simple GameScene.swift file. The main difference is that in the first case, sprite2
is not a child of the camera, whereas in the version 2 it is.
Note: In the gifs, notice how on version 1, clicking the purple sprite (sprite2
) causes it to print "You tapped the purple sprite.", whereas it says "blue sprite" in version 2. Thus the issue is made clear:
Tapping the child of another SKNode registers as tapping the uppermost parent node, rather than as the node actually tapped!
New question: How to correct this.
Addendum: in version 2 sprite2
has changed positions a bit due to becoming a child of the camera - as it is an M.W.E. and it doesn't impact this demonstration, I chose not to correct it.
Version 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
}
}
Version 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
}
}