init var with swift using SpriteKit? - swift

How to correctly init with swift using SpriteKit?
This compiles but I get a error once the simulator starts running. "swift_reportUnimplementedInitializer"
import SpriteKit
class GameScene: SKScene {
var paddlePositionUpdate:CGPoint
init(paddlePositionUpdate:CGPoint){
self.paddlePositionUpdate = CGPoint.zeroPoint
super.init()
}
}

The designated inititaliser for SKScene is init(size) where size is the size of your scene. You have attempted to use init() which does not exist.
You can see the exact method signature if you Cmd+click on SKScene...
EDIT: Try something like
class GameScene: SKScene {
var paddlePositionUpdate: CGPoint
init(size: CGSize, paddlePosition: CGPoint = CGPoint.zeroPoint){
paddlePositionUpdate = paddlePosition
super.init(size)
}
}
A scene should be initialised with a size...

I had a similar problem when replacing a scene with another one using
let gameScene = GameScene(size: self.size)
self.view.presentScene(gameScene)
And I kept getting 2 errors about initialization:
// Error #1
'GameScene' is not constructible with '(size: #lvalue CGSize)'
// Error #2
Class 'GameScene' has no initializers
To fix error #1, just replace GameScene(size: self.size) with GameScene.sceneWithSize(self.size)
Error #2 seems to be affected by how the property is initialized. For example, if I wrote:
class GameScene: SKScene {
var test: CGPoint
// ...
}
Error #2 was still there. But if I replaced var test: CGPoint with var test: CGPoint! , var test = CGPoint(0,0) , or any other lines may give test a certain value, Error #2 was gone. I searched the guide book but found lots of classes using lines like var test: CGPoint. And here I got confused.
It maybe a common problem for Swift beginners and I hope my temporary solution helps.

Related

Use multiple classes to control a single SKScene [duplicate]

I'm trying to learn how to make a GameManager type class, and making individual classes for each of my GameScenes... probably the wrong thing to do, but for the sake of this question, please accept this as the way to do things.
My GameManager looks like this, having a reference to each of the scenes, that's static:
import SpriteKit
class GM {
static let scene2 = SecondScene()
static let scene3 = ThirdScene()
static let home = SKScene(fileNamed: "GameScene")
}
How do I create a SKScene programmatically, without size info, since they're in a subclass of SKScene and don't have any idea what the view size is, and I don't want them to need worry about this:
I'm doing this, but getting a EXC_BAD_Access at convenience override init()
class SecondScene: SKScene {
override init(size: CGSize){
super.init(size: size)
}
convenience override init(){
self.init()
self.backgroundColor = SKColor.red
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
}
}
As I mentioned your question is a bit vague but lets do some examples of what a GameManager class can be.
Before I start lets differentiate between calling this
let scene = StartScene(size: ...)
and this
let scene = SKScene(fileNamed: "StartScene")
The 1st method, with size, is when you create your scenes all in code and you are not using the xCode visual level editor.
The 2nd method is when you are using the Xcode level editor, so you would need to create a StartScene.sks file. Its that .sks file that it looks for in fileNamed.
Now for some game manager example, lets first imagine we have 3 SKScenes.
class StartScene: SKScene {
override func didMove(to view: SKView) { ... }
}
class GameScene: SKScene {
override func didMove(to view: SKView) { ... }
}
class GameOverScene: SKScene {
override func didMove(to view: SKView) { ... }
}
Lets say you want to transition from StartScene to GameScene, you would add this code in your StartScene at the correct spot e.g when the play button is pressed. Thats the simplest way to move from one SKScene to the next, directly from the SKScene itself.
// Code only, no xCode level editor
let gameScene = GameScene(size: CGSize(...))
let transition = SKTransition...
gameScene.scaleMode = .aspectFill
view?.presentScene(gameScene, transition: transition)
// With xCode level editor (returns an optional so needs if let
// This will need the GameScene.sks file with the correct custom class set up in the inspector
// Returns optional
if let gameScene = SKScene(fileNamed: "GameScene") {
let transition = SKTransition...
gameScene.scaleMode = .aspectFill
view?.presentScene(gameScene, transition: transition)
}
Now for some actual examples of GameManagers, Im sure you know about some of them already.
EXAMPLE 1
Lets say we want a scene loading manager. You approach with static methods will not work because a new instance of SKScene needs be created when you transition to one, otherwise stuff like enemies etc will not reset. Your approach with static methods means you would use the same instance every time and that is no good.
I personally use a protocol extension for this.
Create a new .swift file and call it SceneLoaderManager or something and add this code
enum SceneIdentifier: String {
case start = "StartScene"
case game = "GameScene"
case gameOver = "GameOverScene"
}
private let sceneSize = CGSize(width: ..., height: ...)
protocol SceneManager { }
extension SceneManager where Self: SKScene {
// No xCode level editor
func loadScene(withIdentifier identifier: SceneIdentifier) {
let scene: SKScene
switch identifier {
case .start:
scene = StartScene(size: sceneSize)
case .game:
scene = GameScene(size: sceneSize)
case .gameOver:
scene = GameOverScene(size: sceneSize)
}
let transition = SKTransition...\
scene.scaleMode = .aspectFill
view?.presentScene(scene, transition: transition)
}
// With xCode level editor
func loadScene(withIdentifier identifier: SceneIdentifier) {
guard let scene = SKScene(fileNamed: identifier.rawValue) else { return }
scene.scaleMode = .aspectFill
let transition = SKTransition...
view?.presentScene(scene, transition: transition)
}
}
Now in the 3 scenes conform to the protocol
class StartScene: SKScene, SceneManager { ... }
and call the load method like so, using 1 of the 3 enum cases as the scene identifier.
loadScene(withIdentifier: .game)
EXAMPLE 2
Lets make a game manager class for game data using the Singleton approach.
class GameData {
static let shared = GameData()
private init() { } // Private singleton init
var highscore = 0
func updateHighscore(forScore score: Int) {
guard score > highscore else { return }
highscore = score
save()
}
func save() {
// Some code to save the highscore property e.g UserDefaults or by archiving the whole GameData class
}
}
Now anywhere in your project you can say
GameData.shared.updateHighscore(forScore: SOMESCORE)
You tend to use Singleton for things where you only need 1 instance of the class. A good usage example for Singleton classes would be things such as helper classes for Game Center, InAppPurchases, GameData etc
EXAMPLE 3
Generic helper for storing some values you might need across all scenes. This uses static method approach similar to what you were trying to do. I like to use this for things such as game settings, to have them in a nice centralised spot.
class GameHelper {
static let enemySpawnTime: TimeInterval = 5
static let enemyBossHealth = 5
static let playerSpeed = ...
}
Use them like so in your scenes
... = GameHelper.playerSpeed
EXAMPLE 4
A class to manage SKSpriteNodes e.g enemies
class Enemy: SKSpriteNode {
var health = 5
init(imageNamed: String) {
let texture = SKTexture(imageNamed: imageNamed)
super.init(texture: texture, color: SKColor.clear, size: texture.size())
}
func reduceHealth(by amount: Int) {
health -= amount
}
}
Than in your scene you can create enemies using this helper class and call the methods and properties on it. This way you can add 10 enemies easily and individually manage their health etc. e.g
let enemy1 = Enemy(imageNamed: "Enemy1")
let enemy2 = Enemy(imageNamed: "Enemy2")
enemy1.reduceHealth(by: 3)
enemy2.reduceHealth(by: 1)
Its a massive answer but I hope this helps.

Assigning SCNScene to SCNView - found nil while unwrapping Optional value

Recently, I decided to apply my previous knowledge in C++ and Python to learning Swift. After which, I decided to see what I could do with the SceneKit framework. After hours of checking through the documentation, and consulting a tutorial, I have to wonder what's going wrong with my code:
class GameViewController: UIViewController {
var gameView:SCNView!
var gameScene:SCNScene!
var cameraNode:SCNNode!
override func viewDidLoad() {
super.viewDidLoad()
initScene()
initView()
initCamera()
}
func initView() {
//initialize the game view - this view holds everything else in the game!
gameView = self.view as! SCNView
//allow the camera to move to gestures - mainly for testing purposes
gameView.allowsCameraControl = true
//use default lighting while still practicing
gameView.autoenablesDefaultLighting = true
}
func initScene() {
//initialize the scene
gameScene = SCNScene()
//set the scen in the gameView object to the scene created by this function
gameView.scene = gameScene
}
func initCamera() {
//create a node that will become the camera
cameraNode = SCNNode()
//since a node can be any object in the scene, this needs to be set up as a camera
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3 (x:0, y:5, z:15)
}
}
After more checking through the documentation and making sure that I was now copying from the tutorial directly to get it to work, I still have no luck with this. According to a lot of the other questions I found here on StackOverflow, it looks like it has something to do with the forced unwrapping, the exclamation points, but I'm not exactly sure why that is.
I've probably been staring the answer in the face combing through this documentation, but I'm not quite seeing what the problem is.
Also, apologies if my comments are a bit long and/or distracting.
You have the following problems:
1) you should re-order the initializations in your viewDidLoad, doing so:
initView() // must be initialized before the scene
initScene() // you have been crashing here on getting `gameView.scene`, but gameView was nil
initCamera()
2) cameraNode is not attached on the rootNode, so you may add the following code at the end of initCamera:
gameScene.rootNode.addChildNode(cameraNode)

how to add a sprite an existing scene

What is the best way to add a sprite node after a scene has been fully loaded? The sequence looks like this:
1) I build the scene, GameScene().
2) Some time later, I download backend data and I use this info to build a SKSpriteNode in a different class, NodeBuilder().
3) I want to add this node to the instance of my scene that I'd already loaded.
What's the best way to achieve step 3)?
In GameScene:
addChild(yourNode)
For getting and keeping a reference to NodeBuilder:
Creation of NodeBuilder in GameScene:
class GameScene : SKScene {
var nodeBuilder = NodeBuilder() // Create an instance of NodeBuilder
func didMoveToView(skView: SKView) {
nodeBuilder.gameScene = self // Add self as the instance of GameScene that nodeBuilder has reference to
}
}
In NodeBuilder:
class NodeBuilder {
var gameScene : GameScene! // This is how you keep your reference
func addNodeToGameScene(node: SKNode) {
self.gameScene.addChild(node)
}
addNodeToGameScene(aNode) // This is how you would call the method to add a node to GameScene from NodeBuilder
}

I keep getting an error saying "expected declaration"

I'm following a tutorial to make a version of flappy bird. I'm using swift and this error keeps coming up. The "addChild(self.myFloor1) keeps saying expected declaration error. What did I do wrong?
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var myBackground = SKSpriteNode()
var myFloor1 = SKSpriteNode()
var myFloor2 = SKSpriteNode()
addChild(self.myFloor1)
override func didMoveToView(view: SKView) {
myBackground = SKSpriteNode(imageNamed: "background")
myBackground.anchorPoint = CGPointZero;
myBackground.position = CGPointMake(100, 0);
self.backgroundColor = SKColor(red: 80.0/255.0, green: 192.0/255.0, blue: 203.0/255.0, alpha: 1.0)
addChild(self.myBackground)
myFloor1 = SKSpriteNode(imageNamed: "floor")
myFloor2 = SKSpriteNode(imageNamed: "floor")
myFloor1.anchorPoint = CGPointZero;
myFloor1.position = CGPointMake(0, 0);
myFloor2.anchorPoint = CGPointZero;
myFloor2.position = CGPointMake(myFloor1.size.width-1, 0);
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
When you write
addChild(self.myFloor1)
You are calling a method, which must be done inside another method declaration.
Within your class declaration, the "highest level" "things" need to be declared as something: "var", "let", "func". Then within a "func", you can call your addChild method.
That's why you are getting the error: At that "class" level, it's expecting only things that you can specify what they are. Which here, you are not. You are trying to directly call that method.
What I suspect you may want, is add the viewDidLoad method and call addChild from within there. Or something like that...whatever makes sense for your view lifecycle.
Declaration refers to creating a new variable or method. The location you wrote addChild() seems as if you're creating a new variable. For example, let's look at the following simple class.
class GameScene: SKScene {
var myBackground = SKSpriteNode()
}
The variable myBackground is being declared as a new variable. You are creating a new instance of an SKSpriteNode object. SKSpriteNode is also a class. Now let's add a method to your GameScene class that prints hello. All the things you declare in the class is referred as being in the top level, which is where you create variables and functions, etc.
class GameScene: SKScene {
var myBackground = SKSpriteNode()
//This is a method/function of the class GameScene
func sayHello() {
print("Hello.")
}
//CAN'T CALL ITS OWN METHOD AT THE TOP LEVEL
sayHello()
}
To help you understand, addChild is a method/function of the SKNode class.
class SKNode: UIResponder {
func addChild(node: SKNode) {...}
}
So when you have something like you have it, it doesn't make sense because addChild is a function and you can't call a function at the top level of a class.
class GameScene: SKScene {
var myBackground = SKSpriteNode()
var myFloor1 = SKSpriteNode()
//CAN'T CALL METHOD ON TOP LEVEL OF CLASS
addChild(self.myFloor1)
}
Xcode thinks you're creating a new function called addChild, so it's expecting you to declare it by using the "func" keyword, which is why it's giving you the error, but obviously you're not creating a function call addChild, you need to call it.
You have to call addChild() inside a function/method because it calls the SKNode's addChild method.

Swift SpriteKit "Extra Arguement "size" in call

When trying to transition between a SKScene to another SKScene. For example when its game over I use the code below and get the following error:-
Extra Arguement "size" in call
Below is a sample of my code used when this error occurs.
let scene = GameOverScreen(size: self.scene!.size) //<<---- Error throws here
scene.scaleMode = SKSceneScaleMode.AspectFill
view!.scene?.paused = true
self.scene!.view!.presentScene(scene, transition: transition)
This has never happened before in previous games of mine, and I cant seem to figure out why?
Using XCode 7, swift 2 and SpriteKit
Thanks in advance,
Rachel
I'm guessing this is happening because you have other initializers defined in GameOverScene that you haven't mentioned. Since you do that, you don't automatically inherit all of SKScene's initializers. You probably just have to add the following to GameOverScene:
override init(size: CGSize) {
// Set up your properties
super.init(size: size)
// Do whatever else you need to
}
If you didn't have any initializers defined in GameOverScene, you wouldn't be seeing this behavior. I'd suggest doing some further reading on Automatic Initializer Inheritance. The section I've linked to on that page describes the rules for when a class inherits the initializers of a superclass. They are not inherited by default.