Swift NSUserDefaults unexpectedly found nil while unwrapping an Optional value - ios

//Help Please This Code won't work, getting this as feedback from Xcode.
fatal error: unexpectedly found nil while unwrapping an Optional value
var savedScore = NSUserDefaults.standardUserDefaults().objectForKey("HighestScore") as! Int
import SpriteKit
class GameScene: SKScene {
var highestScore:Int = 2
var score = Int()
override func didMoveToView(view: SKView) {
/* Setup your scene here */
//To save highest score
//To get the saved score
var savedScore = NSUserDefaults.standardUserDefaults().objectForKey("HighestScore") as! Int
print(savedScore)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
score += 1
print("Score - \(score)")
if score > highestScore
{
highestScore = score
NSUserDefaults.standardUserDefaults().setObject(highestScore, forKey:"HighestScore")
NSUserDefaults.standardUserDefaults().synchronize()
print("Beat")
}
else {
print("Not Beat")
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}

You are calling the userDefaults function before it ever saves. You need to check if it exists. First you can make it easier on yourself and just save it as an integer. Then you need to check to see if there is a value.
NSUserDefaults.standardUserDefaults().setInteger(highestScore, forKey: "HighestScore")
//IntegerForKey will never return nil. This will return 0 if there is no value.
let savedScore = NSUserDefaults.standardUserDefaults().integerForKey("HighestScore")
print(savedScore)

NSUserDefaults.standardUserDefaults is a Dictionary Object. If you try to read a value that has not set value before, you will get a nil. You could check if the value for key exist
let savedScore = NSUserDefaults.standardUserDefaults.integerForKey("HighestScore") ?? 0
Then if value for HighestScore exist, you will get the correct value, if not, you will set 0 as default value for savedScore.

Thank you guys I got it yesterday but didn't find time to get on here and post it but here is the code that works. Thank You Guys again that helped!
class GameScene: SKScene {
var score = Int()
var highScore = Int()
override func didMoveToView(view: SKView) {
/* Setup your scene here */
//IntegerForKey will never return nil. This will return 0 if there is no value.
highScore = NSUserDefaults.standardUserDefaults().integerForKey("HighestScore")
print("highScore - \(highScore)")
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
score += 1
print("Score - \(score)")
if score > highScore
{
highScore = score
NSUserDefaults.standardUserDefaults().setObject(highScore, forKey:"HighestScore")
NSUserDefaults.standardUserDefaults().synchronize()
print("Beat")
}
else {
print("Not Beat")
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}

Related

Detect when rotation has completed and move to new Scene

I am trying to implement moving to another scene when a wheel stops rotating. The code I have is shown below. I cannot figure out how to detect when the velocity has reached 0.0?
import SpriteKit
import GameplayKit
class GameplayScene: SKScene {
var player: Player?;
override func didMove(to view: SKView) {
player = self.childNode(withName: "spinner") as! Player?;
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if atPoint(location).name == "play_button" {
spin()
}
}
}
func spin () {
let random = GKRandomDistribution(lowestValue: 20, highestValue: 90)
let r = random.nextInt()
player?.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(self.frame.width))
player?.physicsBody?.affectedByGravity = false
player?.physicsBody?.isDynamic = true
player?.physicsBody?.allowsRotation = true
player?.physicsBody?.angularVelocity = CGFloat(r)
player?.physicsBody?.angularDamping = 1.0
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
}
So from here, I would like to execute the following when the wheel has stopped spinning:
let play_scene = Questions(fileNamed: "QuestionsScene")
play_scene?.scaleMode = .aspectFill
self.view?.presentScene(play_scene!, transition: SKTransition.doorsOpenVertical(withDuration: 1))
I have now edited the class and it looks as follows:
import SpriteKit
import GameplayKit
class GameplayScene: SKScene, SKSceneDelegate {
var player: Player?
override func didMove(to view: SKView) {
self.delegate = self
player = self.childNode(withName: "spinner") as! Player?;
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if atPoint(location).name == "play_button" {
spin()
}
}
}
func spin () {
let random = GKRandomDistribution(lowestValue: 20, highestValue: 90)
let r = random.nextInt()
player?.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(self.frame.width))
player?.physicsBody?.affectedByGravity = false
player?.physicsBody?.isDynamic = true
player?.physicsBody?.allowsRotation = true
player?.physicsBody?.pinned = true
player?.physicsBody?.angularVelocity = CGFloat(r)
player?.physicsBody?.angularDamping = 1.0
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func didSimulatePhysics() {
if ((player?.physicsBody?.angularVelocity)! <= CGFloat(0.01))
{
print("Got it")
}
}
}
Problem is I still receive an error on the if statement within didSimulatePhysics function. The error I receive is "Thread 1: EXC_BAD_INSTRUCTION...."
Your wheel's SKPhysicsBody has a built-in property, angularVelocity, that tells you how fast it's spinning. You're already using it when you set it to r to start the wheel spinning.
To watch angularVelocity you can use didSimulatePhysics(). It gets called once every frame, right after the physics calculations are done. That will look something like this:
func didSimulatePhysics() {
if wheelIsSpinning && angularVelocity != nil && angularVelocity! <= CGFloat(0.001) {
wheelIsSpinning = false
// wheel has stopped
// add your code here
}
}
Due to the vagaries of physics modeling, the angularVelocity might never be exactly zero. So instead we watch for it to be less than some arbitrary threshold, in this case 0.001.
You don't want it to execute every frame when the wheel isn't moving, so I added a property wheelIsSpinning to keep track. You'll need to add it as an instance property to GameplayScene, and set it to true in spin().

Is there a way in Sprite Kit to get coordinates of a finger that doesn't move at the moment?

To avoid unexpected game behaviour I'd like to know coordinates of all fingers on the touchscreen at any given moment. Is there a way of doing this?
For example get coordinates of the fist finger when second is taken off from the screen.
Thanks in advance!
Here's the solution based on trojanfoe's idea.
import SpriteKit
class GameScene : SKScene {
struct fingerTrackData {
var fingerID : Int // used to identify fingers in other part of the code
var lastLocation : CGPoint // location of each finger
}
// array that stores information about each finger coordinates
var fingerTracks : [fingerTrackData] = []
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
// getAvailableFingerID() returns available fingerID to avoid ID duplication
self.fingerTracks.append(fingerTrackData(fingerID: self.getAvailableFingerID(), lastLocation: location))
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
self.updateFingerTracks(location)
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
// deregistering unnecessary finger tracks
self.fingerTracks.removeAtIndex(self.getClosestFingerArrayIndexByLocation(location))
}
}
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
// touchesCancelled occurs e.g. when more than 5 fingers on iPhone is on the screen
// Deregistering all tracks here
self.fingerTracks.removeAll()
}
override func update(currentTime: NSTimeInterval) {
// to see in console what is actually happening in array
if fingerTracks.count > 0 {
for fingerTrack in self.fingerTracks {
print("Finger: \(fingerTrack.fingerID) Location: \(fingerTrack.lastLocation)")
}
} else {
print("---")
}
}
/*
* Below are some helper functions for convenience
*/
func getClosestFingerArrayIndexByLocation (location: CGPoint) -> Int {
var fingerTrackKeyToUpdate : Int = 0
var shortestDistance : CGFloat = 100500 // some big number that is much bigger than screen size
for (fingerTrackKey, fingerTrack) in self.fingerTracks.enumerate() {
// calculating distance from previous record for each finger
let calculatedDistance = sqrt(pow(fingerTrack.lastLocation.x - location.x,2) + pow(fingerTrack.lastLocation.y - location.y,2))
// shortest one gives us finger ID
if calculatedDistance < shortestDistance {
shortestDistance = calculatedDistance
fingerTrackKeyToUpdate = fingerTrackKey
}
}
return fingerTrackKeyToUpdate
}
func updateFingerTracks(location: CGPoint) {
let closestFingerArrayIndex = self.getClosestFingerArrayIndexByLocation(location)
let fingerIDToUpdate = self.fingerTracks[closestFingerArrayIndex].fingerID
self.fingerTracks[closestFingerArrayIndex] = fingerTrackData(fingerID: fingerIDToUpdate, lastLocation: location)
}
func getClosestFingerIdByLocation(location: CGPoint) -> Int {
return self.fingerTracks[self.getClosestFingerArrayIndexByLocation(location)].fingerID
}
func getAvailableFingerID() -> Int {
var availableFingerID : Int = 0
if self.fingerTracks.count > 0 {
var checkAgain = true
while checkAgain {
checkAgain = false
for fingerTrack in self.fingerTracks {
if availableFingerID == fingerTrack.fingerID {
checkAgain = true
availableFingerID++
}
}
}
}
return availableFingerID
}
}
Thanks all for your assistance!
class GameScene : SKScene {
// stores all touches at any moment
var touchTracks : [UITouch] = []
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
self.touchTracks.append(touch)
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
var touchIndexToRemove : Int?
for i in 0..<self.touchTracks.count {
if self.touchTracks[i] == touch {
touchIndexToRemove = i
}
}
self.touchTracks.removeAtIndex(touchIndexToRemove!)
}
}
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
// touchesCancelled occurs e.g. when more than 5 fingers on iPhone is on the screen
// Deregistering all tracks here
self.touchTracks.removeAll()
}
}

Swift 2 SkScene Changing

I am new to both coding (swift) and making games. I am trying to get my game to switch SKScenes which I have created, for some reason when I try and switch scenes I just get a grey screen and I have no idea why. (It should be noted that the scene I am trying to transition from was already changed from the previous main scene. Below is the method I am using to change scenes. (I do not want any tranistion between the SKScenes)
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
if(self.nodeAtPoint(location).name != nil && self.nodeAtPoint(location).name != "enter") {
userSequence.append(Int(self.nodeAtPoint(location).name!)!)
print("userSequence:\(userSequence)")
}
if(self.nodeAtPoint(location).name == "enter"){
//compareSequences()
if PreviewNumbersGameScene.sharedInstance.TempGeneratedSequence == userSequence.reverse(){
print(PreviewNumbersGameScene.sharedInstance.TempGeneratedSequence)
print("good")
let Winscene = WinGameScene(fileNamed: "WinGameScene")
self.scene!.view?.presentScene(Winscene)
}
else{
print("Temp:\(PreviewNumbersGameScene.sharedInstance.TempGeneratedSequence)")
loose()
print("bad")
}
}
}
}
And this is the scene I want to change to: (Name is WinGameScene)
import SpriteKit
class WinGameScene: SKScene{
//MARK: - Variables
//MARK: - View
override func didMoveToView(view: SKView) {
///* Setup your scene here */
loadView()
}
func loadView(){
backgroundColor = SKColor.greenColor()
print("winSceneLoaded")
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let scene = PreviewNumbersGameScene(fileNamed: "PreviewNumbersGameScene")
self.view?.presentScene(scene)
}
}
}
In order to transition to a new scene, you should do something like this:
if let winGameScene = WinGameScene(fileNamed: "WinGameScene") {
self.view?.presentScene(winGameScene)
}else{
print("error")
}
The important thing here is that before you try to do this, you have to create a resource file called WinGameScene.sks. You can do that by going to: File->New->File...->Resource->SpriteKit Scene.

Ease out animation for SKSpriteNode

I'm moving a sprite by using the touchesMoved method. So far this is working fine, but the result looks a little bit unnatural. The sprite stops immediately, once the movement of my finger stops. A more natural impression would be, if the sprite continuous the movement a little time with an ease out function. Any idea how to implement this?
Here is my code:
import SpriteKit
class GameScene: SKScene {
var sprite: SKSpriteNode?
var xOrgPosition: CGFloat = 0.0
override func didMoveToView(view: SKView) {
sprite = SKSpriteNode(imageNamed:"Spaceship")
sprite!.xScale = 0.1
sprite!.yScale = 0.1
sprite!.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame));
self.addChild(sprite!)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let xTouchPosition = touch.locationInNode(self).x
if xOrgPosition != 0.0 {
// calculate the new position
sprite!.position = CGPoint(x: sprite!.position.x - (xOrgPosition - xTouchPosition), y: sprite!.position.y)
}
// Store the current touch position to calculate the delta in the next iteration
xOrgPosition = xTouchPosition
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
// Reset value for the next swipe gesture
xOrgPosition = 0
}
}
Update:
I've made a short sample project, based on the answers from hamobi
Tutorial
Git repository
i think this might be what youre going for. let me know if you have any questions :)
import SpriteKit
class GameScene: SKScene {
var sprite: SKSpriteNode?
var xOrgPosition: CGFloat?
override func didMoveToView(view: SKView) {
sprite = SKSpriteNode(imageNamed:"Spaceship")
sprite!.xScale = 0.1
sprite!.yScale = 0.1
sprite!.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame));
self.addChild(sprite!)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
xOrgPosition = touch.locationInNode(self).x
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
xOrgPosition = nil
}
override func update(currentTime: NSTimeInterval) {
if let xPos = xOrgPosition {
sprite!.position.x += (xPos - sprite!.position.x) * 0.1
}
}
}

Have a Spritekit display a Integer thats stored in a Variable

So I'm having difficulty trying to do something that should be easy, clearly I don't see whats wrong.
This is the code:
import SpriteKit
var money = "0"
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
let moneyLabel = SKLabelNode(fontNamed:"Times New Roman")
moneyLabel.text = money;
moneyLabel.fontSize = 14;
moneyLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame));
self.addChild(moneyLabel)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in touches {
money + 1
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
What this is suppose to do is have a label display a Variable, and when the user touches the screen the variable changes by 1, going up by 1 each time its pressed. The Label is suppose to change with the variable.
How do I make this work?
The problem
You are simply adding 1 to money
money + 1
This code:
does not change the money property
does not change the text in you moneyLabel
is illegal since you cannot sum a String and an Int
Solution
This code should do the job
import SpriteKit
class GameScene: SKScene {
private var money = 0 {
didSet {
self.moneyLabel?.text = money.description
}
}
private var moneyLabel : SKLabelNode?
override func didMoveToView(view: SKView) {
let moneyLabel = SKLabelNode(fontNamed:"Times New Roman")
moneyLabel.text = money.description
moneyLabel.fontSize = 14
moneyLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
self.addChild(moneyLabel)
self.moneyLabel = moneyLabel
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
money += touches.count
}
}
Explaination
1)
As you can se I added an observer to the money property (by the way I made it an Int property, not a String as in your code!).
didSet {
self.moneyLabel?.text = money.description
}
Thanks to the observer, each time the money changes, the moneyLabel node is retrieved and it text gets updated.
2)
At the end of didMoveToView I am saving moneyLabel into an instance property
self.moneyLabel = moneyLabel
so I can easily retrieve (we have seen this in the previous point)
3)
Finally in touchesBegan I simply increase the money property by the number of received touches.
money += touches.count
Thanks to the observer, each change to the money property does trigger an updated to the text inside the moneyLabel node.
Hope this helps.
There are three problems here:
money should be an Int, so you can add to it.
You haven't actually updated money to a new value.
You'll need to update the label in addition to money.
Take a look at the changes below; each comment explains why a change was necessary:
import SpriteKit
class GameScene: SKScene {
var money = 0 //now an Int, so we can add to it later
let moneyLabel = SKLabelNode(fontNamed:"Times New Roman") //keep this around so we can update it later
override func didMoveToView(view: SKView) {
moneyLabel.text = String(money) //convert money to a String so we can set the text property to it
moneyLabel.fontSize = 14
moneyLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
self.addChild(moneyLabel)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
money = money + 1 //update money to itself + 1
moneyLabel.text = String(money) //update the label, too
}
}
}

Resources