Slider value doesn't update the value unless it's moved - swift

I've been playing around with a basic betting app and I can't seem to figure out this problem.
The slider is a percentage of the bank unless it's moved at which point it will update the value based on the value. If it isn't moved and kept at the previous value it also uses the previous value.
For example:
My bank is 1000. I move the slider to 10% to bet 100. I win and now my bank is 1100.
I want to bet 10% again but it doesn't update to the new value which should be 110. It will keep the same value of 100 until the slider is moved? How can I fix it so that even if it doesn't move the value of 10% of the bank is true.
#IBAction func slider(_ sender: UISlider)
{
betAmount.text = String(format: "%.0f%%",sender.value)
wager = Int(sender.value) * bank / 100
}
#IBAction func flipCoin(_ sender: UIButton)
{
let number = [1,2]
winnings = wager + ((wager * 90) / 100)
if let coin = coins.randomElement()
{
if(coin == 1 && wager <= bank && wager > 0)
{
youBet.text = ("$\(wager)")
bank += winnings
bankLabel.text = ("$\(bank)")
}
else if(coin == 2 && wager <= bank && wager > 0)
{
youBet.text = ("$\(wager)")
bank -= wager
bankLabel.text = ("$\(bank)")
}
}
}

The easiest way is to do the following:-
If you don't already have an IBOutlet for the UISlider, create one (e.g. called sliderOutlet.
Extract the contents of the slider func to a new private function, called (e.g.) updateWager(). Reference sliderOutlet instead of sender.
Change the slider func to call your new function.
Also call this function from the end of flipCoin (i.e. after the bank has been updated).
This will make the wager calculation occur automatically when the flip is complete. You can also call the new function should any other event (now or in the future) update the bank.
I.e.:
#IBOutlet weak var sliderOutlet: UISlider! // Create this from the storyboard
func updateWager()
{
betAmount.text = String(format: "%.0f%%", sliderOutlet.value)
wager = Int(sliderOutlet.value) * bank / 100.0
}
#IBAction func slider(_ sender: UISlider)
{
updateWager()
}
#IBAction func flipCoin(_ sender: UIButton)
{
let number = [1,2]
winnings = wager + ((wager * 90) / 100)
if let coin = coins.randomElement()
{
if(coin == 1 && wager <= bank && wager > 0)
{
youBet.text = ("$\(wager)")
bank += winnings
bankLabel.text = ("$\(bank)")
}
else if(coin == 2 && wager <= bank && wager > 0)
{
youBet.text = ("$\(wager)")
bank -= wager
bankLabel.text = ("$\(bank)")
}
updateWager()
}
}

Related

CoreMotion - Recognizing a jump (Swift)

I'm pretty new to CoreMotion but I'm trying to build an app that uses CoreMotion to see if a user is jumping up and down (think skipping rope). It also need to allow the user to be able to hold their phone (no iWatch) however they want (landscape, portrait, strange tilted way, etc.) and still be able to tell if they are traveling up and down regardless. I guess it's a bit like "throw your phone" app, but without measuring the distance.
So I'm using userAcceleration and gravity to see which way is "down" by checking the value of the different gravity axis and it works okey, but it feels like a clumsy way of going about it.
Is there a better way of doing what I'm doing? Basically all the app currently needs is to be able to tell if there is an acceleration perpendicular to the ground (regardless of how you'r holding the phone).
import UIKit
import CoreMotion
class TestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
startJump()
}
func startJump() {
if CMMotionManager.shared.isDeviceMotionAvailable {
CMMotionManager.shared.deviceMotionUpdateInterval = 0.01
CMMotionManager.shared.startDeviceMotionUpdates(to: OperationQueue.current!) { (data, error) in
guard let myData = data else {
return
}
var gravX = abs(myData.gravity.x)
var gravY = abs(myData.gravity.y)
var gravZ = abs(myData.gravity.z)
var userX = abs(myData.userAcceleration.x)
var userY = abs(myData.userAcceleration.y)
var userZ = abs(myData.userAcceleration.z)
if gravZ > 0.9 && userZ > 1 {
print("Jump; Z is up")
}
if gravX > 0.9 && userX > 1 {
print("Jump; X is up ")
}
if gravY > 0.9 && userY > 1 {
print("Jump; Y is up")
}
}
}
}
override func viewDidDisappear(_ animated: Bool) {
CMMotionManager.shared.stopDeviceMotionUpdates()
}
}

iOS Sprites Contact Detection

I'm working on a platform game where the character can climb ladders. Since the total length of each ladder varies, I have several ladders stacked on top of each other in SceneEditor. Upon entering a ladder, the contact delegate works fine and my code allows the character to move up the ladder. The problem I'm having is that as soon as the character moves off the first ladder segment, the didEnd method fires even though the character has entered the next ladder segment. I've gotten around it by given the ladder segments different category masks. Is this the only way to do it?
try overlapping the ladders by 1 rung, and then set a count whenever didBegin contact is fired increase the value by 1 whenever didEnd is called decrease the value by 1. at the end of the didEnd func check if onladderCount == 0. if it is, trigger whatever code is supposed to fire when the player is not on a ladder.
this assumes that you have a ladder class, and will have to put a property in the ladder class for onLadder to ensure that the += 1 isn't called multiple times.
var onladderCount: Int = 0
func didBegin(_ contact: SKPhysicsContact) {
let contactAName = contact.bodyA.node?.name
let contactBName = contact.bodyB.node?.name
if (contactAName == "ladder") || (contactBName == "ladder") {
let ladder: Ladder? = (contact.bodyA.categoryBitMask == PhysicsCategory. ladder ? (contact.bodyA.node as? Ladder) : (contact.bodyB.node as? Ladder))
if !ladder.onLadder {
ladder.onLadder = true
onladderCount += 1
}
}
}
func didEnd(_ contact: SKPhysicsContact) {
let contactAName = contact.bodyA.node?.name
let contactBName = contact.bodyB.node?.name
if (contactAName == "ladder") || (contactBName == "ladder") {
let ladder: Ladder? = (contact.bodyA.categoryBitMask == PhysicsCategory. ladder ? (contact.bodyA.node as? Ladder) : (contact.bodyB.node as? Ladder))
if ladder.onLadder {
ladder.onLadder = false
onladderCount -= 1
}
}
if onladderCount == 0 {
//trigger whatever code happens when they leave a ladder
}
}

swift call function multiple times inside update

I create a game via SpriteKit. in the game every few second a ball spawn to the screen (via function) and the player has to blow them up. now, I want to check the player level via his score so if the score is bigger than 10 the spawnBall function will be executed twice (so 2 ball will spawn on the screen) an so on. I tried to to it via the update fun (that will "read" the player score and depends on the score will call the spawnBall function). Unfortunately when I do it the screen is spawn with million (or so) balls in few seconds (as I said I want it to call the function every few seconds and increase the call while the score is X). I really don't have any idea how to do it.
here is my code:
override func update(_ currentTime: CFTimeInterval) {
if (self.score <= 10){
spawnBalls()
}
if (self.score > 10 && self.score <= 20){
spawnBalls()
spawnBalls()
}
if (self.score > 20){
spawnBalls()
spawnBalls()
spawnBalls()
}
if (self.subscore == 3) {
_ = randomBallColorToBlow()
self.subscore = 0
}
}
func spawnBalls() {
let wait = SKAction.wait(forDuration: 1)
let action = SKAction.run {
self.createBall()
}
run(SKAction.repeatForever((SKAction.sequence([wait, action]))))
}
how can I do it without using the update function??
you are calling spawn balls 60 times a second by calling it in your update func.
try just checking if a certain requirement is met to upgrade to a higher spawn rate in your update but keep the calls out of the update func.
private var upgradedToLevel2 = false
private var upgradedToLevel3 = false
//called somewhere probably in a start game func
spawnBalls(duration: 1.0)
override func update(_ currentTime: CFTimeInterval) {
if (self.score > 10 && self.score <= 20) && !upgradedToLevel2 {
//this prevents the if loop from running more than once
upgradedToLevel2 = true
self.removeAction(forKey: "spawn")
spawnBalls(duration: 0.5)
}
if (self.score > 20) && !upgradedToLevel3 {
//this prevents the if loop from running more than once
upgradedToLevel3 = true
spawnBalls(duration: 0.33)
}
}
func spawnBalls(duration: Double) {
let wait = SKAction.wait(forDuration: duration)
let action = SKAction.run { self.createBall() }
let repeater = SKAction.repeatForever(SKAction.sequence([wait, action]))
run(repeater, withKey: "spawn")
}
As stated, you are spawning your balls multiple times and need to break it up. I would recommend keeping track of level using an Int instead of a bool to be able to handle an "infinite" amount of level ups without making an "infinite" amount of boolean variables
private var nextLevel = 0 //Level 0 allows us to spawn a ball on startup, so no need to call spawnBalls anywhere else
override func update(_ currentTime: CFTimeInterval) {
if (self.score > 10 * nextLevel){
self.removeAction(forKey: "spawn") //this prevents the if loop from running more than once
nextLevel += 1
spawnBalls(count:nextLevel,forDuration:1) //You can change the 1 here if you want to spawn balls at a faster speed, I would recommend a variable that uses nextLevel in a forumula
}
}
func spawnBalls(count:Int, forDuration duration:TimeInterval) {
let range = 0..<count
let wait = SKAction.wait(forDuration: duration)
let action = SKAction.run {range.forEach{self.createBall()}}
let repeater = SKAction.repeatForever(SKAction.sequence([wait, action]))
removeAction(forKey:"spawn")
run(repeater, withKey: "spawn")
}

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

Can't increment variable in Swift - not throwing any errors

(I'm very new to programming, so layman-friendly answers are certainly appreciated)
I'm working on a game played with two teams. I have a button that does a check for which team is up and then updates that team's score. The code runs with no errors but the score doesn't update when the button is pressed.
In my model file I declare
var teamOneScore = 0
var teamTwoScore = 0
var teamCounter = 2
In the view controller I have
#IBAction func buttonPressed(sender: AnyObject) {
if timer.valid && teamCounter % 2 == 0 {
++teamOneScore
} else if timer.valid && teamCounter % 2 != 0 {
++teamTwoScore
}
}
In viewDidLoad
if teamCounter % 2 == 0 {
scoreLabel.text = "Score: \(teamOneScore)"
} else {
scoreLabel.text = "Score: \(teamTwoScore)"
}
When the view loads, scoreLabel correctly displays 0, but when I press the button, the displayed score doesn't go up. The timer and teamCounter checks are working fine everywhere else in the code and I have another button that increments teamCounter (which is also stored as an int in the model) with no problems. So all the separate components of buttonPressed seem to be working fine and I don't have any errors to go on. I'm stumped.
You have to move the text setting in an extra method. Right now the text only gets set in viewDidLoad - that function will not be triggered more than once however.
change your viewDidLoad to something like
updateUI()
add a new function
func updateUI() {
if teamCounter % 2 == 0 {
scoreLabel.text = "Score: \(teamOneScore)"
} else {
scoreLabel.text = "Score: \(teamTwoScore)"
}
}
and call that method as the last thing in the button action:
#IBAction func buttonPressed(sender: AnyObject) {
if timer.valid && teamCounter % 2 == 0 {
++teamOneScore
} else if timer.valid && teamCounter % 2 != 0 {
++teamTwoScore
}
updateUI()
}
When you create the scoreLabel in viewDidLoad, you assign it a text value of "Score: \(teamOneScore)" which is great. However, when you increment the teamOneScore variable, the actual UILabel does not know to change its text. It assumes you wanted to display Score: 0. Even if a variable changed its value, that label has already been created and will continue to display whatever text was initialized.
What you need to do in your buttonPressed function is add
scoreLabel.text = "Score: \(teamOneScore)" or
scoreLabel.text = "Score: \(teamOneScore)" if it was team 2 that scored.
after you increment the score. This is what allows the label's text to actually change.