Adding a sprite to GameScene from another class - ios

I want to add a new sprite to my GameScene within my new file "Constructor.swift" Constructor class. I tried to make a getter within my GameViewController so I could get the current instance of my GameScene and then adding the sprite to it but this doesn't work.
Conclusion;
How can I get the current instance of my GameScene in another class, so my code would be easy maintainable because of all the sprites getting constructed in the constructor class. I want to prevent getting massive amount lines of code in my "GameScene.sks" file.

You can subclassing with a generic class named for example:
class MyDefaultScene: SKScene {
var sprite1: SKSpriteNode!
var followTrack: SKAction!
var followTrackForever: SKAction!
var clockWise : Bool = false
func setDefaultPhisics() {
self.physicsBody!.mass = 0
self.physicsBody!.friction = 0
self.physicsBody!.affectedByGravity = false
self.physicsBody?.linearDamping = 0
self.physicsBody?.angularDamping = 0
self.physicsBody?.restitution = 1
self.physicsBody?.dynamic = false
}
...
}
class GameScene1: MyDefaulScene {
override func didMoveToView(view: SKView) {
print("∙ \(NSStringFromClass(self.dynamicType))")
print("clockwise: \(self.clockWise)")
}
}
class GameScene2: MyDefaulScene {
override func didMoveToView(view: SKView) {
print("∙ \(NSStringFromClass(self.dynamicType))")
print("clockwise: \(self.clockWise)")
}
}

Related

Change the text of a SKLabelNode added from the scene editor

I want to change the text of a SKLabelNode created in the scene editor named myScoreLabel to update a score on collision of two objects. Here's the relevant code:
class holeOne: SKScene, SKPhysicsContactDelegate {
var myScoreLabel: SKLabelNode!
var myscore:Int = 0
func addScore() {
myscore += 1
myScoreLabel.text = "\(myscore)"
}
func didBegin(_ contact: SKPhysicsContact) {
addScore()
}
}
At the moment after the collision the app crashes with "unexpectedly found nil while unwrapping an Optional value". What am I doing wrong and how can I do it right? Thanks!
From code that you provide var myScoreLabel: SKLabelNode! is not created.
Try to create SKLabelNode firstly. And then set value.
Example:
myScoreLabel = SKLabelNode(fontNamed: "Chalkduster")
myScoreLabel.text = "Test"
myScoreLabel.horizontalAlignmentMode = .right
myScoreLabel.position = CGPoint(x: 0, y:10)
addChild(scoreLabel)
Or you can connect it from .sks scene.
override func sceneDidLoad() {
if let label = self.childNode(withName: "myScoreLabel") as? SKLabelNode {
label.text = "Test" //Must add '.text' otherwise will not compile
}
}
Great, so in the end I did this:
class holeOne: SKScene, SKPhysicsContactDelegate {
var myScoreLabel: SKLabelNode!
var myscore:Int = 0
func addScore() {
myscore += 1
if let myScoreLabel = self.childNode(withName: "myScoreLabel") as? SKLabelNode {
myScoreLabel.text = "\(myscore)"
}
}
func didBegin(_ contact: SKPhysicsContact) {
addScore()
}
}

Pass current GameScene instance into a class called within GameScene

I'm building a PauseButton class in my SpriteKit game, and I want to draw a label to the GameScene when it is activated. However, I'm required to instantiate the button outside of didMoveToView (right before it- in GameScene) so that I can access it more "globally" from the touchBegan method.
let pauseButton = PauseButton(theTexture: pauseButtonTexture,gameScene:GameScene)
override func didMoveToView(view: SKView) {
Ideally I would be able to pass in the view by instantiating it within didMoveToView but as of now I don't know how to do this and also access it from the rest of the scene. That said, is there a way to pass in the current GameScene instance I'm working with so that I can do something like gameScene.addchild(label) from inside a method of my button class? I couldn't find anything useful on this, so any help would be great!
You basically can do it 2 ways.
1) Just create the property like this
var pauseButton: PauseButton!
and than instantiate it in didMoveToView like so.
override func didMoveToView(view: SKView) {
pauseButton = PauseButton(theTexture: pauseButtonTexture,gameScene: self)
addChild(pauseButton)
}
2) Use lazy instantiation so you can do what you tried to do initially.
A lazy var in in simplest form allows you to use self without doing step 1.
lazy var pauseButton: PauseButton = PauseButton(theTexture: pauseButtonTexture,gameScene: self)
override func didMoveToView(view: SKView) {
You could even do it like so to add more properties of this button all in the same spot.
lazy var pauseButton: PauseButton = {
let button = PauseButton(theTexture: pauseButtonTexture,gameScene: self)
button.zPosition = 200
button.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
button.alpha = 0.5
return button
}()
override func didMoveToView(view: SKView) {
addChild(pauseButton)
}
Remember every SKNode/SKSpriteNode etc has a scene property. If your button class is a subclass of SKNode/SKSpriteNode than you can get its scene property and use the "as" operator to get a reference to GameScene without passing in the scene in the initialisation method.
e.g
class GameScene: SKScene {
lazy var pauseButton: PauseButton = PauseButton(theTexture: pauseButtonTexture,gameScene: self)
var someBool = false
override func didMoveToView(view: SKView) {
addChild(pauseButton)
pauseButton.loadPausedLabel()
}
func someMethod() {
}
}
class PauseButton: SKSpriteNode {
func loadPauseLabel() {
// Way 1, reference to current scene but not to specific class
guard let scene = scene else { return }
// Scene property is optional and is nil until button is added to a Scene so add the guard check.
scene.someBool = true // NOT WORKING
scene.someMethod() // NOT WORKING
let label = SKLabelNode(...
scene.addChild(label) // WORKING
// Way 2, use "as" to cast to specific scene (e.g GameScene) so you can call properties/methods on it. This is the same as passing GameScene in the init method.
guard let gameScene = scene as? GameScene else { return }
// Scene property is optional and is nil until button is added to a Scene so add the guard check.
// The button must be added to the scene you are trying the as? cast with, in this example GameScene.
gameScene.someBool = true // WORKING
gameScene.someMethod() // WORKING
let label = SKLabelNode(...
gameScene.addChild(label) // WORKING
}
}
As a tip, I would not add the pause label in the pauseButton subclass, I would add it from within the scene it self or from a pause menu subclass.
Also I would not subclass each button, I would crate 1 class that can be used for all buttons in your game. You could create an enum with button names and pass that into the init method to distinguish between your buttons when they are pressed.
Your button subclass should only handle animations, textures etc. You can check out apples game DemoBots for an example.
Hope this helps

Updating nodes in a SpriteKit scene from an outside UIViewController

How can I update SpriteKit Scene from outside the scene itself. E.g. how do I update a SKLabelNode by pressing a UIButton.
Scene
class GameScene: SKScene {
var myLabel:SKLabelNode = SKLabelNode()
override func didMoveToView(view: SKView) {
myLabel.text = "Initial State"
myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
self.addChild(myLabel)
}
func didPressButton() {
myLabel.text = "Pressed! 👻"
}
ViewController
import UIKit
import SpriteKit
class GameViewController: UIViewController {
#IBAction func didPressButton(sender: AnyObject) {
if let scene = GameScene(fileNamed:"GameScene") {
scene.didPressButton()
}
}
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene(fileNamed:"GameScene") {
let skView = self.view as! SKView
skView.presentScene(scene)
}
}
…
}
I understand that the usual touchesBegan inside the scene works just fine for updating the SKLabelNode inside the scene. But I am specifically interested in updating the scene from outside events. Any ideas?
In the file where is your button:
protocol PauseBtnSelectorDelegate {
func didPressPauseBtn(pauseBtn:SKSpriteNode)
}
class HUD: SKNode {
var pauseBtnDelegate:PauseBtnSelectorDelegate?
var pauseBtn :SKSpriteNode!
...
func pauseBtnTap(sender:SKSpriteNode) {
print("pauseBtn touched")
pauseBtnDelegate!.didPressPauseBtn(sender)
}
}
In the file where you want to notify the tap event:
class GameScene: SKScene,PauseBtnSelectorDelegate {
var hud: HUD!
...
override func didMoveToView(view: SKView) {
super.didMoveToView(view)
// Adding HUD
self.hud = HUD(currentScene: self,nodeSize: self.frame.size)
self.hud.pauseBtnDelegate = self
self.addChild(hud)
}
func didPressPauseBtn(pauseBtn:SKSpriteNode) {
print("pauseBtn touched")
// Pause the game
}
}

Seque from a storyboard image tap into a SpriteKitScene

I'm creating a Swift project for a high school programming class. I can't seem to figure out this problem, and everyone else in my class doesn't seem to have any ideas.
To start, I created a new Swift project, and chose a game format.
I then used some basic code to make the first level for my game, a maze game where the maze moves around instead of the ball based on how the user tilts the device.
This is my GameScene.swift:
import SpriteKit
import CoreMotion
var accelupdateinterval = 0.1
var accelmultiplier = 15.0
class GameScene: SKScene {
let manager = CMMotionManager()
override func didMoveToView(view: SKView) {
manager.startAccelerometerUpdates()
manager.accelerometerUpdateInterval = accelupdateinterval
manager.startAccelerometerUpdatesToQueue(NSOperationQueue.mainQueue()){
(data, error) in
self.physicsWorld.gravity = CGVectorMake(CGFloat((data?.acceleration.x)!) * CGFloat(accelmultiplier), CGFloat((data?.acceleration.y)!) * CGFloat(accelmultiplier))
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
I want to have a main menu that the app opens into, which is mainMenu.storyboard:
I successfully have the app launching into the mainMenu.storyboard (and the level physics work well when I've tested the level1.sks), but I'm having trouble figuring out how to segue.
GOAL: I want people to be segued into the level1.sks (and the levels that I add later), when they tap the corresponding image in mainMenu.storyboard.
I can't use the method of adding a Storyboard Reference to segue it, as the Storyboard Reference won't let me choose level1.sks.
I'd also love to find out how to send users back to the main menu when the player icon touches the goal (the blue thing up near the top in this screenshot):
So to do this I think the best approach is to create another ViewController subclass, maybe named LauncherViewController, which will present your SKScene. Then in your storyboard add this viewController and have your menu segue to it on an image press.
Here is a start for the LauncherViewController
class LauncherViewController: UIViewController {
var gameScene: GameScene!
override func viewDidLoad() {
}
override func viewWillAppear() {
presentGameScene()
}
func presentGameScene(){
let skView = self.view as! SKView
skView.showsFPS = false
skView.showsNodeCount = false
skView.ignoresSiblingOrder = true
gameScene.size = skView.bounds.size
gameScene.scaleMode = .AspectFill
skView.presentScene(gameScene)
}
}
Where in your menu controller you have a prepareForSegue like this:
override func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?) {
if segue.identifier == "yourSegue" {
let destinationViewController = segue.destinationViewController as! LauncherViewController
destinationViewController.gameScene = GameScene()
}
to have your LauncherViewController dismiss the gameScene when the user finishes the maze, use a delegate pattern. So in GameScene add a protocol above your class
protocol GameDelegate {
func gameFinished()
}
have your LauncherViewController conform to this delegate and set the gameScene's delegate variable to self (see below)
class LauncherViewController: UIViewController, GameDelegate {
var gameScene: GameScene!
override func viewDidLoad() {
gameScene.delegate = self
}
override func viewWillAppear() {
presentGameScene()
}
func presentGameScene(){
let skView = self.view as! SKView
skView.showsFPS = false
skView.showsNodeCount = false
skView.ignoresSiblingOrder = true
gameScene.size = skView.bounds.size
gameScene.scaleMode = .AspectFill
skView.presentScene(gameScene)
}
func gameFinished(){
// this forces LauncherViewController to dismiss itself
dismissViewControllerAnimated(true, completion: nil)
}
}
add a variable in GameScene to hold the delegate (LauncherViewController) and add a function that calls the delegate function. You will also need to add the logic to know when the game is over as I haven't done that.
class GameScene: SKScene {
let manager = CMMotionManager()
var delegate: GameDelegate!
override func didMoveToView(view: SKView) {
manager.startAccelerometerUpdates()
manager.accelerometerUpdateInterval = accelupdateinterval
manager.startAccelerometerUpdatesToQueue(NSOperationQueue.mainQueue()){
(data, error) in
self.physicsWorld.gravity = CGVectorMake(CGFloat((data?.acceleration.x)!) * CGFloat(accelmultiplier), CGFloat((data?.acceleration.y)!) * CGFloat(accelmultiplier))
}
}
func gameOver(){
delegate.gameFinished()
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
// it's probably easiest to add the logic for a gameOver here
if gameIsOver {
gameOver()
}
}
}
There will probably be some mistakes in here as I wrote this on my phone so just comment below for anything you are unsure about or doesn't work.

How to make buttons and labels appear and disappear

What I want to know:
I want to know that how to make buttons/labels appear and disappear. When my character collides with an object the buttons/labels will show up over the view and the game-view wont be working any more, only the buttons/labels that appeared can be interacted with.
What I have tried:
I have tried .hidden = false and .hidden = true but it didn't work but maybe I was not using it correctly.
CODE: I have delete unnecessary code!
import Foundation
import AVFoundation
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var movingGround: PPMovingGround!
var square1: PPSquare1!
var square2: PPSquare2!
var wallGen: PPWallGen!
var isStarted = false
var isGameOver = false
override func didMoveToView(view: SKView) {
addMovingGround()
addSquare1()
addWallGen()
start()
}
func addSquare1() {
square1 = PPSquare1()
square1.position = CGPointMake(70, movingGround.position.y + movingGround.frame.size.height/2 + square1.frame.size.height/2)
square1.zPosition = 1
playerNode.addChild(square1)
}
func addWallGen() {
wallGen = PPWallGen(color: UIColor.clearColor(), size: view!.frame.size)
wallGen.position = view!.center
addChild(wallGen)
}
func start() {
isStarted = true
//square2.stop()
square1.stop()
movingGround.start()
wallGen.startGenWallsEvery(1)
}
// MARK - Game Lifecycle
func gameOver() {
isGameOver = true
// everything stops
//square2.fall()
square1.fall()
wallGen.stopWalls()
diamondGen.stopDiamonds()
movingGround.stop()
square1.stop()
//square2.stop()
// create game over label
let gameOverLabel = SKLabelNode(text: "Game Over!")
gameOverLabel.fontColor = UIColor.whiteColor()
gameOverLabel.fontName = "Helvetica"
gameOverLabel.position.x = view!.center.x
gameOverLabel.position.y = view!.center.y + 80
gameOverLabel.fontSize = 22.0
addChild(gameOverLabel)
func restart() {
let newScence = GameScene(size: view!.bounds.size)
newScence.scaleMode = .AspectFill
view!.presentScene(newScence)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
if isGameOver {
restart()
} else {
square1.flip()
}
}
override func update(currentTime: CFTimeInterval) {
// MARK: - SKPhysicsContactDelegate
func didBeginContact(contact: SKPhysicsContact) {
if !isGameOver {
gameOver()
} else {
println("error, not game over!"
}
Without seeing your code, this is a little hard to determine, but I would suggest the following:
Be sure you have connected the buttons to an Outlet variable. This is critical. Without connecting them, you can use the hidden boolean, but it would not have an effect on an actual button.
Be sure you are not somehow undoing your own changes. For example, further down in the code, you might have something which is setting hidden to false even after you set it to true, and so on.
In some cases, you might want to set your outlet variable as strong instead of weak. This may retain changes that are being lost with a view switch.
You can also use "alpha" such as:
myButton.alpha = 0
as an alternate way of controlling visibility. 0 would set the alpha to none (which would make the button invisible) and 1 would set the alpha to full (which would make the button visible again.)
Right after you set hidden (or alpha) put in:
println("i hid the button!")
just to be sure the code you think you are executing really is being executed. Sometimes code we think is not working is actually not even being called.
Please provide more info and I will gladly work to get this solved for you.

Resources