when I try to present an uiactivityviewcontroller or just another viewcontroller, I get an lldb error without any further explanation
EDITED:
Game starts with GameMenuScene, when play is clicked it will move to GameScene:
class GameMenuScene: SKScene {
weak var weakGameVC: GameViewController?
if nodeAtPoint.name == "play"{
NSUserDefaults.standardUserDefaults().setInteger(score+1, forKey: "currentLevel")
NSUserDefaults.standardUserDefaults().synchronize()
self.removeAllActions()
self.removeAllChildren()
var scene1:SKScene = GameScene(size: self.size)
scene1.weakGameVC = self ##Updated : error: SKSCene does not have a member named "weakGameVC"
self.view?.presentScene(scene1)
}
}
GameScene:
Here is the GameViewController(as you can see, first scene is GameMenuScene:
import UIKit
import SpriteKit
import AVFoundation
import Social
class GameViewController: UIViewController, UITextFieldDelegate{
weak var weakGameVC: GameViewController? ##Updated
var player:SKSpriteNode = SKSpriteNode()
var scene:GameMenuScene!
override func viewDidLoad() {
super.viewDidLoad()
let skView = view as SKView
skView.multipleTouchEnabled = false
scene = GameMenuScene(size: skView.bounds.size)
scene.weakGameVC = self ##Updated
//scene.scaleMode = SKSceneScaleMode.ResizeFill
skView.showsFPS = true
skView.showsNodeCount = true
skView.presentScene(scene)
}
func shareButton() {
var myShare = "aa"
let activityVC:UIActivityViewController = UIActivityViewController(activityItems: ["aa"], applicationActivities: nil)
presentViewController(activityVC, animated: true, completion: nil)
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
And here the GameScene, where the gameplay is:
import SpriteKit
import Social
class GameScene: SKScene, SKPhysicsContactDelegate {
weak var weakGameVC: GameViewController?
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location: CGPoint! = touch.locationInNode(self)
let nodeAtPoint = self.nodeAtPoint(location)
if (nodeAtPoint.name != nil) {
if nodeAtPoint.name == "share"{
println("1")
if let gvc = weakGameVC {
println("2")
gvc.shareButton()
}
}
}
}
}
}
It doesn't work because in touchBegan method you create new object GameViewController:
var gameViewController = GameViewController()
The gameViewController is not added to the view hierarchy (it's not presented) and after that you call shareButton() method which wants to present another view (share view) on not presented view (game view).
The solution should be that you handle the touch (touchesBegan) in the same class where you have shareButton() by calling shareButton() on the self not gameViewController.shareButton().
Or create delegate method from scene to view controller, if touch happened in scene call delegate method and handle it in view controller to present the share view
// EXTENDED - Solution with weak reference to game view controller.
In GameScene add weak reference to gameVC:
weak var weakGameVC: GameViewController?
and later in the same file:
if nodeAtPoint.name == "share" {
if let gvc = weakGameVC {
gvc.shareButton()
}
}
In GameViewController file after you create game scene you have to add something like that:
gameScene.weakGameVC = self
Related
I have created a TilemapScene (CKTilemapScene.sks) in Swift 5. This is basically to lay a tile map background of my game project. Refer to the second screenshot below.
Then, in my main swift code, I load all the background with the standard codes.
But, somehow the tile map is not centered. Refer to the first screenshot below . But, at the bottom left of the screen. I tried to play with anchor point. That doesn't help. Did I set anything wrong?
import UIKit
import SpriteKit
import GameplayKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
if let scene = SKScene(fileNamed: "CKTilemapScene") {
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
view.showsPhysics = true
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .landscape
//.allButUpsideDown
} else {
return .all
}
}
Set the position of SKScene to the SKView's center.
override func viewDidLoad() {
super.viewDidLoad()
if let view = view as? SKView {
if let scene = SKScene(fileNamed: "CKTilemapScene") {
scene.position = view.center
view.presentScene(scene)
}
}
//...
}
I'm developing a SpriteKit Game using the GKStateMachine for transitioning between multiple scenes. I'm using a class that holds an instance of the StateMachine, which has multiple states. The states themselves have access to the current SKView through an initializer property. The States are responsible for presenting scenes, so they have a scene property, which will be presented on the didEnter-Method.
Now I have a MainMenuState, which has a scene with 3 buttons. For passing the button events to the State, I wrote a custom delegate. The MainMenuState implements the delegate protocol and sets the scene's delegate property to 'self'. So when a user hits a button the action is forwarded to a delegate method. I wanted to use the delegate method to transition to the next state (e.g. SettingsState). However, when I try to access the GKStates StateMachine property within the delegate func it's always nil.
I think that I have a design problem here and I don't know how to solve it.
The Code of the MainMenuState
import Foundation
import GameplayKit
class MainMenuState : GKState, MenuSceneDelegate {
var view: SKView
var scene: GKScene?
init(view: SKView) {
self.view = view;
self.scene = GKScene(fileNamed: "MenuScene")
super.init()
}
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
return stateClass is MultiplayerHostState.Type ||
stateClass is MultiplayerSearchState.Type ||
stateClass is SettingsState.Type
}
override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState)
// Load 'GameScene.sks' as a GKScene. This provides gameplay related content
// including entities and graphs.
if let scene = self.scene {
// Get the SKScene from the loaded GKScene
if let sceneNode = scene.rootNode as! MenuScene? {
// Set delegate
sceneNode.menuDelegate = self
// Copy gameplay related content over to the scene
sceneNode.entities = scene.entities
sceneNode.graphs = scene.graphs
// Set the scale mode to scale to fit the window
sceneNode.scaleMode = .aspectFill
sceneNode.size = view.bounds.size
// Present the scene
if let view = self.view as SKView? {
view.presentScene(sceneNode)
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
}
}
override func willExit(to nextState: GKState) {
super.willExit(to: nextState)
}
func hostGameClicked() {
if let stateMachine = self.stateMachine {
stateMachine.enter(MultiplayerHostState.self)
}
}
func joinGameClicked() {
if let stateMachine = self.stateMachine {
stateMachine.enter(MultiplayerSearchState.self)
}
}
func settingsClicked() {
// <-- Here the StateMachine is nil -->
if let stateMachine = self.stateMachine {
stateMachine.enter(SettingsState.self)
}
}
}
After some research I found out this behavior was caused by Swifts ARC System.
The class holding the reference of the state machine was only declared within a func of the overall ViewController. So after existing the func, the class and state machine were deallocated.
I solved it within the View Controller:
class ViewController {
var classWithStateMachine: ClassWithStateMachine?
func initializeClassWithStateMachine {
self.classWithStateMachine = ClassWithStateMachine()
}
}
This code snippet is just a demonstration of the concept, no real code.
SKTransition doesn't work during transition from a scene to MainScene. It just jumps to MainScene without transition effect. You can find the codes below.
import UIKit
import SpriteKit
import GameplayKit
import FBSDKLoginKit
import TwitterKit
import Firebase
import GoogleSignIn
import GoogleMobileAds
protocol SceneManagerDelegate {
func presentMainScene()
func presentGameScene()
func presentWelcomeScene()
func presentGameOverScene()
func presentSettingsScene()
}
class GameViewController: UIViewController, TransitionDelegate, GIDSignInDelegate, GIDSignInUIDelegate, GADRewardBasedVideoAdDelegate, UIAlertViewDelegate{
/.....
}
/....
extension GameViewController: SceneManagerDelegate {
func presentSettingsScene() {
let settingsScene = SettingsScene(fileNamed: "SettingsScene")
settingsScene?.sceneManagerDelegate = self
present(scene: settingsScene!)
}
func presentGameOverScene() {
let gameOverScene = GameOverScene(fileNamed: "GameOverScene")
gameOverScene?.sceneManagerDelegate = self
present(scene: gameOverScene!)
}
func presentWelcomeScene() {
let welcomeScene = WelcomeScene(fileNamed: "WelcomeScene")
welcomeScene?.sceneManagerDelegate = self
present(scene: welcomeScene!)
}
func presentMainScene() {
let mainScene = MainScene(fileNamed: "MainScene")
mainScene?.sceneManagerDelegate = self
present(scene: mainScene!)
}
func presentGameScene() {
let gameScene = GameScene(fileNamed: "GameScene")
gameScene?.sceneManagerDelegate = self
present(scene: gameScene!)
}
func presentMatchingScene() {
let matchingScene = MatchingScene(fileNamed: "MatchingScene")
matchingScene?.sceneManagerDelegate = self
present(scene: matchingScene!)
}
func present(scene: SKScene) {
let transition = SKTransition.reveal(with: .left, duration: 1)
if let view = self.view as! SKView? {
if let gestureRecognizers = view.gestureRecognizers {
for recognizer in gestureRecognizers {
view.removeGestureRecognizer(recognizer)
}
}
scene.scaleMode = .aspectFit
scene.delegate = self as TransitionDelegate
view.presentScene(scene, transition: transition)
view.ignoresSiblingOrder = true
view.showsNodeCount = true
view.showsFPS = true
}
}
}
To call the method from other scenes
var sceneManagerDelegate: SceneManagerDelegate? // sceneManagerDelegate.present....
Your transition function is OK but your delegate implementation is faulty. In your present(scene:) method you set scene.delegate = self as TransitionDelegate, it would not be there. I have implemented your transition Code here in gameOver() method in Swift 4, and it works great. You can see this tutorial for Delegate implementation for transition in SpriteKit. Also read this answer given by KnightOfDragon.
I have a node I added in the scene. Also in the scene I give it a component to bounce. The component looks like this:
class BounceComponent: GKComponent, ComponentSetupProt {
var bounce: SKAction?
var node: SKSpriteNode?
#GKInspectable var diff: CGFloat = 0.2
#GKInspectable var startScale: CGFloat = 1
#GKInspectable var duration: TimeInterval = 0.5
override init() {
super.init()
comps.append(self)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func setup() {
print("setup")
node = entity?.component(ofType: GKSKNodeComponent.self)!.node as? SKSpriteNode
setupBounce()
}
func setupBounce() {
...
}
func performBounce() {
print("performing")
node?.run(bounce!)
}
}//
In the didMove function on my scene file, it calls the components setup(). This works fine. I'm trying to call the function performBounce() when I click on the button...
if (play?.contains(pos))! {
print("test")
if let _ = play?.entity {
print("we have an entity")
}
play?.entity?.component(ofType: BounceComponent.self)?.performBounce()
}
When I click, the only thing it prints is "test" and the entity is nil. I was under the assumption that when you add a node to the editor, it also sets up the entity for that node, so I'm not sure why it is nil. Curious if anyone could shed some light on this?
Thanks for any help!
The entity on the SKSpriteNode is a weak reference, you need to make sure you retain your entities from your gameplaykit object
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Load 'GameScene.sks' as a GKScene. This provides gameplay related content
// including entities and graphs.
if let scene = GKScene(fileNamed: "Main") {
// Get the SKScene from the loaded GKScene
if let sceneNode = scene.rootNode as? Main {
sceneNode.entities = scene.entities // add this
// Set the scale mode to scale to fit the window
sceneNode.scaleMode = .aspectFill
// Present the scene
if let view = self.view as! SKView? {
view.presentScene(sceneNode)
view.showsDrawCount = true
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
}
}
class Main: Base {
var entities = [GKEntity]() // add this
I am using UIGestureRecognizer. I am trying to call a func from different class in the selector, but i am getting NSInvalidArgumentException when it executes.
import Foundation
import UIKit
class helperClass {
var onBoardingImageArray : [UIImage]?
var onBoardingPageControl : UIPageControl?
var onBoardingImageView : UIImageView?
init(imageArray : [UIImage] , pageControl : UIPageControl , yourImageView : UIImageView) {
onBoardingImageArray = imageArray
onBoardingPageControl = pageControl
onBoardingImageView = yourImageView
}
#objc func firstImageSwipeGestureAction(gesture :UIGestureRecognizer){
if let swipeGesture = gesture as? UISwipeGestureRecognizer {
switch swipeGesture.direction {
case UISwipeGestureRecognizerDirection.right:
if (onBoardingPageControl?.currentPage)! > 0{
print("Swiped right")
onBoardingPageControl?.currentPage -= 1
self.onBoardingImageView?.image = onBoardingImageArray?[(onBoardingPageControl?.currentPage)!]
}
case UISwipeGestureRecognizerDirection.left:
if (onBoardingPageControl?.currentPage)! < (onBoardingImageArray?.count)! - 1{
print("Swiped left")
onBoardingPageControl?.currentPage += 1
self.onBoardingImageView?.image = onBoardingImageArray?[(onBoardingPageControl?.currentPage)!]
}
default:
break
}
}
}
}
import UIKit
class MainController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.addTaped()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func addTaped(){
let helpClasses : helperClass = helperClass.init(imageArray: self.firtImageViewArray! , pageControl:firstPageControl , yourImageView: firstImageView)
let firstImageswipeGestureRecognizer = UISwipeGestureRecognizer(target: helpClasses, action: #selector(helpClasses.firstImageSwipeGestureAction))
firstImageswipeGestureRecognizer.direction = .right
self.firstImageView.isUserInteractionEnabled = true
self.firstImageView.addGestureRecognizer(firstImageswipeGestureRecognizer)
let firstImageswipeGestureRecognizerLeft = UISwipeGestureRecognizer(target: helpClasses, action: #selector(helpClasses.firstImageSwipeGestureAction))
firstImageswipeGestureRecognizer.direction = .left
self.firstImageView.isUserInteractionEnabled = true
self.firstImageView.addGestureRecognizer(firstImageswipeGestureRecognizerLeft)
}
#IBOutlet weak var firstImageView: UIImageView!
#IBOutlet weak var firstPageControl: UIPageControl!
let firtImageViewArray : [UIImage]? = [#imageLiteral(resourceName: "Eagle9")]
}
You are setting up everything right, but make one mistake, when you initialise your helpClasses.
Because you declare the helpClasses variable inside the scope of addTaped() function, it will be allocated on the stack. As soon as your function finishes, helpClasses variable will be deallocated, removed from the stack, and it becomes nil. From than on, you are sending messages to an object, what is nil, therefore it is understandable, that nothing happens.
To overcome on this problem, declare your variable on the heap, outside of you functions scope. Best is if you declare it on the scope of your MainController.
Example:
import UIKit
class MainController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.addTaped()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func addTaped(){
// Initialise helpclasses here, but not as a local variable!!
helpClasses = helperClass.init(imageArray: self.firtImageViewArray! , pageControl:firstPageControl , yourImageView: firstImageView)
let firstImageswipeGestureRecognizer = UISwipeGestureRecognizer(target: helpClasses, action: #selector(helpClasses.firstImageSwipeGestureAction))
firstImageswipeGestureRecognizer.direction = .right
self.firstImageView.isUserInteractionEnabled = true
self.firstImageView.addGestureRecognizer(firstImageswipeGestureRecognizer)
let firstImageswipeGestureRecognizerLeft = UISwipeGestureRecognizer(target: helpClasses, action: #selector(helpClasses.firstImageSwipeGestureAction))
firstImageswipeGestureRecognizer.direction = .left
self.firstImageView.isUserInteractionEnabled = true
self.firstImageView.addGestureRecognizer(firstImageswipeGestureRecognizerLeft)
}
#IBOutlet weak var firstImageView: UIImageView!
#IBOutlet weak var firstPageControl: UIPageControl!
let firtImageViewArray : [UIImage]? = [#imageLiteral(resourceName: "Eagle9")]
// Move your helpClasses variable here
var helpClasses: helperClass!
}
To call a selector method from another class you just have to do the following :
let recognizer = UISwipeGestureRecognizer(target: objClass,
action: #selector(objClass.actionMethodName))