I am trying to implement a pause button in my game. However every time I tap the pause button (SKSpriteNode) on my iOS Device nothing happens. I have tried making the button do other actions and tried making other sprites do the action. None have worked, although I am able to touch any location on the screen and the action is performed. I am using Swift 4 with the latest version of Xcode (9.4.1). The app is a iOS Game and I am using the GameScene.swift that is created along with the app.
Here is the part of the code for the button (irrelevant parts of the code are left out):
import SpriteKit
import CoreMotion
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var pauseButton:SKSpriteNode!
override func didMove(to view: SKView) {
pauseButton = SKSpriteNode(imageNamed: "pauseButton")
pauseButton.size = CGSize(width: 50, height: 50)
pauseButton.position = CGPoint(x: self.size.width -
pauseButton.size.width, y: self.size.height -
pauseButton.size.height)
pauseButton.zPosition = 5
self.addChild(pauseButton)
}
override func touchesEnded(_ touches: Set<UITouch>, with: UIEvent?) {
fireBullet() //This function does not relate to the pause button.
}
override func touchesBegan(_ touches: Set<UITouch>, with event:
UIEvent?) {
let touch = touches.first
if let location = touch?.location(in: self) {
let nodesArray = self.nodes(at: location)
if nodesArray.first?.name == "pauseButton" {
self.view?.isPaused = true
}
}
}
}
Thanks in advance for taking your time to reply it really helps me!
Thomas
Easy fix, in your touchesBegan method you are searching for touches on a node named "pauseButton" but there are no nodes named "pauseButton" so your search returns a false value.
Just add pauseButton.name = "pauseButton" to your code where you set up the pause button and it should work.
Related
So I have these moving sprites with physics bodies that I initially have constrained to one area. Let's say call this area Box1. When the sprites bump the borders of Box1, they bump back and the SKConstraint is just an additional measure to make sure they stay within the box.
What I'm trying to do is make a button that causes all the sprites in Box1 move across the screen to a second box (Box2) that is basically identical to Box1 but right beside it. Box1 and Box2 share a border wall, so essentially the screen is divided in two sections.
My problem is when I call up my button function, the sprites get stuck at the wall and aren't able to go past it to Box2. I've tried setting both the physics bodies and the SKConstraints of the nodes to nil so I don't know what else is preventing them.
func moveSprites(){
if box1Sprites.isEmpty == false {
for sprite in box1Sprites{
sprite.constraints = nil
sprite.physicsBody = nil
let moveAction = SKAction.move(to: CGPoint(x: -300, y: 0), duration: 1) //This is a point within Box2
sprite.run(moveAction)
}
}
}
EDIT:
I also made it so that I could drag and drop the sprites from one box to the other and the code for that is pretty much the same and it works fine.
var currentMovingSprite: SKSpriteNode? = SKSpriteNode()
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let pos = touch.location(in: self)
let node = self.atPoint(pos)
if node.name == "sprite"{
node.constraints = nil
node.physicsBody = nil
currentMovingSprite = node
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let pos = touch.location(in: self)
if let sprite = currentMovingSprite{
sprite.position = pos
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
spritePhysics(sprite: currentMovingSprite) // resets the sprite to how it was before
currentMovingSprite = nil
}
I'm currently working on a game in swift using spritekit. I've got my joystick working using this library, and thats all good. I'm also moving the joystick to where the user taps using this code:
func updateJoystickPosition(pos: CGPoint) {
let posX = -(self.sceneSize.width/2) + pos.x
let posY = (self.sceneSize.height/2) - pos.y
let newJoystickPos = CGPoint(x: posX, y: posY);
self.joystick.position = newJoystickPos
}
which also works just fine, however, the joystick doesn't engage on that one tap. You have to tap on the actual joystick node for the joystick to engage, so obviously, i'd want the user to put their finger down on the screen and immediately be able to start moving it around to move the player.
the joystick it's self starts tracking with this function
open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print(touches)
if let touch = touches.first, stick == atPoint(touch.location(in: self)) {
tracking = true
startHandler?()
}
}
Question: Is there anyway i can modify the touchesBegan function or any of my other functions to achieve the desired results?
You have to override touchesMoved(_ touches: ,with event:) and in this method just processing all touches.
For example:
var startTouchingPoint = CGPoint.zero // property
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
startTouchingPoint = touch.location(in: self.camera!))
yourControllerPlace.position = startTouchingPoint
stickOnYourJoystick.position = CGPoint.zero
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let point = touch.location(in: self.camera!))
let convertedPoint = CGPoint(x: point.x - startTouchingPoint.x,
y: point.y - startTouchingPoint.y)
stickOnYourJoystick.position = convertedPoint
}
}
I'm subclassing SKSpritenode (In Swift) to create coloured blocks that can then be dragged around the scene. The subclass is SoundNode.
import SpriteKit
class SoundNode: SKSpriteNode {
init() {
super.init(texture: nil, color: UIColor.blue, size: CGSize(width: 100, height: 100))
self.isUserInteractionEnabled = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("touch began")
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
print("touches moved")
guard let touch = touches.first else {
return
}
let touchLocation = touch.location(in: self)
self.position = touchLocation
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
print("touch ended")
}
}
in GameScene.swift
import SpriteKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
addSoundBlock()
}
func addSoundBlock() {
let soundBlock = SoundNode()
soundBlock.position = CGPoint(x: 800, y: 800)
addChild(soundBlock)
}
}
This works, sort of.
soundBlock is added, and can be dragged around the scene. But it flickers and sometimes disappears.
I have tried other methods within touchesMoved, none of them effected the jerkiness.
If I don't subclass the touchesBegan, touchesMoved, touchesEnded, and implement the actions in GameScene, then the dragging becomes smooth. But future plans hinge on being able to subclass these.
Xcode 8, Swift 3, iOS10
I created a class method to handle the movement of the subclass of SKSpriteNode, in my case 'Player' and passed it the location of the touch within GameScene.swift.
Class method declaration in Player.swift:
func movePlayer(location: CGPoint) {
//movement code
}
Calling the function in GameScene.swift:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let location = touches.first?.location(in: self) else { return }
player.movePlayer(location: location)
}
Somewhat cleaner, I suppose. And allows for further subclassing.
I had this same issue, with a subclass of SKNode as the child element of a SKNode on the GameScene parent:
GameScene (top level parent)
GridNode (SKNode subclass, child of GameScene)
TileNode (SKNode subclass, child of GridNode)
The touchesMoved method called from TileNode was returning erratic values for touches.first.location when swiping in a straight line:
134.6666259765625
-42.33331298828125
133.6666259765625
-41.6666259765625
132.33331298828125
-40.6666259765625
131.6666259765625
-40.33331298828125
I believe the issue stems from TileNode's size (and therefore position) being effectively 0 from the perspective of the GameScene.
I solved it like this (in the TileNode class):
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
guard let parent = self.parent as? GridNode else { return }
let movingPoint: CGPoint = touch.location(in: parent)
...
}
Note touch.location(in: parent) vs. touch.location(in: self). GridNode has a set position in its GameScene parent, whereas TileNode does not.
Now movingPoint returns smooth values, without the jitter.
Hope this helps someone.
I have sometimes found inconsistent appearance of a sprite (i.e. sometimes visible, sometimes not) when zPosition is not set to ensure that the sprite is in front of other nodes. I think this might be because if two nodes have the same zPosition, then there isn't a way for the graphics engine to ensure which one is on top.
Try setting zPosition to some number which ensures it's in front of other nodes.
For example after:
soundBlock.position = CGPoint(x: 800, y: 800)
add:
soundBlock.zPosition = 1000
I haven't tried running this but may be worth a go?
In my previous app (single view application) i used touch down and touch up inside IBActions for buttons in my game. However in a SpriteKit game you have to completely create the scene and i am having a hard time coding my touchesBegan method.
Here is my basic movement method/function:
func heroMovementLeft () {
hero.position = CGPointMake(hero.position.x - 0.5,hero.position.y);
}
here is my Left arrow node, i would like the HeroMovementLeft method to be called upon touching the LeftArrow
func LeftArrow () {
let LeftArrow = SKSpriteNode(imageNamed: "Left Arrow.png")
LeftArrow.position = CGPointMake(30, 30)
self.addChild(LeftArrow)
}
Here's where I am at right now for coding the touchesBegan method
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in touches {
}
}
Do I create an if statement? Do I make touch a variable? What do I need to write in my touches began method so that my hero will move when left arrow is touched. I have looked all over online and cannot find an answer to this question, please help.
You basically have 2 options to identify your sprites
1) make them global properties to your scene
class YourScene: SKScene {
var leftArrow: SKSpriteNode!
override func didMoveToView(view: SKView) {
}
func leftArrow() {
leftArrow = SKSpriteNode(imageNamed: "Left Arrow.png")
leftArrow.position = CGPointMake(30, 30)
addChild(leftArrow)
}
}
than in touches began you can get the location like so
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in touches {
let location = touch.locationInNode(self)
let node = nodeAtPoint(location)
if node == leftArrow {
heroMovementLeft()
}
}
}
Option 2 is to give your sprites names
func leftArrow() {
let leftArrow = SKSpriteNode(...)
leftArrow.name = "LeftArrow"
...
}
and than check for those names in the touches began method
....
if node.name == "LeftArrow" {
heroMovementLeft()
}
As a side note you should start your properties or func with small letters and only start classes, structs and protocols with capital letters.
I am really really frustrated by this and I have been trying to get it to work for almost two full days now :(
How do I go about adding a click event to a SKSpriteNode in swift
let InstantReplay:SKSpriteNode = SKSpriteNode(imageNamed: "InstantReplay")
InstantReplay.position = CGPoint(x: size.width + InstantReplay.size.width/2, y: size.height/2)
InstantReplay.size = CGSize(width: size.width/1.4, height: size.height/8)
addChild(InstantReplay)
InstantReplay.runAction(SKAction.moveToX(size.width/2, duration: NSTimeInterval(1)))
All I want to happen is when "InstantReplay" is clicked for it to run a function called "InstantReplay_Clicked", How would I go about Implementing this?
Any help much appreciated :)
Give your SKSpriteNode a name so we can identify it in the touchesBegan or touchesEnded method of your SKScene.
Also set userInteractionEnabled of your SKSpriteNode to false, so that it doesn't trap touch events for itself, and not pass them up to the your Scene.
override init() {
super.init()
let instantReplay = SKSpriteNode(imageNamed: "InstantReplay")
instantReplay.position = CGPoint(x: size.width + instantReplay.size.width/2, y: size.height/2)
instantReplay.size = CGSize(width: size.width/1.4, height: size.height/8)
instantReplay.name = "InstantReplay"; // set the name for your sprite
instantReplay.userInteractionEnabled = false; // userInteractionEnabled should be disabled
self.addChild(instantReplay)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let location = touches.anyObject()?.locationInNode(self)
let node = self.nodeAtPoint(location!)
if (node.name == "InstantReplay") {
println("you hit me with your best shot!")
}
}
(oh - i also renamed your instantReplay variable to use lowerCamelCase, in line with Swift best practices.
Swift 4 Update
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches {
let node = self.atPoint(t.location(in :self))
if (node.name == "InstantReplay") {
print("you hit me with your best shot!")
}
}
}