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.
Related
XCode Version 12.4 (12D4e)
The first SKScene class in Functions.swift included the buildBackground function which I want to call in different SKScene class.
class Functions: SKScene {
func buildBackground(backgroundPath: String){
let texture = SKTexture(imageNamed: backgroundPath)
let background = SKSpriteNode(texture: texture)
background.size = CGSize(width: 512, height: 512)
background.position = CGPoint(x: 0, y: 0)
background.name = "BACKGROUND"
addChild(background)
print("Background loaded")
}
}
I'm trying to call this function in the second SKScene class in the Room.swift file
I created an instance of Functions in Room and called my function just like that:
public class Room: SKScene {
let functions = Functions()
override public func didMove(to view: SKView) {
functions.buildBackground(backgroundPath: "roomBackground")
}
}
I got "Cannot find 'Functions' in scope" on the "let functions = Functions()" line. After running a build, I got my message "Background loaded", so I guess, the function was called, however, my wallpaper SKSpriteNode has not appeared in my scene. After I tried to move buildBackground method inside Room.swift class, it all works, but there are a lot of different scenes in my project (Room1, Room2, etc.) so I don't want to copy-paste this method each file.
since you are overriding a public function, you probably need to use self :
self.functions.buildBackground(...)
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.
I have a simple app where I'm creating a shape dynamically. This shape has physics, but starts out with it's dynamics set to false (as intended).
var dot = SKSpriteNode(imageNamed: "ShapeDot.png");
override func sceneDidLoad() {
dot.name = "MyShapeDot";
dot.size = CGSize(width: 10,height: 10);
dot.position = CGPoint(x: 0, y: 0);
dot.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(dot.size.width/2))
dot.physicsBody?.isDynamic = false;
dot.physicsBody?.allowsRotation = false;
dot.physicsBody?.pinned = false;
dot.physicsBody?.affectedByGravity = true;
//add to spritekit scene
self.addChild(dot)
}
The shape is successfully added to the .sks and the controller (I see it on the screen). Then on a tap gesture I'm calling a function to turn on dynamics for the physics sprite node.
func MyTapGesture(){
dot.physicsBody?.isDynamic = true;
}
The MyTapGesture is being called (I debugged that it triggers), but the shape doesn't become dynamic and start using gravity... Does anyone know what I'm missing???
I'm calling the MyTapGesture from my interfaceController... It's wired up as so
let gameScene = GameScene();
#IBOutlet weak var spriteTapGestures: WKTapGestureRecognizer!
#IBAction func onSpriteTap(_ sender: Any) {
NSLog("tap")
gameScene. MyTapGesture()
}
Within the MyTapGesture I've also tried print(dot) and it outputs the following:
name:'MyShapeDot' texture:[<SKTexture> 'ShapeDot.png' (128 x 128)] position:{0, 0} scale:{1.00, 1.00} size:{10, 10} anchor:{0.5, 0.5} rotation:0.00
This leads me to believe it should work and I'm calling the right reference of the class that's attached to the object. But it doesn't work. If I call MyTapGesture() within the update func of the SpriteKit class where my dot was created
override func update(_ currentTime: TimeInterval) {
MyTapGesture()
}
It works and the dynamics update! ...so for some reason my tap gesture must be calling a wrong reference or something??? So confused since the debug shows the correct data printed for the shape that I created...
To solve this - I realized that my gameScene var in my interface controller didn't have the correct reference. So I instantiated it as nil:
var gameScene : GameScene?;
And then assigned the variable in the interface controllers awake func
if let scene = GameScene(fileNamed: "GameScene") {
gameScene = scene
}
I have a question about using another class in my main GameplayScene. What I am trying to do is make the motions of the X-axis of the phone move the character left and right. Here is what I have in my MotionClass.swift
import SpriteKit
import CoreMotion
class MotionClass: SKScene {
var player: Player?
var motionManager = CMMotionManager()
var destX: CGFloat = 0.0
override func sceneDidLoad() {
motionManager.accelerometerUpdateInterval = 0.2
motionManager.startAccelerometerUpdates(to: OperationQueue.current!) { (data, error) in
if let myData = data {
let currentX = self.player?.position.x
if myData.acceleration.x > 0.2 {
self.destX = currentX! + CGFloat(myData.acceleration.x * 100)
print("Tilted Right")
} else {
if myData.acceleration.x < -0.2 {
self.destX = currentX! + CGFloat(myData.acceleration.x * 100)
print("Tilted Left")
}
}
}
}
}
override func update(_ currentTime: TimeInterval) {
let action = SKAction.moveTo(x: destX, duration: 1)
self.player?.run(action)
}
}
Now I'm trying to call this class in my GameplayScene.swift in the motionBegan function, but I don't know how to go about doing that. I have the variable 'grapple' as MotionClass? but I don't know where to go from there. Could anyone give a good an example on doing this?
I think you may be confused about the purpose of an SKScene subclass, which is what your MotionClass currently is. (The main idea) is to only use one SKScene at a time: if you need stuff from MotionClass then you should just make it a plain class, not an SKScene subclass.
I think you may also need to familiarize yourself a bit more with OOP as well... Outside of static properties / functions, you don't "call" a class, you instantiate it ( you make an object :] )
So, if you have goodies in MotionClass that you want to access in GamePlayClass, you need a reference to a MotionClass object
This can be done with a simple global variable... I suggest putting it into your GameViewController.swift:
// Here is a global reference to an 'empty' motion class object..
var global_motionClassObject = MotionClass()
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// ...
if let view = self.view as! SKView? else {
let scene = MotionClass(size: view.frame.size)
// Assign our global to the new scene just made:
global_motionClassObject = scene
scene.scaleMode = .aspectFit
view.presentScene(scene)
}
// ...
}
Now inside of your GamePlayClass, or wherever else, you can access MotionClass by calling global_motionClassObject
However, this may not give the desired results because I'm worried you may need to restructure your MotionClass into something other than an SKScene :)
I am creating a game and I am trying to keep a record of all enemy's killed but my SKLabel node is not updating. Here's how I'm implementing it
class GameScene: SKScene, SKPhysicsContactDelegate {
var Enemy1KillCounter:Int = 0
var Enemy1KillCounterLabel = SKLabelNode ()
override func didMoveToView(view: SKView) {
createEnemyKilledLabel()
}
func createEnemyKilledLabel() {
Enemy1KillCounterLabel.text = "\(Enemy1KillCounter)"
Enemy1KillCounterLabel.fontSize = 65
Enemy1KillCounterLabel.fontColor = SKColor .blackColor()
Enemy1KillCounterLabel.position = CGPointMake(400, 400)
self.addChild(Enemy1KillCounterLabel)
}
func updateEnemy1KillCounter() {
Enemy1KillCounter = Enemy1KillCounter + 1
print(Enemy1KillCounter)
}
// I use the next method because i call this method in my enemy class
when the enemy is "killed"
func Enemy1DieG () {
updateEnemy1KillCounter()
}
Does anybody know why my label is not being updated?
When you update Enemy1KillCounter, you also need to update the Enemy1KillCounterLabel.text with the new value. Besides, I don't see where your createEnemyKilledLabel() is called. Make sure it is called somewhere.
A side note - variable names typically start with lowercase, like enemy1KillCounterLabel. Following the standards makes the code easier to read by others...
Update your label text after updating your Enemy1KillCounter variable.
func updateEnemy1KillCounter() {
Enemy1KillCounter = Enemy1KillCounter + 1
Enemy1KillCounterLabel.text = "\(Enemy1KillCounter)"
print(Enemy1KillCounter)
}