Game Scene cannot be constructed because it has not accessible initializers - swift

Xcode is dropping an error and I dont know how to solve it. Maybe someone can give me a hand or a good hint?
Here is my code that is working within my Game Scene:
var currentLevel: Int = 1
var platforms: PlatformsNode!
var platformNode1: PlatformNode!
var platformNode2: PlatformNode!
var platformNode3: PlatformNode!
var platformArray = [SKNode]()
let passenger = SKSpriteNode(imageNamed: "passenger")
var passengerNumber:Int = 1
var startPlatform = [3, 3, 2, 1, 2]
var destinationPlatform = [1, 1, 3, 2, 1]
// Level selection
class func level(levelNum: Int) -> GameScene? {
let scene = GameScene(fileNamed: "Level\(levelNum)")! // <- compiler error
scene.currentLevel = levelNum
scene.scaleMode = .AspectFill
return scene
}
As soon as I want to replace
let passenger = SKSpriteNode(imageNamed: "passenger")
with let passenger: PassengerNode! the compiler drops an compiler error "Game Scene cannot be constructed because it has not accessible initializers".
And this error just shows up, when I change the way I want to declare let passenger.
The reason I want to change it, is that I want to replace it with a class, so that the passenger
a. can have a different type with a different texture (this can may be solved differently)
b. can be removed from the parent and later be added again - with hopefully no error.
You got any ideas? I am really stuck somehow :-?

declaring passenger with only its type and no initial value requires that you add an init() to your class because the compiler does not know what initial value to give it when the object is created.
You can either create an init() and set a value to passenger or delare it as
let passenger: PassengerNode! = nil
(this assumes that your code does not reference the variable before making sure to put something in it or checks for nil/optional before using it).

Related

SpriteKit, copying a physics body does NOT copy the settings?

It seems almost incredible but,
let pb = model.physicsBody?.copy() as! SKPhysicsBody
print("orig .. \(model.physicsBody!.linearDamping)")
print("pb .. \(pb.linearDamping)")
they are NOT the same. No really.
This is so bizarre that I must be doing something wrong. What am I doing wrong?
Other than just manually copying all the properties, and maintaining the code forever as that evolves, how to do this??
Here's a dupe-qualities function if anyone needs it, to save typing. (This of 2017 - of course it has to be maintained forever as Apple add qualities.)
extension SKSpriteNode
{
func dupe() -> Any {
// annoyingly, Apple does not provide a dupe function as you would use
// when spawning from a model.
let c = self.copy() as! SKSpriteNode
c.physicsBody = self.physicsBody!.copy() as? SKPhysicsBody
c.physicsBody?.affectedByGravity = self.physicsBody!.affectedByGravity
c.physicsBody?.allowsRotation = self.physicsBody!.allowsRotation
c.physicsBody?.isDynamic = self.physicsBody!.isDynamic
c.physicsBody?.pinned = self.physicsBody!.pinned
c.physicsBody?.mass = self.physicsBody!.mass
c.physicsBody?.density = self.physicsBody!.density
c.physicsBody?.friction = self.physicsBody!.friction
c.physicsBody?.restitution = self.physicsBody!.restitution
c.physicsBody?.linearDamping = self.physicsBody!.linearDamping
c.physicsBody?.angularDamping = self.physicsBody!.angularDamping
c.physicsBody?.categoryBitMask = self.physicsBody!.categoryBitMask
c.physicsBody?.collisionBitMask = self.physicsBody!.collisionBitMask
c.physicsBody?.fieldBitMask = self.physicsBody!.fieldBitMask
c.physicsBody?.contactTestBitMask = self.physicsBody!.contactTestBitMask
return c
}
}
(Just one man's opinion, I feel it's better to not just override the NSCopy, since, the whole thing is a touchy issue and it's probably better to simply be explicit for the sake of the next engineer. It's very common to "dupe" the qualities of a game object to another, so, this is fine.)
It has to do with SKPhysicsBody being a wrapper class to PKPhysicsBody
Essentially what is going on is when you create a copy of SKPhysicsBody, it creates a new instance of PKPhysicsBody, not a copy of it.
To get around this, you need to write an extension that fills in the values for you:
extension SKPhysicsBody
{
override open func copy() -> Any {
guard let body = super.copy() as? SKPhysicsBody else {fatalError("SKPhysicsBody.copy() failed")}
body.affectedbyGravity = affectedByGravity
body.allowsRotation= allowsRotation
body.isDynamic= isDynamic
body.mass= mass
body.density = density
body.friction = friction
body.restitution = restitution
body.linearDamping = linearDamping
body.angularDamping = angularDamping
return body
}
}
Note, I typed this by hand, I do not have XCode available at this time to test if it does work.

updating an SKLabel to show the right integer

ok so first off I have an achievement Variable that is defined from a struct like so:
struct Achieve {
var node: SKSpriteNode?
var aName:String = ""
var aDes:String = ""
var aImage: String = "" {
didSet {
node?.texture = SKTexture(imageNamed: aImage)
}
}
var aAmount:Int = 0
var aRequired:Int = 0
var aStage:Int = 0
}
var Ach1 = Achieve(node: nil, aName: "Player", aDes: "Games Played", aImage: "locked", aAmount: 0, aRequired: 10, aStage: 1)
My problem is when I try and change the number of the aAmount property it isn't displayed on the SKLabel that displays the amount it just stays at 0 my sklabel is defined in a function like below:
func generateLabels(location: CGPoint, page:SKSpriteNode, text: String) -> SKLabelNode {
let node = SKLabelNode()
node.fontColor = UIColor.whiteColor()
node.fontName = "Helvetica"
node.position = location
node.fontSize = 15
node.text = text
page.addChild(node)
return node
}
func menu() {
_ = generateLabels(CGPointMake(0, -285), page:page1ScrollView, text: "\(Ach1.aAmount) / \(Ach1.aRequired)")
}
It seems to work if I make changes to aAmount when I stop running it and then change it and then run the game again. it also seems to make changes to the aAmount property during gameplay but it doesn't seem to update the label for some reason during the gameplay. Can someone please tell me why it won't update?
Also i'm updating the aAmount property like so:
Ach1.aAmount += 1
print("\(Ach1.Amount)")
In your Achieve struct, add a property called label:
var label: SKLabelNode?
And change the aAmount property to look something like this:
var aAmount: Int = 0 {
didSet {
label?.text = "\(aAmount) / \(aRequired)"
}
}
Now when you generate the labels, replace this:
_ = generateLabels(CGPointMake(0, -285), page:page1ScrollView,
text: "\(Ach1.aAmount) / \(Ach1.aRequired)")
with this
var label = _ = generateLabels(CGPointMake(0, -285), page:page1ScrollView,
text: "\(Ach1.aAmount) / \(Ach1.aRequired)")
Ach1.label = label
Now when you change the aAmount property of Ach1, the label's text should change as well.
Suggestions:
Judging from this and your last question about sprite node images not updating, I think your programming skills are not mature enough. In the long term, you should learn the basics first. Try playing around with UIKit and read more others' code. See how other people do things. You will definitely learn a lot. In the short term though, you shouldn't make each of all these properties binds to a node. That will make Achieve too dependent. What you should do is make Achieve a subclass of SKSpriteNode and add those other sprite nodes and label nodes as sub nodes of the Achieve node.
If I read your code, there isn't any part where you update your SKLabelNode.
If you call the function :
func menu() {
_ = generateLabels(CGPointMake(0, -285), page:page1ScrollView, text: "\(Ach1.aAmount) / \(Ach1.aRequired)")
}
for example to didMoveToView, it generate a label with your aAmount value when you call your scene (so 1 time only as didMoveToView works).
Instead, assuming that you call this menu function many times, it generate everytime a new label but there is no code written from you where you remove the previous label or labels added in past (see page.addChild(node)) and I don't understand why you want to return a label from your function if you don't use it, in fact you assign your result to _. Also you don't explain what is "page1ScrollView", are you using a scrollview?
Anyway, the best mode to update your label is simply to create 1 time only (for example on didMoveToView) and, everytime you want to update it, doing:
myNodeLabel.text = "\(Ach1.aAmount) / \(Ach1.aRequired)"
instead of re-create it everytime. I hope this helps to understand the dynamics that prevent your label to upgrade.
You have to reassign the text of the SKLabel. So when you call the function to change the number (or whatever it is), also run the code to make the label what you want it to be. My guess as to why it only works sometimes is that sometimes you call it in the update (or somewhere else that gets called repeatedly) and sometimes you call it somewhere like didMoveToView (only called once, so label doesn't get updated because of the way your code is).
Just whenever you update aAmount, add this code after it:
label.text = "\(aAmount)"

NSUserDefault GlobalData/Base Scene Multiple "Keys" Overall Total

I have multiple scenes that are stored in Global/BaseScene
Each SceneType: is stored as enum: set to an Integer similar to this present scene
First objective was to get the score to populate in each scene, there is seven of them. DONE thanks to stack overflow, and experimentation
I had to create default keys for each individual scene, to calculate the highScore, so I have seven unique "highScore" "keys" for each scene.
In the GameScene:
var highScoreA1: Int = 0
var score: Int = 0 {
didSet {
a1Score.text = "Score: \(score)"
}
}
//above called before override func didMoveToView(view: SKView) {
//Called in GameOver Method
let scoreDefault = NSUserDefaults.standardUserDefaults()
scoreDefault.setInteger(score, forKey: "score")
if (score > highScoreA1){
let highScoreA1Default = NSUserDefaults.standardUserDefaults()
highScoreA1Default.setInteger(score, forKey: "highScoreA1")
//highscoreA1Default.synchronize()
There are six more keys similar to this.. My objective is to populate a "totalScoreKey" in two different scenes a Hud Scene and another scene (possibly game over scene)
I was thinking a function to add these keys together to populate the total score.
Taking into consideration all these scenes are subclasses (of Global BaseScene, and each scene has sub classes (for the nodes operation, probably not relevant yet thought it might be useful)
I have tried: Moving all score data into a Class and using NSCoding/NSObject the required init, and optional binding, became a serious pain, and honestly I am trying to keep things simple for version one.
import Foundation
class GameState {
var score:Int = 0
var highScore:Int()
var totalScore:Int()
class var sharedInstance: GameState {
struct Singleton {
static let instance = GameState()
}
return Singleton.instance
}
}
init() {
// Init
score.type = 0
highScore = 0
totalScore = 0
// Load game state
let defaults = NSUserDefaults.standardUserDefaults()
highScore = defaults.integerForKey("highScore")
}
func saveState() {
// Update highScore if the current score is greater
highScore = max(score, highScore)
// Store in user defaults
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setInteger(highScore, forKey: "highScore")
defaults.setInteger(totalScore, forKey: "totalScore")
NSUserDefaults.standardUserDefaults().synchronize()
}
Various functions that have not worked they all default to zero, that is until I figure out how to retrieve data properly.
let total score = "totalScoreKey"
similar to this post exactly like this post actually except I had to do different configurations because of my on personal set up..
total Score example I tried to implement
outside of class and referring to that in the scene I needed to populate that data. NO Go defaults to zero.
How do I simply add the value of those keys together? For which I can display similar to the other scenes, I already have implemented.
later on down the road I may want to assign a key chain value, right now I am just trying to get it show up for posting in GameCenter. (which also has key "GameCenterHighScore")
Setting them all to the same key "highScore" does not work.... just to be clear, I tried multiple times. Thanks in advance.
EDIT
if I try to add all the defaults together to get the total, it throws the following error:
Expression was too complex to be solved in reasonable time; consider
breaking up the expression into distinct sub-expressions
[Swift compound arithmetic operation ERROR3

assigned enum value doesn't change

I have a Player class which has an enum for changing the Player type. See init below.
init(playerType: PlayerType) {
self.playerType = playerType
spriteTexture = SKTexture(imageNamed: playerType.simpleDescription())
sprite = SKSpriteNode(texture: spriteTexture)
sprite.name = playerCategoryName
sprite.zPosition = 10
}
In the GameScene I change some player properties using the following method, but the enum value for player.playerType doesn't change when assigned. What is cause and how can I correct this?
func changePlayer (newPlayerType:Player.PlayerType) {
player.spriteTexture = SKTexture(imageNamed: newPlayerType.simpleDescription())
let action = SKAction.setTexture(player.spriteTexture)
player.sprite.runAction(action)
player.playerType == newPlayerType //this doesn't seem to work, player.playerType remains unchanged every time
println("raw value of newPlayerType is \(newPlayerType.rawValue)")
println("raw value of player.playerType is \(player.playerType.rawValue)")
}
You cannot assign a different case to an existing enum; you can make an existing enum change its own case, but not by assigning into it.
You can replace an enum instance (with one case) by another enum instance (with a different case) of the same enum, but only if the enum is a var reference, not a let reference.
== is the equality operator.
You're looking for = for assignment (note, only one equals sign).
Check your enum reference it should be var not let

How do I get the screen dimensions before init()?

I'm making a small game in swift and want to make my player move across 3 fixed points on the screen.
I created an array to store the values in which I calculate in the init function, but the compiler doesn't agree with me not declaring the array before super.init(). Here's what I have
//properties
var player:SKSpriteNode = SKSpriteNode()
var playerPos = 1
var legalPositions: [CGFloat]
override init(size:CGSize)
{
super.init(size: size)
legalPositions[0] = self.frame.width/4
legalPositions[0] = self.frame.width/2
legalPositions[0] = self.frame.width/0.5
}
When I try to build it I get the error
GameScene.swift:31:9: Property 'self.legalPositions' not initialized at super.init call
Is there a way to initialize the property before calling the init function?
This is actually a good use case for Implicitly Unwrapped Optionals. Basically you'd define legalPositions as [CGFloat]! which will default it to nil. Then you can set it after your call to super.init(size: size).
Since it's implicitly unwrapped, you don't have to worry about unwrapping it with optional binding or forced unwrapping each time you use it. And, since you're always setting it in your init method, you don't have to worry about it being nil and causing a crash.
//properties
var player:SKSpriteNode = SKSpriteNode()
var playerPos = 1
var legalPositions: [CGFloat]!
override init(size:CGSize)
{
super.init(size: size)
legalPositions[0] = self.frame.width/4
legalPositions[0] = self.frame.width/2
legalPositions[0] = self.frame.width/0.5
}
Yes, you can use a dummy value that gets overwritten in the initializer:
var legalPositions: [CGFloat] = [0]
That way, the legalPositions array is initialized before you're calling the base class initializer. Swift's two-phase initialization requires this process in order to be able to guarantee that all properties are initialized after creating a new instance of a class.
The benefit of this approach is that there's no need for implicitly unwrapped optionals. They open up the possibility for nil values being set, which is not what you want.
Note that the first two assignments in your snippet are unnecessary since the third one will overwrite the value of the first item of the legalPositions array anyway.