does SKSpriteNode recognize gesture? - ios

i build a wall and the bricks are not moving but i need to have one of them calling some info. this code below is inside a function may be should have this brick global
let brk = SKSpriteNode(imageNamed: imgName)
if (imgName == "brickinfo"){
brk.name = "brickinfo"
}
brk.position = CGPoint(x:CGRectGetMinX(self.frame)+brkx, y: CGRectGetMinY(self.frame)+brky)
if (imgName == "brickinfo"){
let singleTap = UITapGestureRecognizer(target: self, action: Selector("ShowInfo"))
singleTap.numberOfTapsRequired = 1
brk.userInteractionEnabled = true
//brk.addGestureRecognizer(ShowInfo) //do not exist
}
brk.xScale = 0.2
brk.yScale = 0.2
addChild(brk)
i am using the same code for other moving images which mean they have SKAction so when i click on them, touches began is fired. they both using the same code, add a name, addchild().
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in touches {
let location = touch.locationInNode(self)
//675
//30
print(self.nodeAtPoint(location).name)
if let theName = self.nodeAtPoint(location).name {
but the name of "brickinfo" is always coming back nil.
Can this brick image fired touchesbegan?
Thank you

ok i was not looking for gesture, i thought i needed to add gesture to trigger the image, but i only needed to have this image responding with its name. After 3-4 days looking at many different ways to do it, i found the solution and it is pretty easy, the zposition was not set up for this specific brick. Now of course this bring its own problem. Sorry for the title of the question. Does xcode comes with a zposition layer view in 3D?
Thank you

Related

How to keep track of animation in Sprite Kit

I need to keep track of animation with texture. I am animating power bar and when user clicks the screen it should stop and save the power. I can not figure out how to save power. So far I have this: on first touch power bar animates, but on the second touch it only stops but does not save power.
This is how I create animation:
textureAtlas = SKTextureAtlas(named:"images")
for i in 1...textureAtlas.textureNames.count{
let name = "\(i).png"
textureArray.append(SKTexture(imageNamed: name))
}
let animateForward = SKAction.animate(with: textureArray, timePerFrame: 0.1)
let animateBackward = SKAction.reversed(animateForward)
let sequence = SKAction.sequence([animateForward,animateBackward()])
This is how I detect touches:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouchStorage = touches.first{ // after first touch
let animateForward = SKAction.animate(with: textureArray, timePerFrame: 0.1)
let animateBackward = SKAction.reversed(animateForward)
let sequence = SKAction.sequence([animateForward,animateBackward()])
arrow.removeAllActions()
arrow.run(SKAction.repeatForever(sequence))
firstTouch = firstTouchStorage
}
for touch in touches{
if touch == firstTouch{
touchesArray.append(touch)
let angle = arrow.zRotation
if touchesArray.count == 2{
arrow.removeAllActions()
arrow.removeFromParent()
}
}
}
I am trying to solve this problem for too long, but I can not figure it out. I hope you will help me.
As #KnightOfDragon suggested : animateWithTextures has a restore: parameter, if you set that to false, when you stop animating the last texture should stay on the sprite. If you go ahead and read the textures description, then you will know what texture it stopped on, and could plan accordingly

SKSpriteNode does not let touches pass through when isUserInteractionEnabled false

I'm attempting to create an overlay in SpriteKit which by using an SKSpriteNode. However, I want the touches to pass through the overlay, so I set isUserInteractionEnabled to false. However, when I do this, the SKSpriteNode still seems to absorb all the touches and does not allow the underlying nodes to do anything.
Here's an example of what I'm talking about:
class
TouchableSprite: SKShapeNode {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Touch began")
}
}
class GameScene: SKScene {
override func sceneDidLoad() {
// Creat touchable
let touchable = TouchableSprite(circleOfRadius: 100)
touchable.zPosition = -1
touchable.fillColor = SKColor.red
touchable.isUserInteractionEnabled = true
addChild(touchable)
// Create overlay
let overlayNode = SKSpriteNode(imageNamed: "Fade")
overlayNode.zPosition = 1
overlayNode.isUserInteractionEnabled = false
addChild(overlayNode)
}
}
I've tried subclassing SKSpriteNode and manually delegating the touch events to the appropriate nodes, but that results in a lot of weird behavior.
Any help would be much appreciated. Thanks!
Reading the actual sources you find:
/**
Controls whether or not the node receives touch events
*/
open var isUserInteractionEnabled: Bool
but this refers to internal node methods of touches.
By default isUserInteractionEnabled is false then the touch on a child like your overlay SKSpriteNode is, by default, a simple touch handled to the main (or parent) class (the object is here, exist but if you don't implement any action, you simply touch it)
To go through your overlay with the touch you could implement a code like this below to your GameScene.Remember also to not using -1 as zPosition because means it's below your scene.
P.S. :I've added a name to your sprites to recall it to touchesBegan but you can create global variables to your GameScene:
override func sceneDidLoad() {
// Creat touchable
let touchable = TouchableSprite(circleOfRadius: 100)
touchable.zPosition = 0
touchable.fillColor = SKColor.red
touchable.isUserInteractionEnabled = true
touchable.name = "touchable"
addChild(touchable)
// Create overlay
let overlayNode = SKSpriteNode(imageNamed: "fade")
overlayNode.zPosition = 1
overlayNode.name = "overlayNode"
overlayNode.isUserInteractionEnabled = false
addChild(overlayNode)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("GameScene Touch began")
for t in touches {
let touchLocation = t.location(in: self)
if let overlay = self.childNode(withName: "//overlayNode") as? SKSpriteNode {
if overlay.contains(touchLocation) {
print("overlay touched")
if let touchable = self.childNode(withName: "//touchable") as? TouchableSprite {
if touchable.contains(touchLocation) {
touchable.touchesBegan(touches, with: event)
}
}
}
}
}
}
}
You need to be careful with zPosition. It is possible to go behind the scene, making things complicated.
What is worse is the order of touch events. I remember reading that it is based on the node tree, not the zPosition. Disable the scene touch and you should be seeing results.
This is what it says now:
The Hit-Testing Order Is the Reverse of Drawing Order In a scene, when
SpriteKit processes touch or mouse events, it walks the scene to find
the closest node that wants to accept the event. If that node doesn’t
want the event, SpriteKit checks the next closest node, and so on. The
order in which hit-testing is processed is essentially the reverse of
drawing order.
For a node to be considered during hit-testing, its
userInteractionEnabled property must be set to YES. The default value
is NO for any node except a scene node. A node that wants to receive
events needs to implement the appropriate responder methods from its
parent class (UIResponder on iOS and NSResponder on OS X). This is one
of the few places where you must implement platform-specific code in
SpriteKit.
But this may not have always been the case, so you will need to check between 8 , 9, and 10 for consistency

SKSpriteNode UserInteractionEnabled Not Working

I've looked everywhere but nothing works. Thought I would just ask the question myself. I'm creating a little game in iOS 9 using SpriteKit. My game is going to have left and right controller buttons to move the player sprite. I add the SKSpriteNodes for the directional pads as follows
At the top of the main scene I put:
private var leftDirectionalPad = SKSpriteNode(imageNamed: "left")
private var rightDirectionalPad = SKSpriteNode(imageNamed: "right")
Then I run a method called prepareDirectionalPads
func prepareDirectionalPads() {
// left
self.leftDirectionalPad.position.x = self.size.width * directionalPadLeftXPositionMultiplier
self.leftDirectionalPad.position.y = self.size.height*directionalPadYPosition
self.leftDirectionalPad.size.width = self.leftDirectionalPad.size.width/directionalPadSizeReductionMultiple
self.leftDirectionalPad.size.height = self.leftDirectionalPad.size.height/directionalPadSizeReductionMultiple
self.leftDirectionalPad.name = "leftDirectionalPad"
self.leftDirectionalPad.alpha = directionalPadAlphaValue
self.leftDirectionalPad.zPosition = 1
self.addChild(leftDirectionalPad)
self.leftDirectionalPad.userInteractionEnabled = true
// right
self.rightDirectionalPad.position.x = self.leftDirectionalPad.position.x*directionalPadSpacingMultiple
self.rightDirectionalPad.position.y = self.size.height*directionalPadYPosition
self.rightDirectionalPad.size.width = self.rightDirectionalPad.size.width/directionalPadSizeReductionMultiple
self.rightDirectionalPad.size.height = self.rightDirectionalPad.size.height/directionalPadSizeReductionMultiple
self.rightDirectionalPad.name = "rightDirectionalPad"
self.rightDirectionalPad.alpha = directionalPadAlphaValue
self.rightDirectionalPad.zPosition = 1
self.addChild(rightDirectionalPad)
self.rightDirectionalPad.userInteractionEnabled = true
}
I clearly set userInteractionEnabled for each SKSpriteNode to true. Then, in touches began I wrote...
override func touchesBegan(let touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
var touch = touches.first! as UITouch
var location = touch.locationInView(self.view)
var node = nodeAtPoint(location)
print("Touched")
}
Note: I have also tried var location = touch.locationInNode(self) but that doesnt work either.
I then run the app (either on my xCode Simulator or iPhone 6). If I touch on the SKNodes, nothing happens. Nothing is printed to the console. However, if I touch anywhere else on the screen, I get "touched" printed to the screen.
What am I doing wrong? I want to detect the touch on the pads so I can move the player sprite accordingly. It might be something really stupid I'm forgetting to do. Thanks for your time and patience. Much appreciated.
Figured it out. So apparently self.leftDirectionalPad.userInteractionEnabled = true does not work. It needs to be self.leftDirectionalPad.userInteractionEnabled = false, which is very counter-intuitive. I don't get it but it works now. touchesBegan responds to when the user touches the SKSpriteNode.
let touch = touches.first! as UITouch
let location = touch.locationInNode(self)
let node = nodeAtPoint(location)
if(node.name == leftDirectionalPad.name)
{
print("left")
}
else if (node.name == rightDirectionalPad.name)
{
print("right")
}

moving sprite based on drag length/direction

In my game I have a node on the bottom of the screen that id like to move along the x axis using touch. I'd like for my node to move left or right depending on the direction of the drag, and to also move the same distance as the drag. So if the user drags from left to right (CGPoint(x: 200, y: 500)toCGPoint(x:300, y: 500)) the node would move 100 to the right. This is what I've tried to do, but it didn't work. If anyone has a way to fix this I'd really appreciate it
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let touchLocation = touch.locationInNode(self)
firstTouch = touchLocation
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let touchLocation = touch.locationInNode(self)
secondTouch = touchLocation
if gameStarted {
let change = secondTouch.x - firstTouch.x
let move = SKAction.moveToX(greenGuy.position.x + change, duration: 0.1)
greenGuy.runAction(move)
}
}
Update your touchesMoved with the following code:
let touch = touches.first as! UITouch
let touchLocation = touch.locationInNode(self)
secondTouch = touchLocation
if gameStarted {
let change = secondTouch.x - firstTouch.x
//Update greenGuys position
greenGuy.position = CGPoint(x: greenGuy.position.x + change, y:greenGuy.position.y)
//Update the firstTouch
firstTouch = secondTouch
}
The reason not using an SKAction as I commented before is because we don't know how much time will pass between two calls of touchesMoved method, so we don't know exactly what time to input in SKAction duration.
You have a pretty good start. First, change:
let move = SKAction.moveToX(greenGuy.position.x + change, duration: 0.1)
to:
let move = SKAction.moveByX(greenGuy.position.x + changeInX, duration: moveDuration)
If you want 2-dimensional movement, use the SKAction moveByX:ChangeInX y:ChangeInY duration:moveDuration instead. Now, you have a few more variables that are based on the duration/distance of the swipe. The duration that you will choose for moveDuration will be up to you, and it will be the product of some coefficient and the swipe distance.
To get the swipe distance:
Id advise you to ditch the touches method and use a UIGestureRecognizer. The one you need is UIPanGestureRecognizer.
Here's a useful link detailing its use: UISwipeGestureRecognizer Swipe length .
Essentially it has different states that get set when the user starts or ends a swipe/drag motion. Then, you could take the locationInView at those moments and calculate the distance between them :D
Hope I helped. I know touchesMoved is also a way to do it, but Ive had problems with it in the past (unnecessary lagging and uncertainty) and gesture recognizers are much more intuitive and easier to use.

When the screen is touched, how do I make the game over?

I want a game over screen and the game to stop when the screen is touched during a specific animation.
let lightTexture = SKTexture(imageNamed: "green light.png")
let lightTexture2 = SKTexture(imageNamed: "red light.png")
let animateGreenLight = SKAction.sequence([SKAction.waitForDuration(2.0, withRange: 0.1), SKAction.animateWithTextures([lightTexture, lightTexture2], timePerFrame: 3)])
let changeGreenLight = SKAction.repeatActionForever(animateGreenLight)
let animateRedLight = SKAction.sequence([SKAction.waitForDuration(2.0, withRange: 0.1), SKAction.animateWithTextures([lightTexture, lightTexture2], timePerFrame: 3)])
let changeRedLight = SKAction.repeatActionForever(animateRedLight)
let greenLight = SKSpriteNode(texture: lightTexture)
greenLight.position = CGPointMake(CGRectGetMidX(self.frame), 650)
greenLight.runAction(changeGreenLight)
self.addChild(greenLight)
let redLight = SKSpriteNode(texture: lightTexture2)
redLight.position = CGPointMake(CGRectGetMidX(self.frame), 650)
redLight.runAction(changeRedLight)
self.addChild(redLight)
When the animation for the red light is on the screen, I want it to be game over. Do I have to make an if statement, and if so for what specifically?
Thank You in advance!
You can make yourself another node which has the same size and position as the red light. Additionally, that node is able to handle touch events. Then, add that node before the animation runs. This can be done with a sequence of actions, e.g.:
let addAction = SKAction.runBlock({ self.addChild(touchNode) })
let animationAction = SKAction.repeatActionForever(animateRedLight)
redLight.runAction(SKAction.sequence([ addAction, animationAction ]))
Update
Since you want the game to end when you touch anywhere, alter the code such that the block sets a variable which indicates that the animation is executed and implement touchesBegan which checks for that variable, e.g.
let addAction = SKAction.runBlock({ self.redLightAnimationRuns = true })
[...]
// In touchesBegan
if touched
{
if redLightAnimationRuns
{
endGame()
}
}
use the touchesBegan() function to call a GameOver() function when the red light is on the screen (which you can control with a variable).
So, when the red light comes on to the screen, variable redLightCurrent is set to true. in TouchesBegan(), when redLightCurrent is true, then call the gameOver() function where you can include what to do when the game is over. This will only occur when a touch has began on the screen.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesBegan(touches, withEvent: event)
let array = Array(touches)
let touch = array[0] as UITouch
let touchLocation = touch.locationInNode(self)
if redLightCurrent {
gameOver()
}
}
This code works with the new xCode 7 and Swift 2.0

Resources