SKaction blocked touchesBegan event - ios

I have a grid with different square. When user click in the square this square do a flip. But during the time of this flip the event of touchesBegan didn't work.
This code is execute after click in square :
func flipTile(node : RectSprite){
let flip = SKAction.scaleXTo(-1, duration: 0.5)
let flip2 = SKAction.scaleXTo(-1, duration: 0.2)
node.setScale(1.0)
var key : String = Array(CircleColor.colors.keys.array)[10]
var changeColor = SKAction.runBlock( { node.texture = SKTexture(imageNamed:"white")})
var removeCombinaison = SKAction.runBlock( {
self.combination.last?.removeFromParent()
self.combination.removeLast()
self.updateGrid()
})
var action = SKAction.sequence([flip, changeColor])
var action2 = SKAction.sequence([flip2, removeCombinaison] )
node.imageName = "white"
node.name = "square"
node.runAction(action)
self.combination.last?.runAction(action2)
}
But if i click fast in the next square the event touch didn't work. Maybe i need to execute this code in the new thread.

You could try to check the touch position using (I think this even works during the flip animation):
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
if yourNode.containsPoint(location) {
/* called whenever the square is touched */
} else {
/* called when some other location on the screen is touched */
}
}
}

Related

How do I use buttons or a slider to rotate a sprite?

I am making a game for iOS in Swift with SpriteKit, the game is currently just a ball moving around with a sword.
I have anchored the sword to the bottom of the sword, but I need to know how to control the direction of rotation with 2 buttons or a slider of some sort.
Here is my code:
import SpriteKit
let sprite = SKSpriteNode(imageNamed:"Player")
let weapon = SKSpriteNode(imageNamed: "weapon")
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
let initialPlayerLocation = CGPoint(x: self.frame.width/2, y: self.frame.height/2)
/* Setup your scene here */
//player
sprite.setScale(1.0)
sprite.position = initialPlayerLocation
sprite.zPosition = 20
self.addChild(sprite)
//weapon
weapon.setScale(1.0)
weapon.position = initialPlayerLocation
weapon.zPosition = -20
weapon.anchorPoint = CGPointMake(0.5,0.0);
self.addChild(weapon)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
var move = SKAction.moveTo(location, duration:1.0)
sprite.runAction(move)
weapon.runAction(move)
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
Okay, I think I understand what you are trying to do. It involves a lot of code, but in theory it is pretty simple. We detect if the touch is inside the left or right buttons (which in this example, I've made them SKSpriteNodes), and then set boolean values for the update method to use and rotate the weapon node (I'm assuming this is the sword you were talking about).
I've also included a variable that lets you set the speed of rotation. As you did not say what speed you were looking for, I left it up to you.
At the top of the program:
let sprite = SKSpriteNode(imageNamed:"Player")
let weapon = SKSpriteNode(imageNamed: "weapon")
let leftButton = SKSpriteNode(imageNamed: "leftButton")
let rightButton = SKSpriteNode(imageNamed: "rightButton")
var leftPressed = false
var rightPressed = false
let weaponRotateSpeed = 0.01 // Change this for rotation speed of weapon
And then modify touchesBegan to look like this:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
if (leftButton.containsPoint(p: location))
leftPressed = true
else if (rightButton.containsPoint(p: location))
rightPressed = true
else {
var move = SKAction.moveTo(location, duration:1.0)
sprite.runAction(move)
weapon.runAction(move)
}
}
In update, add this code:
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if (leftPressed && rightPressed) {
// Do nothing, as both buttons are pressed
} else if (leftPressed) {
weapon.zRotation -= weaponRotateSpeed
} else if (rightPressed) {
weapon.zRotation += weaponRotateSpeed
}
}
We want to stop the weapon from rotating when we detect when the finger has moved off the button like so:
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
if (leftButton.containsPoint(p: location))
leftPressed = true
else
leftPressed = false
if (rightButton.containsPoint(p: location))
rightPressed = true
else
rightPressed = false
}
}
And at last, we need to detect when your finger has left the screen:
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if (leftButton.containsPoint(p: location))
leftPressed = false
if (rightButton.containsPoint(p: location))
rightPressed = false
}
}

How can I avoid a delay with continues touch detection in swift?

I want control a character on my screen with a continuous swipe gesture. As soon as I do a swipe +x or -x my character moves left or right. And as long as I hold my finger it continuous to move to the direction in that I swiped. When I swipe into the opposite direction of my last swipe without leaving my finger from the touch ground, my character should move into this direction. When I release my finger from the touch screen, my character should stops instantly.
My problem is that there is often a small delay within the change of direction. And this delay makes it less precise.
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
var player = self.childNodeWithName("man")
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(location)
lastFingerPosition = location
}
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent){
var player = self.childNodeWithName("man")
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(location)
if(location.x > lastFingerPosition?.x){
movePlayer("right")
} else if(location.x < lastFingerPosition?.x)
movePlayer("left")
}
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
var player = self.childNodeWithName("man")
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(location)
if(touchedNode.name != "buttonFire"){
player?.removeAllActions()
lastMoveDirection = ""
}
}
}
func movePlayer(direction: String) {
var player = self.childNodeWithName("man")
if(lastMoveDirection != direction){
player?.removeAllActions()
lastMoveDirection = direction
var duration:CGFloat? = 0
var x = player?.position.x
duration = x!
if(direction == "left"){
var xDestination:CGFloat = 10
var run = SKAction.moveToX(10, duration: NSTimeInterval(Double(duration! / runSpeed)))
player?.runAction(run, withKey: "moveLeft")
} else if(direction == "right") {
duration = self.frame.width - x!
var xDestination = frame.size.width - 1
var run = SKAction.moveToX(xDestination, duration: NSTimeInterval(Double(duration! / runSpeed)))
player?.runAction(run, withKey: "moveRight")
}
}
}
I think it is not a programmatic problem. It is more an issue of the control itself. Because a swipe with your finger into one direction and then, a swipe to the opposite direction results in a rotation of your thumb.
And while it's rotating a little bit, it is not moving and that causes the feeling of a delay.
I guess this control is useless, if you need really precise moves - just because of the anatomy of our thumbs.

RemoveAction when swiped from node

I have this sene with few nodes in it. It is circle inside circle inside circle. Pressing on smallest circle inside, I have this animation made with few SKActions.
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
let growOut = SKAction.scaleTo(1.2, duration: 0.3)
let growIn = SKAction.scaleTo(1.0, duration: 0.5)
let glowOut = SKAction.fadeAlphaTo(0.5, duration: 0.3)
let glowIn = SKAction.fadeAlphaTo(1, duration: 0.5)
let sOut = SKAction.group([glowOut, growOut])
let sIn = SKAction.group([glowIn, growIn])
let circleTouched = SKAction.sequence([sOut, sIn])
let circleRepeat = SKAction.repeatActionForever(circleTouched)
for touch in touches {
let location = (touch as! UITouch).locationInNode(self)
if let theCircle = nodeAtPoint(location) as SKNode?{
if theCircle.name == "SmallCircle" {
theCircle.runAction(circleRepeat, withKey: "circleTouched")
}
}
}
}
When touch ends, I remove this action like so:
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
let growIn = SKAction.scaleTo(1.0, duration: 0.5)
let glowIn = SKAction.fadeAlphaTo(1, duration: 0.5)
let sIn = SKAction.group([glowIn, growIn])
for touch in touches {
let location = (touch as! UITouch).locationInNode(self)
if let theCircle = nodeAtPoint(location) as SKNode?{
theCircle.runAction(sIn)
theCircle.removeActionForKey("circleTouched")
}
}
}
But when I move my finger out of this circle with actions on it, it keeps on playing. I've tried to fix it with touchesMoved function, but it acts kind of strange for me.
override func touchesMoved(touches: Set, withEvent event: UIEvent) {
let growIn = SKAction.scaleTo(1.0, duration: 0.5)
let glowIn = SKAction.fadeAlphaTo(1, duration: 0.5)
let sIn = SKAction.group([glowIn, growIn])
let circleRepeat = SKAction.repeatActionForever(circleTouched)
for touch in touches {
let location = (touch as! UITouch).locationInNode(self)
if let theCircle = nodeAtPoint(location) as SKNode?{
if theCircle.name != "SmallCircle" {
println("I'M MOVING FROM NODE!!!")
theCircle.runAction(sIn)
theCircle.removeActionForKey("circleTouched")
}
}
}
}
So I receive this "I'M OUT OF THE NODE" signal, but action don't stop.
Where am I wrong? The same code works for touchesEnded function.
The problem happens because of this one
if let theCircle = nodeAtPoint(location) as SKNode?
Everytime you move your mouse, "theCircle" resets. For example, for the first time, you click the circle, "theCircle" is the circle you clicked, hence the animation is attached to it. For the second time, say, you clicked the background, this time "theCircle" is the background, so it does not have the animation you set, therefore there is no way to remove "the animation".
The solution is, you declare the circle as a scope level variable, usually inside the class, at the top:
var smallCircle: SKSpriteNode!
Then in didMoveToView(view: SKView), configure the circle(if you use .sks):
smallCircle = childNodeWithName("the circle name") as! SKSpriteNode
smallCircle.name = "SmallCircle"
This time, you can point to the circle in touchMoved:
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
if let theCircle = nodeAtPoint(location) as SKNode?{
if theCircle.name != "SmallCircle" {
smallCircle.runAction(sIn)
smallCircle.removeActionForKey("circleTouched")
}
}
}
At last, you will find the animation stopped.

Slingshot Always Returns to Anchor Point When touchesMoved Stops

So I have a pretty basic slingshot that has two physics bodies attached to one another using a SKPhysicsSpringJoint. When touches begin, I create a projectile and spring. When touches are moved, I move the projectile accordingly to the user's touch position. When touches end, the projectile is launched.
The issue is whenever the user stops moving the projectile, but doesn't let go, the projectile moves towards its anchor point and swings around it until the user releases their touch. I've been working for hours trying to figure out why this is happening, any ideas?
Here are the main functions:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let touchLocation = touch.locationInNode(self)
//Add Cannon Ball
var uniqueID:Int = nodeArray.count
var uniqueString = "cannonBall\(uniqueID)"
var cannonBall = CannonBallNode(location: CGPoint(x: size.width/2, y: 200))
cannonBall.name = uniqueString
// 3 - Determine offset of location to projectile
let offset = touchLocation - cannonBall.position
// 4 - Bail out if you click above cannon
if (offset.y > 50) { return }
self.addChild(cannonBall)
println("\(nodeArray.count)")
if var cannon = childNodeWithName("cannon") {
if var heldCannonBall = childNodeWithName(uniqueString) {
heldCannonBall.physicsBody?.affectedByGravity = false
var spring = SKPhysicsJointSpring.jointWithBodyA(cannon.physicsBody, bodyB: cannonBall.physicsBody, anchorA: cannon.position, anchorB: cannonBall.position)
spring.damping = 0.4;
spring.frequency = 1.0;
// Add Physics
physicsWorld.addJoint(spring)
}
}
}
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let touchLocation = touch.locationInNode(self)
var uniqueID:Int = nodeArray.count
var uniqueString = "cannonBall\(uniqueID)"
if var heldCannonBall = childNodeWithName(uniqueString) {
// 3 - Determine offset of location to projectile
let offset = touchLocation - heldCannonBall.position
// 4 - Bail out if you are shooting down or backwards
if (offset.y > 0) { return }
heldCannonBall.position = touchLocation
}
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let touchLocation = touch.locationInNode(self)
var uniqueID:Int = nodeArray.count
var uniqueString = "cannonBall\(uniqueID)"
if var releasedCannonBall = childNodeWithName(uniqueString) {
var angle = CGFloat(M_PI_4)
var magnitude:CGFloat = 1;
releasedCannonBall.physicsBody?.applyImpulse(CGVectorMake(magnitude*cos(angle), magnitude*sin(angle)))
releasedCannonBall.physicsBody?.affectedByGravity = true
}
runAction(SKAction.sequence([
SKAction.runBlock{[self.physicsWorld.removeAllJoints()]},
SKAction.waitForDuration(0.1)]
))
nodeArray.append("\(uniqueString)")
}
}
Finally came across the solution! I needed to produce an update function to update the projectile's position as it was moved via the touchesMoved function. I created a global variable and had it store the location of the touch when it began and when it was moved. I threw it in the update function and voila, no more issues.
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
//#1
var uniqueID:Int = nodeArray.count
var uniqueString = "cannonBall\(uniqueID)"
println("\(cannonBallLocation)")
if var releasedCannonBall = childNodeWithName(uniqueString) {
releasedCannonBall.position = cannonBallLocation
}
} //end func

Detect Touches on SKNode in Array

I have the following function which spawns squares and adds them to an array of squares. This adds new squares indefinitely until the function is stopped. The array of squares is declared in the SKScene like so: var rsArray = [RedSquare]().
func spawnRedSquares() {
if !self.gameOver {
let rs = RedSquare()
var rsSpawnRange = self.frame.size.width/2
rs.position = CGPointMake(rsSpawnRange, CGRectGetMaxY(self.frame) + rs.sprite.size.height * 2)
rs.zPosition = 3
self.addChild(rs)
self.rsArray.append(rs)
let spawn = SKAction.runBlock(self.spawnRedSquares)
let delay = SKAction.waitForDuration(NSTimeInterval(timeBetweenRedSquares))
let spawnThenDelay = SKAction.sequence([delay, spawn])
self.runAction(spawnThenDelay)
}
}
I'm trying to use the touchesBegan() function to detect when a specific square in the array is tapped and then access the properties of the square. I can't figure out how to determine which square is being touched. How would I go about doing this?
Give each of the squares you spawn a unique name and check that name in touchesBegan. You can use a counter and do
rs.name = "square\(counter++)"
In touchesBegan you can retrieve the name of the touched node and check it against the names of the nodes in the array.
First you have to give the rs node a name. For example
rs.name = "RedSquare"
Then you can use the nodeAtPoint function to find the node at a particular touch point. If the node is a RedSquare, you can modify it.
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch in touches {
let touchPoint = touch.locationInNode(self)
let node = self.nodeAtPoint(touchPoint)
if node.name == "RedSquare" {
// Modify node
}
}
}
I was able to answer my own question by experimenting, and decided that I would post the answer in case anyone else had a similar problem. The code that I needed inside the touchesBegan() function was the following:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let rsCurrent = self.nodeAtPoint(location)
for RedSquare in rsArray {
let rsBody = RedSquare.sprite.physicsBody
if rsBody == rsCurrent.physicsBody? {
//Action when RedSquare is touched
}
}
}
}

Resources