How to request data from another scene in SpriteKit - ios

I have 2 SKScenes. The first scene has 2 buttons: a male player button and a female player button. The second scene has 2 hidden images: a male player image and a female player images.
If male player is selected in Scene1, I want to make the male player image visible in Scene2, but I'm having trouble passing the data between SKScenes. I'm using Sprite Kit and programming in Swift. Please advise.
Here is what I have tried so far:
// Scene1:
import SpriteKit
class Scene1: SKScene {
var malePlayer = SKSpriteNode()
var femalePlayer = SKSpriteNode()
var maleSelect = false
var femaleSelect = false
// in touches began..
if self.nodeAtPoint(location) == self.malePlayer {
maleSelect = true
// then i present scene2
} else if self.nodeAtPoint(location) == self.femalePlayer {
femaleSelect = true
//then i present scene2
// in scene 2:
class Scene2: SKScene {
var playerSelected: Scene1()
// did move to view
var boy = SKSpriteNode()
var girl = SKSpriteNode()
if self.playerSelected.maleSelect == true {
self.addChild(boy)
} else if self.playerSelected.femaleSelect == true {
self.addChild(girl)
}

There are several ways to do this. Here is one example that uses NSUserDefaults. One reason that makes this a good option is that the selection will persist even after the app is closed and can be reloaded when the app is next opened.
To save the selection, use the following code:
if self.nodeAtPoint(location) == self.malePlayer {
//save the selection in NSUserDefaults
NSUserDefaults.standardUserDefaults().setBool(true, forKey:"maleSelected")
//present next scene
} else if self.nodeAtPoint(location) == self.femalePlayer {
//save the selection
NSUserDefaults.standardUserDefaults().setBool(false, forKey:"maleSelected")
//present next scene
}
The value is now saved in local storage. When you need to access the selection in your other scenes, grab the value from NSUserDefaults:
So Scene2 might looking something like this:
class Scene2: SKScene {
var playerSelected: Scene1()
// did move to view
var boy = SKSpriteNode()
var girl = SKSpriteNode()
//grab the selection value
let maleSelected = NSUserDefaults.standardUserDefaults().boolForKey("maleSelected")
if maleSelected == true {
self.addChild(boy)
} else {
self.addChild(girl)
}
}

In scene 2 the code:
var playerSelected: Scene1()
Creates a new instance of the scene 1 class, it does not provide a link back to your original scene 1. To access scene 1's properties you need to pass a reference to it from scene 2.

Related

Pass Computed Values as Parameters in Swift

I am making a custom animation handler class with swift, and GameKit. In summary, I want to initialize an Animator (a custom class), on any SKNode that I want to animate. In principle, this animator has a list of animations (SKActions) that all have various "trigger States", which correspond with the various game states the game can be in. The Animator class then observers the global state variable, and if it changes, and matches the animations' trigger states, then it runs that animation on the target node.
I got this to work, however, I want to add just a bit more functionality to this, in the form of changing timings for the animation. An example of this would be something like a dog running, perhaps over a set period of time (where the state does not change), I want the dog to start running fast, and slow over time. The way I'm thinking of implementing something like this is to have a variable, speed, for example, that represents the speed of the animation, and have it slow over time. Then I would pass in a computed property (return the current value of the speed var)to the animator class, such that every time it calls the function for this specific animation, instead of having a fixed time interval, it would get the speed var and pass it in.
I'm having trouble because when I pass in a computed property, swift just takes its value when it's getting passed and holds onto that, instead of taking the entire getter with it. I'm wondering if there is a way I can pass a computed property with a getter, into a function/class. Here is the animator classes that I'm referring to, if you would like to see some code :)
class Animation {
let waitForCompletion: Bool
let repeating: Bool
let animation: SKAction
var running: Bool = false
let triggerState: State
var currentState: State {
return States.currentState
}
let target: SKNode
init(_ triggerState: State, animates animation: SKAction, for target: SKNode, waitForCompletion: Bool = true, repeating: Bool = true) {
self.triggerState = triggerState
self.target = target
//here I simply just pass in an animation, which allows for grouping animations if I want to do more than jsut SKAction.Animat(with:,timeInterval:)
self.waitForCompletion = waitForCompletion
self.repeating = repeating
self.animation = animation
}
func update(_ first: Bool) {
if (currentState == triggerState || first) && !running {
self.running = true
if !self.waitForCompletion { target.removeAllActions() }
if !target.hasActions() {
target.run(animation) {
self.running = false
self.target.removeAllActions()
if self.repeating { self.update(false) }
}
}
}
}
}
heres the animator class:
class Animator {
var itemObserver: AnyCancellable?
var animations: [Animation] = []
var state: State {
didSet {
update()
}
}
init(_ animations: [Animation]) {
self.animations = animations
self.state = States.currentState
//initialize a temporary value, such that the class has initialized self
itemObserver = States.$currentState.sink() { let _ = ($0) }
itemObserver = States.$currentState
.sink() { self.state = $0 }
}
func update() {
if let animation = animations.first(where: {$0.triggerState == state}) {
animation.update(true)
}
}
}
and a sample initialization in an SKNode class:
//this initializes the animtor with a series of animations. The animation being passed here is a group animation(an important capabality of this system, and in the scaleY, I pass a stretchVar, which is a computed property, that only passes its value in this example)
animator = Animator([
Animation(State.throwing, animates: SKAction.group([
SKAction.animate(with: ballAtlas, timePerFrame: 0.1),
SKAction.scaleY(to: stretchVar, duration: 0)
]), for: self)
])

Is it safe (or good programming style) to initialize a lazy var with nil vars?

Let me explain a little better what I mean since it's kinda tricky to understand.
I'm creating a prototype for a videogame. Every level inherits the main rules from a SKScene called SceneLogic:
class SceneLogic: SKScene, SKPhysicsContactDelegate {
// Set up the physics, the contacts, touches and so on...
}
class Level1: SceneLogic { }
class Level2: SceneLogic { }
Every level has its own .sks file which specifies the different icon to show in the HUD. In order to create a kind of "game engine" I thought to init every kind of graphics inside the SceneLogic class by lazy var and ignore them if the current level doesn't need it.
Let me explain with an example
class SceneLogic: SKScene, SKPhysicsContactDelegate {
// Text, available only for level 1
private lazy var textTopHUD = childNode(withName: "textTop") as! SKLabelNode
// Icon, available only for levels 3,4,5
private lazy var iconBottomHUD = childNode(withName: "iconBottom") as! SKSpriteNode
// Icon, available only for level 2
private lazy var iconLeftHUD = childNode(withName: "iconLeft") as! SKSpriteNode
func initGame(level: Int) {
switch mode {
case 1: // Level 1
textTopHUD.text = "Level 1"
case 2: // Level 2
iconLeftHUD.position = ....
}
}
}
The fact is: for level 1, iconBottomHUD is nil, for level 2 textTopHUD is nil... but the app doesn't crash since the var is lazy and it won't be called for some levels.
My question is: is it a good programming style? Is it safe to use lazy in this way?
The thing about lazy properties is that it defers the execution of the initialization code. So in your case it doesn't really matter since they are actually nil. So you defer the init of nothing basically. If i were you i would basically either make it as a computed propert as such:
private lazy var iconBottomHUD: SKSpriteNode = {
guard let node = childNode(withName: "iconBottom") as? SKSpriteNode else {
fatalError()
}
return node
}
Or make it as #JavierRivarola mentioned, make it protocol based.

Computer AI turns in a UIViewController

In a swift game using UIKit I am writing, a human player will interact with UIKit UIButtons, GUI elements to take actions.
In the game, the player will play against AI players.
But here's the thing; the human player presses buttons and interacts and the AI player does not.
Given a simple UIViewController;
class SampleViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buyBtnPressed(_ sender: UIButton) {
print ("pressed")
}
}
So what I'm trying to ascertain is, how does the AI player itself take actions and handling turns within the context of the current view controller?
I believe the best way to do this is that there should be a loop that will wait until all players have completed their respective turns.
But where does this loop go? In the view did load?
If so, won't it eat up memory, or potentially lead (if not careful) to an endless loop?
I'm finding it hard to ascertain how an AI player can take actions within the given context of a UIViewController considering GUI elements are for human interaction.
I don't mean the AI should be animating pressing buttons or interacting with the screen, I mean; I have a UIViewController, it has a view did load; what is the strategy of implementing AI turns and whether or not this should be be achieved in a "game loop" in the View did load or can this be achieved in another way?
My question is; given the context of a UIViewController; how can I code the handling of an AI player taking turns and can this be achieved with a loop or another strategy?
Many thanks
edit: Code is now added
I have written out a turn base manager using Swift playgrounds, and 2 examples one using a UIViewController and another is a loop.
code now follows;
import Foundation
import GameplayKit
class Player {
var name: String
public private(set) var isAI: Bool = false
public private(set) var turnOrder: Int = 0
init(name: String, isAI: Bool?) {
self.name = name
if let hasAI = isAI {
self.isAI = hasAI
}
}
func setTurnOrderIndex(number: Int) {
self.turnOrder = number
}
}
let p1 = Player.init(name: "Bob", isAI: false)
let p2 = Player.init(name: "Alex", isAI: true)
protocol TurnOrderManagerDelegate: NSObjectProtocol {
func turnOrderWasSet()
}
protocol TurnDelegate: class {
func turnIsCompleted()
}
class Turn: NSObject {
weak var player: Player?
weak var delegate: TurnDelegate?
public private(set) var completed: Bool = false {
didSet {
delegate?.turnIsCompleted()
}
}
init(player:Player, delegate: TurnDelegate) {
self.player = player
self.delegate = delegate
}
func setAsComplete() {
self.completed = true
}
}
class TurnOrderManager: NSObject, TurnOrderManagerDelegate, TurnDelegate {
static var instance = TurnOrderManager()
public private(set) var turnOrderIndex: Int = 0
public private(set) var turnOrder: [Turn] = [Turn]() {
didSet {
self.turnOrderWasSet()
}
}
var playerOnTurn: Player? {
let turnObj = self.turnOrder[turnOrderIndex]
return (turnObj.player)
}
var allTurnsCompleted: Bool {
let filtered = turnOrder.filter { (turnObj:Turn) -> Bool in
return (turnObj.completed)
}.count
return (filtered == turnOrder.count)
}
func setTurnOrder(players:[Player]) {
if (self.turnOrder.count == 0) {
for playerObj in players {
let turnObj = Turn.init(player: playerObj, delegate: self)
self.turnOrder.append(turnObj)
}
}
}
func turnOrderWasSet() {
for (index, turnObj) in self.turnOrder.enumerated() {
turnObj.player?.setTurnOrderIndex(number: index)
}
}
func next() {
if (turnOrderIndex < (self.turnOrder.count - 1)) {
turnOrderIndex += 1
}
else {
turnOrderIndex = 0
}
}
internal func turnIsCompleted() {
print (" - turnIsCompleted")
TurnOrderManager.instance.next()
}
}
class GameModel {
var turnOrderManager: TurnOrderManager
init() {
self.turnOrderManager = TurnOrderManager.instance
self.turnOrderManager.setTurnOrder(players:[p1,p2])
}
// other game model stuff [...]
}
class Phase1State : GKState {
var gameModel: GameModel!
init(gameModel:GameModel) {
super.init()
self.gameModel = gameModel
}
override func isValidNextState(_ stateClass: AnyClass) -> Bool
{
return false
}
override func didEnter(from previousState: GKState?) {
}
override func willExit(to nextState: GKState) {
}
// MARK: - Action
func buy() {
let index = self.gameModel.turnOrderManager.turnOrderIndex
let turn = self.gameModel.turnOrderManager.turnOrder[index]
turn.setAsComplete()
}
}
class SomeViewController: UIViewController
{
var gameModel: GameModel?
weak var gamePhase: Phase1State?
var isPhaseComplete: Bool {
return self.gameModel?.turnOrderManager.allTurnsCompleted ?? false
}
override func viewDidLoad() {
super.viewDidLoad()
self.gameModel = GameModel.init()
self.gamePhase = Phase1State.init(gameModel: self.gameModel!)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func buyButtonPressed() {
self.gamePhase?.buy()
self.finishTurn()
}
func finishTurn() {
guard let turnIndex = self.gameModel?.turnOrderManager.turnOrderIndex else {
return
}
guard let turn = self.gameModel?.turnOrderManager.turnOrder[turnIndex] else {
return
}
turn.setAsComplete()
if (self.isPhaseComplete)
{
print ("All turns are completed")
}
else {
//self.gameModel?.turnOrderManager.next()
self.gamePhase?.buy()
guard let playerOnTurn = self.gameModel?.turnOrderManager.playerOnTurn else {
print ("No player is on turn")
return
}
print ("\(playerOnTurn.name) is on turn")
if (playerOnTurn.isAI)
{
self.gamePhase?.buy()
self.finishTurn()
}
}
}
}
// EXAMPLE 1 -- first attempt ...
let vc = SomeViewController()
vc.viewDidLoad()
vc.buyButtonPressed()
// EXAMPLE 2 -- another attempt ....
let gameModel: GameModel = GameModel.init()
let gamePhase = Phase1State.init(gameModel: gameModel)
// player then takes an action
while (gameModel.turnOrderManager.allTurnsCompleted == false)
{
let turnIndex = gameModel.turnOrderManager.turnOrderIndex
let turnObj = gameModel.turnOrderManager.turnOrder[turnIndex]
guard let playerOnTurn = turnObj.player else {
break
}
print ("Player \(playerOnTurn.name) is on turn")
gamePhase.buy()
}
print ("All turns are completed, advance to next phase")
The issue is;
On the finishTurn, it only seems to work if it relies on the first player in the index is a human player. If its not, I have no idea how to make it fire the buy action.
On the second example, I use a loop; but I'm concerned using a loop could end up just looping forever.
My query is therefore clarifyed, how can I ensure my view controller will fire actions for AI players when they don't press buttons and loop through each player and execute their respective turn.
Many thanks
Further edit:
I do not know if I should have the while (gameModel.turnOrderManager.allTurnsCompleted == false) loop inside my viewDidLoad() to act like a game loop.
There is no need to specifically use Sprite Kit for this. SpriteKit would be more to do with how the UI is made rather than how the logic of the game works.
However, I would recommend looking at GameplayKit. It's a framework that contains lots of built in game logic tools. Specifically you want something like the GKDecisionTree. There are a few WWDC videos about it too. GameplayKit can be used with SpriteKit, UIKit, SSceneKit or any other game engine that you decide to use (or not).
Also, the question you're asking is a very general question about game development. Having the computer "decide" to do something is quite a complex subject.
I'd also suggest having a quick watch of this video from AI & Games and other videos from that channel.
It'll give you an idea of how to approach your problem.
Session 609 and 608 from WWDC 2015 and 2016 are prob good :D
Regarding updating the AI.
Your AI should be event driven. You have the concept of "turns" and "players". There is a point in the game at which it becomes a "player's" "turn". (Even at the very beginning of the game it is either Player 1 or Player 2's turn.
At this time there are two possibilities. Either the player is an AI, or the player is a person.
As soon as this happens there should be some sort of trigger (like a function call or something) that tells the player its turn has started.
If that player is the AI then you need to start some sort of calculation (maybe with a built in delay to make it realistic) so that it decides what to do.
Look, I'm not sure on what kind of game you're making, buy you should probably learn SpriteKit, specially SKActions. With that, you can easily control the flow of events from your game.
With that said, how is your AI implementation? Based on your code, I would begin with something like this:
class AI {
enum Decision {
case doSomething
case doAnotherThing
case dontDoAnything
}
public func decide() -> Decision {
// Decide which action the AI will take...
return .doSomething // This return is just a example!
}
public func act(on : Decision) {
// Do whatever the AI needs based on a decision...
}
}
Then, in your ViewController:
class SampleViewController: UIViewController {
var ai = AI()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buyBtnPressed(_ sender: UIButton) {
print ("pressed")
ai.act(on: ai.decide())
}
}
I hope that helps!

SpriteKit: Why does it wait one round for the score to update? (Swift)

In one scene, I have this code:
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setInteger(score, forKey: "scoreKey")
defaults.synchronize()
When the user makes contact with the gap, the code runs:
score++
If the user hits an obstacle, the GameOverScene takes over. Here's the code I have for the GameOverScene to move the score to scene to scene:
let defaults = NSUserDefaults.standardUserDefaults()
let score = defaults.integerForKey("scoreKey")
scoreLabel.text = "\(score)"
However, there's a bug in my code where the scoreLabel doesn't update its text. For example, let's say a user scores 1 and dies. When he dies, the gameOverScene will come up and say that the score was 1. Then, lets say the user clicks restart, and scores 5 and then dies. In the GameOverScene, the scoreLabel will say 1.
Please help me!
You don't need to really call synchronise anymore if you use iOS 8 or above. This is recommended by Apple, yet a lot of people still do it. So get rid of that line if you still use it.
My preferred way for game data is using a singleton GameData class with NSCoding. No need to add variables all over the place and much cleaner. I advise you reading this.
http://www.raywenderlich.com/63235/how-to-save-your-game-data-tutorial-part-1-of-2
You can also integrate iCloud key value storage that way, it is very easy as its similar to user defaults (see my example below)
Anyway to start you off here is a simple example of how this could look.
import Foundation
/// Keys
private struct Key {
static let encodedData = "encodedData"
static let highScore = "highScore"
}
class GameData: NSObject, NSCoding {
// MARK: - Static Properties
/// Shared instance
static let shared: GameData = {
if let decodedData = UserDefaults.standard.object(forKey: Key.encodedData) as? GameData {
return gameData
} else {
print("No data, creating new")
return GameData()
}
}
// MARK: - Properties
/// Defaults
private let localDefaults = UserDefaults.standard
private let iCloudDefaults = NSUbiquitousKeyValueStore.default()
/// Progress (not saved, no need for saving the score because of the highScore var. Still have here for global single access)
var score = 0
/// Progress (saved)
var highScore = 0
// MARK: - Init
private override init() {
super.init()
print("GameData init")
NotificationCenter.default.addObserver(self, selector: #selector(updateFromCloud), name: NSUbiquitousKeyValueStore.didChangeExternallyNotification, object: iCloudDefaults)
iCloudDefaults.synchronize()
}
// MARK: - Convenience Init
convenience required init?(coder decoder: NSCoder) {
self.init()
print("GameData convenience init")
// Progress
highScore = decoder.decodeInteger(forKey: Key.highScore)
}
// MARK: - Encode
func encodeWithCoder(encoder: NSCoder) {
// Progress
encoder.encodeInteger(highScore, forKey: Key.highScore)
// MARK: - User Methods
/// Save
func save() {
if score > highScore {
highScore = score
}
saveLocally()
saveToCloud()
}
// MARK: - Internal Methods
/// Save locally
private func saveLocally() {
let encodedData = NSKeyedArchiver.archivedDataWithRootObject(self)
localDefaults.setObject(encodedData, forKey: Key.encodedData)
}
/// Save to icloud
private func saveToCloud() {
print("Saving to iCloud")
// Highscores
if (highScore > iCloudDefaults.objectForKey(Key.highScore) as? Int ?? Int()) {
iCloudDefaults.setObject(highScore, forKey: Key.highScore)
}
/// Update from icloud
func updateFromCloud() {
print("Updating from iCloud")
// Highscores
highScore = max(highScore, iCloudDefaults.object(forKey: Key.highScore) as? Int ?? Int())
// Save
saveLocally()
}
Now in any scene if you want to use the score or saved highScore property you for example could say
GameData.shared.score++
or
scoreLabel.text = "\(GameData.shared.score)"
highScoreLabel.text = "\(GameData.shared.highScore)"
All your text labels will be updated immediately if you transition to a new scene or update the .text property. No need for userDefault sync etc.
Calling ...shared... will also initialise the helper. If you want to load gameData as soon as your game has launched you could call
GameData.shared
in your appDelegate or viewController. Its probably not really needed but your could still do it just to ensure the helper is initialised as soon as the game is launched.
If you want to save you call
GameData.shared.save()
Just remember to reset the score back to 0 in your gameScene.swift in the ViewDidLoad method.
GameData.shared.score = 0
This should make your life much easier. If you want to use iCloud all you have to do is go to your target and capabilities and turn on iCloud and tick keyValueStorage (not core data). Done.
Note:
To take it even a step further you could get the KeychainWrapper helper from JRendel on gitHub. Than instead of using NSUserDefaults to store the encoded gameData you use keychain, its dead simple to use.
You can save your score like below code:
NSUserDefaults.standardUserDefaults().setInteger(score, forKey: "scoreKey")
Then you can get your score was saved like below code:
if NSUserDefaults.standardUserDefaults().objectForKey("scoreKey") != nil
{
score = NSUserDefaults.standardUserDefaults().objectForKey("scoreKey") as! Int
}
scoreLabel.text = "\(score)"
To answer the question from your comment about using global structure...
According to docs :
Global variables are variables that are defined outside of any
function, method, closure, or type context.
Means you should define your struct right after the import statements at the top of the any file.
You make a structure like pointed from the link I've posted in comments, like this (I've placed the struct definition inside the GameScene.swift file):
struct GlobalData
{
static var gold = 0;
static var coins = 0;
static var lives = 0;
static var score = 0;
}
This struct will be available in GameOverScene as well. So, before transition, you will do something like :
GlobalData.score = 20
GlobalData.coins = 10
//etc.
And in your GameOverScene you will access it like this:
scoreNode.text = String(GlobalData.score)//scoreNode is SKLabelNode

Flow Control in Swift - AVAudioPlayer

I wrote an Iphone Swift app that plays a series of sounds in a random order using AVAudioPlayer-- for now a pop sound, a horn sound and a gong. It works when you hit the play button, except....
However, when I hit the stop button nothing happens- it doesn't respond (The stop does work if I have just one sound). I believe it is due to my flow control. If I did not put in the 'while soundPlayer.playing == true {}', the code would "fly" through the sound and not wait for it to finish.
How can I modify the code so the the sound plays to completion before going to the next sound? And also allow the stop button to be functional? See Code and screen shot below. (Actually Stack overflow will not allow me to post an image since I am so new)
//
// ViewController.swift
// InsultSchool2 Swift
//
// Created by JR on 12/3/14.
// Copyright (c) 2014 JR. All rights reserved.
//
import UIKit
import AVFoundation
//---------------------------------------------------
var soundPlayer:AVAudioPlayer = AVAudioPlayer()
// used to try to do some kind of flow control. May not be needed if better way is found.
var stopFlag:Bool = false
//-----------------------------------------------------
class ViewController: UIViewController {
#IBOutlet weak var valueSliderTime: UISlider!
#IBOutlet weak var valueLabelTime: UILabel!
#IBOutlet weak var valueStepperVolume: UIStepper!
#IBOutlet weak var valueLabelVolume: UILabel!
//------------------------------------------------------
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//Displays an initial Volume value in volume label on load
//Convert to an int. Otherwise you get a weird value when getting close to zero
//Multiply by 10 so the int works. Otherwise you would int'ing a number between 0.0 and 1.0.
// "\()" is a shorthand to convert whatever to a string
valueLabelVolume.text = "\(Int(valueStepperVolume.value * 10))"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//------------------------------------------------------
#IBAction func buttonStop(sender: UIBarButtonItem) {
NSLog("Enter Button Stop")
//?1 If a sound is not playing and stop is hit, then it crashes
//?2 the stop button does not work with the whlle loop below
soundPlayer.stop()
}
#IBAction func sliderTime(sender: UISlider) {
valueLabelTime.text = "\(Int(valueSliderTime.value))"
}
#IBAction func stepperVolume(sender: UIStepper) {
//Converted to an int. Otherwise you get a weird value when getting close to zero
valueLabelVolume.text = "\(Int(valueStepperVolume.value * 10))"
}
#IBAction func buttonPlay(sender: UIBarButtonItem) {
NSLog("Enter Button Start")
var soundArray:[String] = ["Sound0", "Sound1", "Sound2"]
// Randomizes a number to indicate which random sound to play in the array
/* Number is from 0 to number in the (). Don't add one or 0 will never play. Go one more than the numbers in the array. For example if you have 3 items in the array go to 3. THis will go from 0 to 2 (ie., 3 items)*/
// Reference----- var soundRandomNumber:Int = Int(arc4random_uniform(3))
var soundRandomNumber:Int
soundRandomNumber = Int(arc4random_uniform(3))
//Creates a random number to wait between sounds based on the slider value.
//arc4random requires a UInt32 (Unsigned is a positive number).
//_uniform is slightly more random than without the Uniform
//The documentation says to use Int otherwise.
println(Int(valueSliderTime.value))
NSLog("valueSliderTime.value")
var waitTimeRandomNumber = Int(arc4random_uniform(UInt32(valueSliderTime.value)))
println(waitTimeRandomNumber)
NSLog("waitTimeRandomNumber")
// Constructs a string with the random number for the URL
//var soundFile:String = soundArray[soundRandomNumber]
var soundFile:String
soundFile = soundArray[soundRandomNumber]
//Reference---- var soundURL = NSBundle.mainBundle().URLForResource(soundFile, withExtension:"mp3")
var soundURL:NSURL!
soundURL = NSBundle.mainBundle().URLForResource(soundFile, withExtension:"mp3")
soundPlayer = AVAudioPlayer(contentsOfURL: soundURL, error: nil)
//?3 How do I set up a loop or control that works until the stop button is pressed?
while stopFlag == false{
NSLog("inside while")
println(stopFlag)
//?4 Is the below correct? The actual volume does not seem to change though the .volume does
soundPlayer.volume = Float(valueStepperVolume.value)
println(Float(valueStepperVolume.value))
NSLog("Float(valueStepperVolume.value)")
println(soundPlayer.volume)
NSLog("soundPlayer.volume")
soundRandomNumber = Int(arc4random_uniform(3))
soundFile = soundArray[soundRandomNumber]
soundURL = NSBundle.mainBundle().URLForResource(soundFile, withExtension:"mp3")
soundPlayer = AVAudioPlayer(contentsOfURL: soundURL, error: nil)
soundPlayer.prepareToPlay()
soundPlayer.play()
//?5 How do I make the player not blow through the sound and wait until is finished
while soundPlayer.playing == true {
}
//?6 How can i make a random timer that waits for a random time before relooping?
waitTimeRandomNumber = Int(arc4random_uniform(UInt32(valueSliderTime.value)))
}// End of while loop
} //ends playButton IBAction
//?7 How to allow this app to play over other music in another player
}
use the repeat while statement instead to control the flow:
here is a link to apple's developers reference:
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html
repeat {
// move up or down for a snake or ladder
square += board[square]
// roll the dice
if ++diceRoll == 7 { diceRoll = 1 }
// move by the rolled amount
square += diceRoll
} while square < finalSquare
print("Game over!")

Resources