Adding tap functionality to ColorSprites - swift

Making a Texas Hold'Em app. This is my first time working in sprite-kit. My current problem is that I have the cards as Color Sprite objects.
This is the GameScene with the cards. Ignore the five cards on the top. I only want the bottom two to have the flip functionality.
My goal is for the user to be able to tap one of their cards and it will flip from the back of the card, to the card in their hand and vice versa. My current strategy is using this override func touchesBegan.
The array playerOne is the cards for playerOne.
The cardOne and cardTwo are the cards for the playerOne.
FlippedOne and flippedTwo are keeping track of which card is currently flipped. This way the cards would flip individually.
This same concept is repeated if there is multiple players.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if (playerCount == 0 && turn == 0) {
let cardOne = playerOne[0].getNum()
let cardTwo = playerOne[1].getNum()
var flippedOne = 0
var flippedTwo = 0
for touch in touches {
let location = touch.location(in: self)
if (playerCardOne.frame.contains(location) && flippedOne == 0) {
playerCardOne.texture = cardTextures[cardOne]
flippedOne = 1
}
if (playerCardOne.frame.contains(location) && flippedTwo == 1) {
playerCardOne.texture = back
flippedOne = 0
}
if (playerCardTwo.frame.contains(location) && flippedTwo == 0) {
playerCardTwo.texture = cardTextures[cardTwo]
flippedTwo = 1
}
if (playerCardTwo.frame.contains(location) && flippedTwo == 0) {
playerCardTwo.texture = back
flippedTwo = 0
}
}
}
}
If there is a better way of making the cards flip on tap, I am open to ideas.
My thought on why this is not working is because it's an override function.
What currently happens is that I can get one of the cards to flip from the back to the actual card face. After which I cannot get either card to flip.

the issue with why you can't repeatedly run the code is because you are resetting the flippedOne and flippedTwo variables every time touches began gets fired
Move the variables out to the scene and it will work fine.
I also recommend converting them to Bool for simplicity
Also you don't need the frame property when checking if the spriteNode contains a location
Also brackets around the if statement aren't required in Swift (you must've come from a different language to Swift)
private var flippedOne = false
private var flippedTwo = false
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let location = touch!.location(in: self)
if card1.contains(location) {
if flippedOne {
card1.texture = back
flippedOne = false
}
else {
card1.texture = cardTextures[cardOne]
flippedOne = true
}
}
if card2.contains(location) {
if flippedTwo {
card2.texture = back
flippedTwo = false
}
else {
card2.texture = cardTextures[cardTwo]
flippedTwo = true
}
}
}
The if statements in the touches began func can be further simplified as such...
if card1.contains(location) {
card1.texture = flippedOne ? back : cardTextures[cardOne]
flippedOne = !flippedOne
}
if card2.contains(location) {
card2.texture = flippedTwo ? back : cardTextures[cardTwo]
flippedTwo = !flippedTwo
}

Related

Sprite-Kit Variable won't stop adding

In my game I am trying to add 1 to a variable score every time certain conditionals happen in my update function. The problem is that the update function is called every frame so my score variable just keeps climbing into the hundreds when it should be going up by 1 slowly. Here is my code:
override func update(_ currentTime: TimeInterval) {
if circleStart.frame.width >= self.frame.width && userTouched == false{
hasTouchedEdge = true
}
if hasTouchedEdge == true && userTouched == false {
scaleSpeed -= 0.2
}
if scaleSpeed <= 1.0 && userTouched == false {
scaleSpeed += 0.3
hasTouchedEdge = false
}
if userTouched == false{
scaleSpeed += 0.1
circleStart.setScale(CGFloat(scaleSpeed))
circleLength = circleStart.frame.width
acceptableBound1 = outerCircle.frame.width
acceptableBound2 = outerCircle.frame.width - 100
}
if userTouched == true && circleLength > acceptableBound2 && circleLength < acceptableBound1{
print("worked")
isCorrect = true
}
if userTouched == true && circleLength > acceptableBound1 {
gameOverLabel.isHidden = false
playAgain.isHidden = false
playAgain.run(fadingTapIn)
}
if userTouched == true && circleLength < acceptableBound2{
gameOverLabel.isHidden = false
playAgain.isHidden = false
playAgain.run(fadingTapIn)
}
if isCorrect == true {
score += 1
}
scoreLabel.text = String(score)
}
What do I need to do or add to have my score variable work in my desired way explained above?
Easy solution:
Add isCorrect = false after score += 1.
Good solution:
Try removing most of that code in update. Update is called nearly 60 times per second in most cases and it is not the best place to make changes for a few reasons.
Try using
override func touchesBegan(touches: Set<UITouch>,with event: UIEvent?) {
for touch in touches {
let location = touch.location
if (someNode.contains(location)) {
//This if statement isn't needed, but an example of how you can determine if a node was pressed
someFunctionToDoStuff(location)
}
}
}
or
override func touchesBegan(touches: Set<UITouch>,with event: UIEvent?) {
let touch = touches.first
let location = touch.location
if (someNode.contains(location)) {
//This if statement isn't needed, but an example of how you can determine if a node was pressed
someFunctionToDoStuff(location)
}
}
The first option may run multiple times depending on how many touches were determined. The second option will only pay attention to the first touch it notices and disregard the rest.
You can define this function however you like, just make sure to take the location of the touch if you need it:
func someFunctionToDoStuff(_ location: CGPoint) {
score += 1
// Then maybe move a node to where the press was located
ball.position = location
}
You can use touchesBegan, touchesMoved, and touchesEnded as functions.
Check out Ray Wenderlich's SpriteKit tutorials, you will learn so much! https://www.raywenderlich.com/145318/spritekit-swift-3-tutorial-beginners

Trying to display a resume button after pausing the game scene on SpriteKit

I’m working with Xcode 8 and Swift 3, trying to display a “Resume Game” button on my scene after pausing the game but since everything stops, I have not found a way to properly do so. I followed Mike's advice on this page “Tap To Resume” Pause text SpriteKit, but that didn't help.
When my pause button is tapped, it runs the pause function with the following code:
func pauseGame() {
self.isPaused = true
self.physicsWorld.speed = 0
self.speed = 0.0
self.scene?.view?.isPaused = true
}
It may seem a little overkill but that works. If I remove
self.scene?.view?.isPaused = true
from my pause function, I'm able to display the tap to Resume Button but I can still interact with some of the SpriteKit nodes in the scene. I'm working on a space shooter game so the user can still move the spceship and tap to fire although the bullets don't travel until I resume the scene.
I thought about adding a boolean "true" to the pause function and adding an IF statement to the firing and the moving above but that seems to me like complicating things a bit.
Any suggestions on how I can display the Resume button when pausing the scene?
You can't interact with any nodes on your scene because you've paused it entirely, which pauses all its children, which is everything on the scene. To avoid this, pause only certain SKNodes (layers).
Add certain nodes to different SKNodes so instead of pausing the entire scene, you can only pause the the layer (SKNode) that you wish to pause (gameLayer). It would look something like this:
Initialize the nodes
let gameLayer = SKNode()
let pauseLayer = SKNode()
Now when you want to add a child to the scene, instead add it to the layer that you want it to be a part of:
Add child nodes to the main layers
gameLayer.addChild(gameSceneNode)
pauseLayer.addChild(resumeButton)
Don't forget to add the layers to the scene too
Add the layers to the scene
addChild(gameLayer)
addChild(pauseLayer)
To pause a layer write this:
gameLayer.isPaused = true
Note that in this example, all the nodes on the gameLayer will be paused, however everything on the pauseLayer will not.
Your complete example might look something like this:
func pauseGame() {
gameLayer.isPaused = true
pauseLayer.isHidden = false
gameLayer.physicsWorld.speed = 0
gameLayer.speed = 0.0
}
func unpauseGame() {
gameLayer.isPaused = false
pauseLayer.isHidden = true
// Whatever else you need to undo
}
For my next game I'm sure I'll give Nik's suggestion a try. As for my issue above, it worked by using the code shown below. The additional code is to dismiss the pause button as well. Now that this works I can maybe add a fadeIn and fadeOut actions to both buttons.
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let pointOfTouch = touch.location(in: self)
let nodeUserTapped = atPoint(pointOfTouch)
if nodeUserTapped.name == "PauseButton" {
if (self.isPaused == false) {
pauseGame()
}
}
if nodeUserTapped.name == "ResumeButton" {
if (self.isPaused == true) {
resumeGame()
}
}
}
}
// MARK: - pauseGame
func pauseGame() {
self.isPaused = true
currentGameState = gameState.pauseGame
self.physicsWorld.speed = 0
self.speed = 0.0
if (backgroundMusicIsOn == true) {
backingAudio.stop()
}
if resumeButton.isHidden == true {
resumeButton.isHidden = false
}
if pauseButton.isHidden == false {
pauseButton.isHidden = true
}
}
// MARK: - resumeGame
func resumeGame() {
self.isPaused = false
currentGameState = gameState.inGame
self.physicsWorld.speed = 1
self.speed = 1.0
if (backgroundMusicIsOn == true) {
backingAudio.play()
}
if resumeButton.isHidden == false {
resumeButton.isHidden = true
}
if pauseButton.isHidden == true {
pauseButton.isHidden = false
}
}

How to add score when changing a sprite from hidden to unhidden

How do I make it so when a sprite is originally hidden is unhidden when a button is pressed and when something touches that sprite then it adds +1 to the score?
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
location = touches.first!.location(in: self)
if Top3.contains(location) && Top3.isHidden == true {
Top3.isHidden = false;
}
}
In the same line where Top3.isHidden = false you would have to have a variable to your score and just do score = score + 1.
if Top3.contains(location) && Top3.isHidden == true {
Top3.isHidden = false;
myGlobalScoreVariable = myGlobalScoreVariable + 1
}
Of course it would not be called "myGoalScoreVariable" but whatever you've set your score variable to be.
Unless I'm misunderstanding your question and you actually mean, how to do it once its been unHidden in which case you would just need another if statement when isHidden == false.
if Top3.contains(location) && Top3.isHidden == false {
myGlobalScoreVariable = myGlobalScoreVariable + 1
}

how to appoint a UItapGesture recognizer to a CGpoint for a button

I am creating a game ad i am having a hard time creating a jump button. I have created the jump up and fall down SKaction sequence which works perfect here is how it works.
func JumpArrow () {
self.addChild(jumpArrow)
jumpArrow.position = CGPointMake(60, 145)
jumpArrow.xScale = 1
jumpArrow.yScale = 1
}
func heroJumpMovement () {
let heroJumpAction = SKAction.moveToY(hero.position.y + 85,
duration: 0.5)
let heroFallAction = SKAction.moveToY(hero.position.y , duration:
0.5)
let jumpWait:SKAction = SKAction.waitForDuration(CFTimeInterval(1))
let heroMovementSequence:SKAction =
SKAction.sequence([heroJumpAction, heroFallAction ,jumpWait])
hero.runAction(heroMovementSequence)
}
override func touchesBegan(touches: Set<UITouch>, withEvent
event:UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
let node = nodeAtPoint(location)
if node == jumpArrow {
heroJumpMovement()
}
however, I have a problem. if you quickly tap the button the player will fly off the screen. I hope that i can create a UItapGestureRecognizer and create a delay for the tap so you can't tap the button 2-4 times per second you will only be able to tap it once. If this is the wrong way to go about this please tell me
Adding a delay would be the wrong way to go about it.
Instead, in your touchesBegan function, before you call heroJumpMovement() you should check to see if your player is on the ground or not.
Another alternative would be to check if the last jump SKActionSequence has completed or not.
To do the above, you would have something like this (code not checked):
var canJump = true; // Variable will be true if we can jump
func JumpArrow () {
self.addChild(jumpArrow)
jumpArrow.position = CGPointMake(60, 145)
jumpArrow.xScale = 1
jumpArrow.yScale = 1
}
func heroJumpMovement () {
let heroJumpAction = SKAction.moveToY(hero.position.y + 85,
duration: 0.5)
let heroFallAction = SKAction.moveToY(hero.position.y , duration:
0.5)
let jumpWait:SKAction = SKAction.waitForDuration(CFTimeInterval(1))
let heroMovementSequence:SKAction =
SKAction.sequence([heroJumpAction, heroFallAction ,jumpWait])
canJump = false; // We are about to jump so set this to false
hero.runAction(heroMovementSequence, completion: {canJump = true;}) // Set the canJump variable back to true after we have landed
}
override func touchesBegan(touches: Set<UITouch>, withEvent
event:UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
let node = nodeAtPoint(location)
if node == jumpArrow {
if (canJump) { // Make sure we are allowed to jump
heroJumpMovement()
}
}
Notice the canJump variable.

Continue in level2

I created a game where I have more scene, scene 1 = 1 level. When you get to level 2. How to make so I continued there even after the games?
I know that it is done by NSUserDefaults. But I do not know how. Thank you
here is my code:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let touchLocation = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(touchLocation)
if(touchedNode.name == "checkbutton"){
if(inputText!.text == "motorka" || inputText!.text == "Motorka" || inputText!.text == "MOTORKA"){
runAction(SKAction.playSoundFileNamed("correct.wav", waitForCompletion: false))
correct.hidden = false
inputText!.hidden = true
check.hidden = true
let nextLevel = level2(size: size)
nextLevel.scaleMode = scaleMode
let transitionType = SKTransition.crossFadeWithDuration(1.4)
let wait = SKAction.waitForDuration(0.9)
let action = SKAction.runBlock {
view?.presentScene(nextLevel, transition: transitionType)
}
self.runAction(SKAction.sequence([wait, action]))
}
else {
unCorrect.hidden = false
runAction(SKAction.playSoundFileNamed("Wrong-answer-sound-effect.mp3", waitForCompletion: false))
delay(1.0) {
self.unCorrect.hidden = true
}
}
You have to figure out what data has to be transferred into your Level 2. Once you have that, you save the data you need in Level 1 (using NSUserDefaults as you already know) and then import the data into the beginning of Level 2.
In Level 1 let's stay you have an enemy with various properties like health, strength and weapon. One option you have is to save those 3 properties as strings, store them in an array (or dictionary) and save that in NSUserDefaults.
When you get to level 2, you read the NSUserDefaults, create your enemy and set the 3 properties.
There are MANY different ways of doing the above so my example is in no way the only method or the best method.