I have looked around at many articles, but none of them seem to make sense or are relevant to my issue. I want to give the user a set time to press a node. If they succeed in pressing the node within the set time the timer should reset, if they fail to press the node within the set time it will be game over.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
if square.contains(touch.location(in: self)) {
moveSquare()
GameScene.score+=1
scoreLabel.text = "\(GameScene.score)"
}
} else {
gameOverScene()
}
}
so my question is how do I set the timer up to achieve those requirements, and where do I put the code?
Use SKActions instead of timers:
let countdown = SKAction.sequence([SKAction.wait(forDuration: 5),
SKAction.perform(#selector(gameOver), onTarget: Self)])
run(countdown, withKey: "gameOverTimer")
(this assumes that the function gameOver is the one you want to call if the correct button is not touched in time)
and in touchesBegan, if the correct node is touched:
removeAction(forKey: "gameOverTimer")
Related
I am currently working with SpriteKit and I want to run a code block in the update-loop as long as the user is touching a certain SpriteNode. I tried to achieve this by using a Boolean, that gets set to true, when the touchesBegan() method recognises a touch on this node and gets set to false, when the touchesEnded() method recognises a touch ending on this node. However, when the user touches the node and then moves his finger outside of the boundaries, the touchesEnded() method does not recognise this.
Is there a simple way to check if a touch, that began in this node, but then moved outside of it, still exists? Or can I check in general if a UITouch instance still exists?
It's not clear what behavior you want, but in general you probably want to use touch identity to track what's happening.
For example, if you're handling touches in the scene containing the node, and if the desire is simply to have the action start when the node is touched and stop when that touch ends, then something like:
// Whatever touch initiated the action
var activeTouch: UITouch?
// Checked by update loop
var doingSomething = false
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
// Ignore new touches if one is already active
guard activeTouch == nil else { return }
let location = touch.location(in: self)
let touchedNodes = self.nodes(at: location)
for node in touchedNodes {
if <some test for the interesting node> {
activeTouch = touch
doingSomething = true
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
if touch == activeTouch {
// Finished
activeTouch = nil
doingSomething = false
}
}
}
If you also want the action to stop if the user moves their finger off the node and restart if they move back on, then also override touchesMoved, e.g.:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
if touch == activeTouch {
let location = touch.location(in: self)
let touchedNodes = self.nodes(at: location)
// Assume they moved off the node
doingSomething = false
for node in touchedNodes {
if <some test for the interesting node> {
// Nope, still touching it
doingSomething = true
}
}
}
}
}
(You should also handle touchesCancelled in some appropriate way, maybe stopping the action and clearing activeTouch as in touchesEnded)
Perhaps you have some other behavior in mind for the case when there are multiple touches active. You might need to keep track of all the active touches and their status of being on or off the node, and then set doingSomething = true if any active touch is on the node. Or maybe you want touchesMoved to discard an active touch as soon as it moves off the node, so moving that touch back on again won't reactivate.
The main point is that keeping track of touch identity gives you a lot of flexibility, but you have to decide on how you want the game to react.
I am creating a game where the user can move some fruits around in the scene. I want to user to be able to move only the fruits and not any other SKSpriteNode in the scene, so I wrote the code below to implement it. However the code doesn't work properly as I can't seem to be able to drag any of my sprites around, but rather they change position only when I stop touching the screen and they don't move by much anyway.
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
if let location = touch?.location(in: self){
let nodesTouched = nodes(at: location)
for node in (nodesTouched) {
if node is Fruit{
for t in touches {
let locationMoved = t.location(in: self)
node.position.x = locationMoved.x
node.position.y = locationMoved.y
}
}
}
}
}
Anyone knows what's wrong with it?
Thanks in advance!
I found a solution to this, which was to basically set the physicsBody.affectedByGravity property to false for that specific Fruit instance every time I touch it, and then set it back to true as soon as I stop touching it. that makes it possible to drag all the fruits everywhere I want.
I'm trying to implement swipe gestures left and right into a game in SpriteKit where the player collects falling objects spawning from the top of the screen. The problem I'm facing is trying to keep a continuous movement of the player while the finger is on the screen until the touch is ended and the player stays where the last touch ended. There might be a better way to implement this other than swipe gestures hence why I'm asking you guys! Any help would be great, thank you all!
This is not something you want to use a swipe (I think you are using pan) gesture for. What you want to do is override the touchesBegan, touchesMoved, and touchesEnded calls on the scene, and plan your movement according to these 3 methods.
You are probably going to want to use SKAction.move(to:duration:) with these methods, and figure out the math to keep a constant speed.
E.G.
func movePlayer(to position:CGPoint)
{
let distance = player.position.distance(to:position)
let move = SKAction.move(to:position, duration: distance / 100) // I want my player to move 100 points per second
//using a key will cancel the last move action
player.runAction(move,withKey:"playerMoving")
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
let touch = touches.first
let position = touch.location(in node:self)
movePlayer(to:position)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
{
let touch = touches.first
let position = touch.location(in node:self)
movePlayer(to:position)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let position = touch.location(in node:self)
movePlayer(to:position)
}
I have a few button with amorphous look. (the Rect of the buttons are intersecting) First I evaluated the BeganTouch in the GameScene. Then I get several touches.
Since I have in my buttons still child nodes, they have swallowed the touchs. Ok, I have made with the help of here a subclass of the SpriteNodes and processed the touch inside the subclass. Now I have the problem that the first button does not pass the touch to the underlying sprites.
But now I would like top ignore the transparent areas of the buttons, how can I do that?
I have read that one can work with physicsbody, I have tried, gravitiy = 0 but since I move and scale the buttons via actions there were violent effects.
Why can I check for the alpha in the touch location and pass the touch to the next sprite.
by the way: how can I get a reference to the view? to get the global loc.
let loc = touch.location(in: view)
with the global touch location I could check all sprites under this point for the alpha!
you can try passing the touch to it's parent (presumably the scene) from your subclass.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch! {
//handle whatever code you want your subclass to do
...
//pass the touch event to the parent
super.touchesBegan(touches, with: event)
}
}
and then in your scene, cycle through the your buttons (in this example I have the buttons in a button array)
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch! {
let touchLocation = touch.location(in: self)
for button in buttons {
if button.contains(touchLocation) {
//handle all other buttons
...
}
}
}
}
although this seems a little redundant to do the touches in two different locations. #Knight0fDragon is correct, it seems odd to have a button have transparent areas.
ok, it's simple, all buttons are from the same subclass. this subclass delegates to an method in the GameScene. here I can check with
allNodes = nodes(at: globalLocation)
now I can check for the name of the node, calculate the point of the pixel inside each node and get the alpha value.
thanxs all
I have the following code in a pong paddle game in the GameViewController:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
main.run(SKAction.moveTo(x: location.x,duration: 4), completion: {
print( "COMPLETED TOUCH BEGAN")
})
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
main.run(SKAction.moveTo(x: location.x,duration: 1), completion: {
print( "COMPLETED TOUCH MOVED")
})
}
}
For some reason, when the duration of touchesBegan is greater than the duration of touchesMoved, my player will complete the touchesMoved actions first (even though touchesBegan had to come first), and then jump to a different x-position and then move with duration 4 to complete the touchesBegan. It's almost like the player reaches the position designated in touchesMoved (which should be almost exactly identical to the position in touchesBegan), goes back a somewhat arbitrary amount, and then moves to touchesBegan position. Is there a reason why it's jumping back and then moving like so?
My hypothesis is that for some reason, the touchesMoved actions are ran concurrently, and when they finish, the position jumps back to where it is slowly moving in touchesBegan and completes that movement to completion - almost like there are two objects moving at different rates with the slower one coming up after the faster one finishes. Is this at all correct? If so, why is it like this?
Edit:
I see that when I make the paddle move to one side of the screen, then tap on the opposite end of the screen and slide over, it will move to the final position that I released at, and then jump and move to where I first pressed. It's almost like the first action is playing in the background. The strange thing is if I hold my finger down (keeping the touchesMoved method called) until the movement in the touchesBegan finishes (so here it's 4 seconds), it doesn't jump to the other side of the screen!
In summary, I have multiple SKActions running with different durations, and from my observation, the priority is over the most recent action added to the action array. But instead of playing multiple actions over each other, the object jumps to different places on the screen to complete the final action.
If you set up a second SKAction moving the same node, the second one doesn't cancel out the first. Rather, the position of the node is calculated after every frame based on all SKActions running. If the SKActions have the same duration and run at the same time, the last SKAction to run will always win out. For example:
// Same duration, node finishes at 100
main.run(SKAction.moveTo(x: -100, duration: 2)) //first to run
main.run(SKAction.moveTo(x: 100, duration: 2)) //second to run
If the first SKAction has a greater duration however, after the second SKAction has finished the position will only be calculated based on the first SKAction, for example:
//First SKAction's duration is longer, node finishes at -100
main.run(SKAction.moveTo(x: -100, duration: 3))
main.run(SKAction.moveTo(x: 100, duration: 2))
That's the reason why you're seeing the 'jump'.
SKAction documentation:
A node can run multiple actions simultaneously, even if those actions
were executed at different times. The scene keeps track of how far
each action is from completing and computes the effect that the action
has on the node. For example, if you run two actions that move the
same node, both actions apply changes to every frame. If the move
actions were in equal and opposite directions, the node would remain
stationary.
In your code touchesMoved can be called many times, and each time it is adding another SKAction to the node. I recommend you remove all actions on the main node before running another SKAction:
main.removeAllActions()
So in the end it will be:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
main.removeAllActions()
main.run(SKAction.moveTo(x: location.x,duration: 4), completion: {
print( "COMPLETED TOUCH BEGAN")
})
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
main.removeAllActions()
main.run(SKAction.moveTo(x: location.x,duration: 1), completion: {
print( "COMPLETED TOUCH MOVED")
})
}
}