I thought it would be interesting to learn how to pass an image from an imagePicker to my GameScene to use as a SKSpriteNode(texture: SKTexture(image: UIImage)) but i keep getting an error after selecting my image and pressing a button to go into my gameScene
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "startSegue") {
var gameViewController = segue.destinationViewController as GameScene
gameViewController.imageToPass = pickedImage.image
}
anyone know what im doing wrong?
keep getting exc_breakpoint (code=exc_i386_bpt, subcode 0x0)
when initializing gameViewController as GameScene
I am trying to pass imageToPass to the GameScene.swift that is created when i create a new game project in Xcode
class GameScene: SKScene {
var imageToPass = UIImage()
var bird = SKSpriteNode()
override func didMoveToView(view: SKView) {
/* Setup your scene here */
var birdTexture = SKTexture(image: imageToPass)
bird = SKSpriteNode(texture: birdTexture)
bird.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
self.addChild(bird)
}
The destination view controller is a UIViewController or its subclass. Not an SKScene. So I don't think it can be converted to GameScene. GameScene is usually a property of a UIViewController that holds it. In my project the UIViewController is called GameViewController. GameViewController has a property called gameScene
So the code should be
if let gameViewController = segue.destinationViewController as? GameViewController
{
gameViewController.imageToPass = pickedImage.image
}
create a property in GameViewController called imageToPass. Set the imageToPass to GameScene.imageToPass in viewDidLoad.
class GameViewController: UIViewController {
var pickedImage : UIImage!
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
let skView = self.view as SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
scene.scaleMode = .AspectFill
scene.imageToPass = pickedImage
skView.presentScene(scene)
}
}
}
You can create a property observer on imageToPass to change it dynamically.
var bird : SKSpriteNode!
var imageToPass : UIImage!
{
didSet
{
createBird()
}
}
func createBird()
{
var birdTexture = SKTexture(image: imageToPass)
bird?.removeFromParent()
bird = SKSpriteNode(texture: birdTexture)
bird.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
self.addChild(bird)
}
In the default SpriteKit implementation, the View Controller created is a GameViewController. In Interface Builder, it has for its view an SKView. In viewDidLoad, the key lines are
if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
// Configure the view.
let skView = self.view as SKView
/* more setup */
skView.presentScene(scene)
}
It looks like you've edited your question to reflect this. I'm not a SpriteKit expert, but if you get imageToPass to the GameViewController, then you should be able to pass it to your GameScene in viewDidLoad before it's presented. Good luck.
Note "GameScene.swift" is just the name of the text file that contains the code, so you wouldn't pass anything to it, the class is created in class GameScene: SKScene { /* class properties and methods */
Original Answer:
Are you sure the destinationViewController is a GameScene? Really sure? Because you're using as, you're force downcasting the segue view controller as a "GameScene".
Take advantage of Swift's safety features:
if var gameViewController = segue.destinationViewController as? GameScene {
gameViewController.imageToPass = pickedImage.image
}
or using optional chaining:
var gameViewController = segue.destinationViewController as? GameScene
gameViewController?.imageToPass = pickedImage.image
The pickedImage won't get through if not a GameScene, regardless, but you won't get a crash.
EDIT: By the way, it really should be GameSceneViewController so its clear everywhere it's a View Controller. If it's not a View Controller, that's a problem and has no chance to work.
Related
I want to call a func of GameViewController from the GameScene.
-> If the game Ends I want to call GameViewController().GameOver()
I tried now a lot of different things like this one: LINK (I tried every answer more than once, still not working)
But doesn't matter what I tried it doesn't even call the func.
Hope anyone can help me with this.
CODE:
GameViewController:
class GameViewController: UIViewController {
#IBOutlet weak var Button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = GameScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
if UIDevice().userInterfaceIdiom == .phone {
scene.scaleMode = .aspectFill
}else{
scene.scaleMode = .aspectFit
}
// Present the scene
view.presentScene(scene)
skView = view
}
view.ignoresSiblingOrder = true
view.showsFPS = false
view.showsNodeCount = false
}
...
#IBAction func Button(_ sender: Any) {
animation()
if let gameScene = skView.scene as? GameScene { // check to see if the current scene is the game scene
gameScene.start()
}
}
func animation(){
UIButton.animate(withDuration: 1, animations: {
self.Button?.alpha = 0
})
}
func GameOver(){
UIButton.animate(withDuration: 1, animations: {
self.Button?.alpha = 1
})
}
}
GameScene:
class GameScene: SKScene, SKPhysicsContactDelegate {
...
func torpedoDidCollideWithAlien (torpedoNode:SKSpriteNode, alienNode:SKSpriteNode) {
GameViewController().GameOver()
removeAllActions()
removeAllChildren()
}
}
Option 1:
You should pass GameViewController instance when you are calling GameScene from GameViewController at the first time, Then just save this instance in some var gameVC: GameViewController! and initialize it like you do in prepare for segue method.
Then you'll be able to call gameVC.GameOver()
Option 2:
Put this in GameScene when you want to call GameOver():
if let controller = self.view?.window?.rootViewController as? GameViewController {
controller.GameOver()
}
This is probably a dumb question ,but here is my situation.
I have an SKShapeNode rect that goes from left to right on the screen as follows :
// GameScene.swift
var rect = SKShapeNode()
var counter = 0{
didSet{
rect.position = CGPoint(x: CGFloat(counter) , y: frame.midY)
if CGFloat(counter) > frame.width{
counter = 0
}}}
override func update(_ currentTime: TimeInterval) {
counter = counter + 4
}
In the ViewController.swift I try to get the rect.position like this , which I know is wrong because it creates a new Instance.
//ViewController.swift
let gameScene = GameScene()
#IBAction func button(_ sender: Any) {
// gameScene.rect.position = games.frame.CGPoint(x: 200, y: 400)
print(gameScene.rect.position) // Always returns (0,0)
}
Question: How can I get rect.position in real time from the other class. So that whenever I press the button , I get the actual position of the rect?
UPDATE
On Ron's suggestion I updated the viewDidLoad method in my ViewController.swift from this :
let gameScene = GameScene()
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.spriteView {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
}}
to this :
var gameScene : GameScene!
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.spriteView {
// Load the SKScene from 'GameScene.sks'
if let scene = GameScene(fileNamed: "GameScene") { // SKScene changed to GameScene
self.gameScene = scene // scene assigned to gameScene variable
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
}
}
INTENTION
I wanted to get the position of the moving bar when the play button was clicked.
Note that the GameScene only represents a part of the actual screen
When you first transition to your GameScene (Assuming that you are going directly from your GameViewController to your GameScene) create a class level variable for gameScene. Then when you need the information from GameScene use that same variable vs. creating a new GameScene variable
var gameScene: GameScene!
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let gameScene = GameScene(fileNamed: "GameScene") {
self.gameScene = gameScene
gameScene = .aspectFill
// Present the scene
view.presentScene(gameScene)
}
}
}
func getCoords() {
print("gameScene.rect.position \(gameScene.rect.position)")
}
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.
Hello my question is simply how to unhide a button in GameScene. I have a segueToMainMenu button that is set up in storyboard.
This is how my GameViewController looks:
#IBOutlet weak var segueToMainMenu: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
let scene = GameScene(size: view.bounds.size)
let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = false
scene.scaleMode = .AspectFill
skView.presentScene(GameScene(size: skView.bounds.size))
scene.viewController = self
self.segueToMainMenu.hidden = true
}
I set the button to hidden but now in my game I would like to unhide it when a lose func runs in GameScene since when the button is clicked it segues back to the main menu which is a separate view controller also created in the storyboard. Anything helps thank you.
You either can create a delegate for you scene which will call appropriate method on gameOver and do something like
// in the view controller
func gameSceneDidSendGameOver() {
self.segueToMainMenu.hidden = false
}
//in the scene
var gameOverDelegate : GameOverDelegate?
func gameOver() {
gamOverDelegate?.gameSceneDidSendGameOver()
}
Or do it a little bit of ugly way like:
func gameOver() {
let gvc = self.viewController as GameViewController
gvc.segueToMainMenu.hidden = false
}
Edit: Declaration of the delegate protocol.
protocol GameOverDelegate {
func gameSceneDidSendGameOver()
}
class GameViewController : ViewController, GameOverDelegate {
...
func gameSceneDidSendGameOver() {
self.segueToMainMenu.hidden = false
}
}
I have a controller that is using a Singleton to change SKScenes in a single storyboard scene.
class GameViewController: UIViewController, GKGameCenterControllerDelegate, GADBannerViewDelegate, GADInterstitialDelegate, ADBannerViewDelegate, FBLoginViewDelegate, CLLocationManagerDelegate, MapViewControllerDelegate {
var sharedInstance = Singleton.sharedInstance
var skView:SKView!
var scene:GameScene! = GameScene.unarchiveFromFile("GameScene") as? GameScene
var searchedTypes = ["bakery", "bar", "cafe", "grocery_or_supermarket", "restaurant"]
let locationManager = CLLocationManager()
var adHelper: AdHelper = AdHelper()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
// Configure the view.
skView = self.view as SKView
skView.showsFPS = false
skView.showsNodeCount = false
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
skView.presentScene(scene, transition: SKTransition.crossFadeWithDuration(kSceneTransitionSpeed))
}
// LOAD MAP SCENE
func showMap(){
let viewController:UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("MapController") as UIViewController
self.presentViewController(viewController, animated: false, completion: nil)
}
}
In my game scene I have a button that I am calling to this controller to change to the other scene in the storyboard because I don't have a layout button to use an IBAction
import SpriteKit
class GameScene: SKScene {
var sharedInstance = Singleton.sharedInstance
let playButton = Button(imageNamed:"playButton");
let highscoresButton = Button(imageNamed:"highscoresButton");
let creditsButton = Button(imageNamed:"creditsButton");
let moreGamesButton = Button(imageNamed: "moreGamesButton")
override func didMoveToView(view: SKView) {
// background
let background = SKSpriteNode(imageNamed: "background")
background.position = CGPointMake(self.size.width/2, self.size.height/2)
self.addChild(background)
// main image
let main = SKSpriteNode(imageNamed: "main")
main.position = CGPointMake(self.size.width/2, self.size.height/2)
self.addChild(main)
// buttons
moreGamesButton.position = CGPointMake(self.size.width/2 + playButton.size.width, main.position.y + main.size.height/2 + playButton.size.height)
self.sharedInstance.addChildFadeIn(moreGamesButton, target: self)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self);
if self.nodeAtPoint(location) == self.moreGamesButton {
var gameController: GameViewController=GameViewController()
gameController.showMap()
}
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
When I click this I don't get a defined error just hits a break and it the thread I get "Swift dynamic cast failed".
Can I not call the controller like that within my scene or is is it a problem in instantiating the other controller?