RemoveAction when swiped from node - ios

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.

Related

Touch event not making spritenode reset its size when moved off location of node

I have a sprite node in game that when touched makes an action of the node being pressed and when the touches began event is called and then goes back to the normal size when the touches ended event is called. my problem is when i press down on the node and then move my finger outside of the node it doesn't go back to its original size after I take my finger off the screen.
I tried using multiple things in the touches moved section of the code to try and get it to go back to its original size after i've moved my finger outside of the node while holding the touch down but it didn't work. My code is below
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first!
let node = self.atPoint(touch.location(in: self))
let pushdown = SKAction.scale(to: 0.8, duration: 0.1)
if node == mainMenu.settingsButton {
node.run(pushdown)
} else if node == mainMenu.viewMapButton {
node.run(pushdown)
}else if node == mainMenu.shopButton {
node.run(pushdown)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first!
var location: CGPoint = touch.location(in: self)
let node: SKNode = atPoint(location)
let pushUp = SKAction.scale(to: 1.0, duration: 0.2)
if node != mainMenu.settingsButton {
//node.run(pushUp)
} else if touch.phase == .moved || touch.phase == .cancelled {
node.run(pushUp)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first!
let node = self.atPoint(touch.location(in: self))
let pushUp = SKAction.scale(to: 1.0, duration: 0.2)
if node == mainMenu.settingsButton {
node.run(pushUp)
//Run Sound Here
let scene = SettingsMenu(fileNamed:"SettingsMenu")
scene?.scaleMode = .aspectFill
//scene?.backgroundColor = UIColor.lightGray
let transition = SKTransition.crossFade(withDuration: 0.5)
self.scene?.view?.presentScene(scene!, transition: transition)
} else if node == mainMenu.viewMapButton {
node.run(pushUp)
}
}
How can i get it to go back to the original size after i've moved my finger outside of the nodes location while holding the touch down?
In touchesBegan, touchesMoved, touchesEnded you are referring always the current node at point. let node = self.atPoint(touch.location(in: self))
Why not to keep a reference to the initial node which was detected in touchesBegan to scale it back if the current node atPoint is not equal to the initial one. I assume this would solve your issue.
EDIT:
To capture the node on first touch ...
private var currentTouchedNode : SKNode?
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
let touch: UITouch = touches.first!
currentTouchedNode = self.atPoint(touch.location(in: self))
let pushdown = SKAction.scale(to: 0.8, duration: 0.1)
if currentTouchedNode == mainMenu.settingsButton {
currentTouchedNode.run(pushdown)
} else if currentTouchedNode == mainMenu.viewMapButton {
currentTouchedNode.run(pushdown)
}else if currentTouchedNode == mainMenu.shopButton {
currentTouchedNode.run(pushdown)
}
}
In touchesMoved, touchesEnded you would compare on if the current node is equal currentTouchedNode and resize it if needed

Constant velocity while still being able to apply an impulse

I am making a game where as the main sprite/player moves constantly, he/she needs to jump through barriers.
I need help with how to set a constant velocity for my moving sprite. When I try and do this in the SpriteKit update function, I can’t apply an impulse to jump whenever the user taps the screen.
Here is my code. I commented the places where I am having trouble:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
if (gameStarted == false) {
gameStarted = true
mainSprite.physicsBody?.affectedByGravity = true
mainSprite.physicsBody?.allowsRotation = true
let spawn = SKAction.runBlock({
() in
self.createWalls()
})
let delay = SKAction.waitForDuration(1.5)
let spawnDelay = SKAction.sequence([spawn, delay])
let spawnDelayForever = SKAction.repeatActionForever(spawnDelay)
self.runAction(spawnDelayForever)
let distance = CGFloat(self.frame.height + wallPair.frame.height)
let movePipes = SKAction.moveByX(0, y: -distance - 50, duration: NSTimeInterval(0.009 * distance)) // Speed up pipes
let removePipes = SKAction.removeFromParent()
moveAndRemove = SKAction.sequence([movePipes, removePipes])
} else {
if died == true {
}
else {
mainSprite.physicsBody?.applyImpulse(CGVectorMake(0, 20)) // TRYING TO APPLY AN IMPULSE TO MY SPRITE SO IT CAN JUMP AS IT MOVES
}
}
for touch in touches {
let location = touch.locationInNode(self)
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
updateSpritePosition()
mainSprite.physicsBody?.velocity = CGVectorMake(400, 0) // SETS A CONSTANT VELOCITY, HOWEVER I CAN NOT APPLY AN IMPULSE.
}
The problem is that you're overwriting the velocity in the update method. So even though you added an impulse, it gets immediately overwritten by code in the update. Try overwriting just the dx part of the velocity.
override func update(currentTime: CFTimeInterval) {
updateSpritePosition()
mainSprite.physicsBody?.velocity = CGVectorMake(400, mainSprite.physicsBody?.velocity.dy)
}

How can I stop the animation to overlap while moving my character

I have 2 different textures for my character that overlap/are displayed too fast
while moving the character. How can I set a duration for the animation, so the textures always switch at the same speed while moving the character?
This is my code:
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let animatePlayerStart = SKAction.setTexture(SKTexture(imageNamed: "Player\(i).png"))
// Determine speed for character movement
var minDuration:CGFloat = 0.7;
var maxDuration:CGFloat = 1.8;
var rangeDuration:CGFloat = maxDuration - minDuration;
var actualDuration:NSTimeInterval = NSTimeInterval((CGFloat(arc4random())%rangeDuration) + minDuration)
let move = SKAction.moveTo(location, duration:actualDuration)
player.runAction(SKAction.sequence([animatePlayerStart, move]))
// i determines which texture is going to be displayed
if(self.i == 2) {
self.i = 1
}
else{
self.i++
}
}
}
You are changing texture in touchesMoved which is called fast, thus the effect you are currently getting. To change textures after pre-defined period of time you can use this method:
+ animateWithTextures:timePerFrame:
import SpriteKit
class GameScene: SKScene {
let hero = SKSpriteNode(imageNamed: "heroState_A")
let textureA = SKTexture(imageNamed: "heroState_A")
let textureB = SKTexture(imageNamed: "heroState_B")
override func didMoveToView(view: SKView) {
/* Setup your scene here */
//Because hero is already initialized with textureA, start from textureB
let animation = SKAction.animateWithTextures([textureB,textureA], timePerFrame:0.5)
hero.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame))
addChild(hero)
//Start animation
hero.runAction(SKAction.repeatActionForever(animation),withKey:"heroAnimation")
//Removing heroAnimation
//You can stop this animation by hero.removeAllActions, but if you run animation with key, you can remove just that particular action, which gives you more control
let stop = SKAction.runBlock({
if(self.hero.actionForKey("heroAnimation") != nil){
self.hero.removeActionForKey("heroAnimation")
}
})
//Just an example, in real app you will do this at certain events (eg. when player stops his movement)
runAction(SKAction.sequence([SKAction.waitForDuration(5),stop]))
}
}

SKaction blocked touchesBegan event

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 */
}
}
}

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.

Resources