How to solve that SKTransition effect doesn't work during transiting? - swift

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.

Related

Black screen on ARView

I am trying to programmatically instantiate an ARView. This is my code for the View Controller
import Foundation
import UIKit
import RealityKit
class ARViewController: UIViewController {
var arView: ARView = {
let arview = ARView()
arview.cameraMode = .ar
arview.automaticallyConfigureSession = true
arview.translatesAutoresizingMaskIntoConstraints = false
return arview
}()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
setupUIConstraints()
}
func setupUI() {
view.addSubview(arView)
// Load the "Cupcake" scene from the "Experience" Reality File
let boxAnchor = try! Experience.loadBox()
// Add the box anchor to the scene
arView.scene.anchors.append(boxAnchor)
}
func setupUIConstraints() {
view.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
view.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
view.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
view.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
}
}
The app will appropriately ask me for Camera permissions, but even after accepting, the 'view' is just a black screen. I am on an iPhone XS, and have imported arkit into my info.plist. Any help would be much appreciated.
In setupUIConstraints you're setting the constraints for the ARViewController.view to its own constraints, rather than the ARView.
Change to the ARView and you should be good!

How to position SKTilemap Scene in the middle of screen in Swift

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)
}
}
//...
}

How to run function multiple times in xcode?

I want to show my interstitial ad after the player have died. Meaning the interstitial ad will load in my game over scene. But i can't just write the function name in the viewdidload section. So is there a way for me to run my interstitialAd function when the player gets to the gameOverScene?
If there was an update function, i would just make a boolean value like so:
var interstitialAdShow = false
and then write this in my didmovetoview:
interstitalAdShow = true
and then in my update function:
If interstitailAdShow == true{
interstitial.present(fromRootViewController: self)
}
But now when there is no update function in the GameViewController, and i can't do it inside of my gameOverScene.swift i can't use this solution, is there another way for me to trigger my interstitial ad function when the gameOverScene comes up.
BTW here is my codes
import UIKit
import SpriteKit
import StoreKit
import GameplayKit
import GoogleMobileAds
var reklameNummer = 0
class GameViewController: UIViewController, GADBannerViewDelegate, GADRewardBasedVideoAdDelegate {
var rewardBaseAd: GADRewardBasedVideoAd!
var interstitial: GADInterstitial!
#IBOutlet weak var bannerView: GADBannerView!
override func viewDidLoad() {
super.viewDidLoad()
let request2 = GADRequest()
request2.testDevices = [kGADSimulatorID]
bannerView.delegate = self
bannerView.adUnitID = "ca-app-pub-1110799225910030/5762940412"
bannerView.rootViewController = self
//bannerView.load(request2)
interstitial = GADInterstitial(adUnitID: "ca-app-pub-1110799225910030/7460037600")
let request = GADRequest()
interstitial.load(request)
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "MenuScene") {
runTheInterStitialAd()
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
rewardBaseAd = GADRewardBasedVideoAd.sharedInstance()
rewardBaseAd.delegate = self
//rewardBaseAd.load(GADRequest(), withAdUnitID: "ca-app-pub-1110799225910030/4904503594")
}
view.ignoresSiblingOrder = true
view.showsFPS = false
view.showsNodeCount = false
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
//MARK: Video ad
func rewardBasedVideoAd(_ rewardBasedVideoAd: GADRewardBasedVideoAd,
didRewardUserWith reward: GADAdReward) {
}
func rewardBasedVideoAdDidReceive(_ rewardBasedVideoAd:GADRewardBasedVideoAd) {
//print.text?.append("Reward based video ad is received.\n")
}
func rewardBasedVideoAdDidOpen(_ rewardBasedVideoAd: GADRewardBasedVideoAd) {
//print.text?.append("Opened reward based video ad.\n")
}
func rewardBasedVideoAdDidStartPlaying(_ rewardBasedVideoAd: GADRewardBasedVideoAd) {
//print.text?.append("Reward based video ad started playing.\n")
}
func rewardBasedVideoAdDidClose(_ rewardBasedVideoAd: GADRewardBasedVideoAd) {
}
func rewardBasedVideoAdWillLeaveApplication(_ rewardBasedVideoAd: GADRewardBasedVideoAd) {
//print.text?.append("Reward based video ad will leave application.\n")
}
func rewardBasedVideoAd(_ rewardBasedVideoAd: GADRewardBasedVideoAd,
didFailToLoadWithError error: Error) {
//print.text?.append("Reward based video ad failed to load.\n")
}
//MARK: Interstitial ad
func runTheInterStitialAd(){
var runFunc = SKAction.run(showInterstitialAdInScene)
var wait = SKAction.wait(forDuration: 1)
var sequence = SKAction.sequence([wait, runFunc])
}
func showInterstitialAdInScene() {
print("this code is working")
if var scene = SKScene(fileNamed: "Gameoverscene") {
// TRUE
if (interstitial.isReady) == true{
interstitial.present(fromRootViewController: self)
gameNumber = 0
}
// FALSE
if (interstitial.isReady) == false{
interstitial = GADInterstitial(adUnitID: "ca-app-pub-3940256099942544/1033173712")
let request = GADRequest()
interstitial.load(request)
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if rewardBaseAd.isReady{
if reklameNummer == 1{
reklameNummer += 1
//rewardBaseAd.present(fromRootViewController: self)
//rewardBaseAd.load(GADRequest(), withAdUnitID: "ca-app-pub-1110799225910030/4904503594")
}
}
if rewardBaseAd.isReady{
if reklameNummer == 2{
reklameNummer = 0
//rewardBaseAd.present(fromRootViewController: self)
//rewardBaseAd.load(GADRequest(), withAdUnitID: "ca-app-pub-1110799225910030/4904503594")
}
}
if gameNumber == 2{
//showInterstitialAdInScene()
}
}
}
One way to do this is use a callback, which is a way to send the class that created a new class(scene) to that new class(scene) as an argument. This way the new class(scene) can call functions from the old class. Beneath is how to implement this in your code. First I make a custom initialiser which takes in the creating class as an argument and assigns it to a local variable(myCreator). Then in the function called when a player dies(deathFunction()), I call the runIntersitialAd() function from the creating class. I also changed the code which sets up the MenuScene from the GameViewController to send itself(GameViewController class) as an argument inside the MenuScene initialisation.
Change your scene class to something like this:
class MenuScene {
var myCreator : GameViewController!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
convenience init(fileNamed: String, VCwhoCreatedMe: GameViewController) {
self.init(fileNamed: fileNamed, VCwhoCreatedMe: VCwhoCreatedMe)
myCreator = VCwhoCreatedMe
}
....your other code
deathFunction() { //whatever code you use when the player dies
myCreator.runTheInterStitialAd() //because of the callback construction this is now possible.
...other code
}
}
Inside your GameViewController, function viewDidLoad():
if let view = self.view as! SKView? {
if let scene = MenuScene(fileNamed: "MenuScene",VCwhoCreatedMe: self ) {
runTheInterStitialAd()
scene.scaleMode = .aspectFill
view.presentScene(scene)
..... other code
}
}

How to detect the current scene in Swift SpriteKit app

In my SpriteKit app I've 3 different scenes and I want to set a specific settings for each scene.
I mean I want set visible an AdBanner in MainPage and SomeInfoScene but not in GameScene. How can I do this?
This is my code:
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
let mainPage = SKScene(fileNamed: "MainPage")!
mainPage.name = "MainPage"
let someInfoPage = SKScene(fileNamed: "SomeInfoScene")!
someInfoPage.name = "SomeInfoScene"
let gameScene = SKScene(fileNamed: "GameScene")!
gameScene.name = "GameScene"
view.presentScene(mainPage)
if let currentScene = view.scene {
if currentScene.name == mainPage.name {
print("MainPage")
adBannerView.isHidden = false
}
if currentScene.name == someInfoPage.name {
print("SomeInfoScene")
adBannerView.isHidden = false
}
if currentScene.name == gameScene.name {
print("GameScene")
adBannerView.isHidden = true
}
}
}
}

error in viewcontroller (lldb) when presenting another view

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