I am making a game with SpriteKit in Swift and I am having trouble when displaying the ad. The ad shows up when the player dies, as it is supposed to, but instead of staying in the current scene (GameScene) when I close it, it switches out to the MainMenu scene for some reason. Actually, if you watch closely, you can see that the "game over" text appears and as the ad slides up to display, it switches to the MainMenu scene. I saw the post here and tried what the answers said (moving everything from ViewWillLayoutSubviews to ViewDidLoad, etc) and it didn't work. Any ideas? Thanks.
Here is all of the code involving the ad in GameViewController:
class GameViewController: UIViewController, UIAlertViewDelegate, GADInterstitialDelegate {
var musicPlayer = AVAudioPlayer()
var musicUrl = Bundle.main.url(forResource: "Loops2.mp3", withExtension: nil)
var interstitial: GADInterstitial!
override func viewDidLoad() {
super.viewDidLoad()
interstitial = loadAd()
NotificationCenter.default.addObserver(self, selector: #selector(self.playerDied), name: NSNotification.Name("ShowAd"), object: nil)
NotificationCenter.default.post(name: NSNotification.Name("ShowingAd"), object: nil)
super.viewWillLayoutSubviews()
let skView = self.view as! SKView
skView.ignoresSiblingOrder = true
skView.showsFPS = true
skView.showsNodeCount = true
let mainMenu = MainMenu()
mainMenu.scaleMode = .aspectFill
mainMenu.size = view.bounds.size
skView.presentScene(mainMenu)
}
func playerDied() {
if self.interstitial.isReady {
self.interstitial.present(fromRootViewController: self)
}
}
func loadAd() -> GADInterstitial {
let interstitial = GADInterstitial(adUnitID: "ca-app-pub-3940256099942544/1033173712")
interstitial.delegate = self
interstitial.load(GADRequest())
return interstitial
}
func interstitialDidDismissScreen(_ ad: GADInterstitial) {
NotificationCenter.default.post(name: NSNotification.Name("AdDismissed"), object: nil)
interstitial = loadAd()
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if let url = musicUrl {
try! musicPlayer = AVAudioPlayer(contentsOf: url)
musicPlayer.numberOfLoops = -1
musicPlayer.prepareToPlay()
musicPlayer.play()
}
}
I use the NotificationCenter to signal GameViewController when to display the ad. I don't think that the code from GameScene is needed, but if you think it would be helpful, just ask. Thanks!
Related
I made a game using spritekit and implement admob interstitial ad following the documentation provided by google. Everything works fine in the simulator, the ad presents fullscreen as expected in the right time. The problem is that when I run in my iPhone 6s the ad doesn't fill the entire screen, just a small part of it on a black screen. I've implemented all the admob code on my gameviewController and call it in a gameScene using a observer.
class GameViewController: UIViewController {
var interstitial: GADInterstitial!
override func viewDidLoad() {
super.viewDidLoad()
interstitial = createAndLoadInterstitial()
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.showIntersticialAd), name: Notification.Name("showIntersticialAd"), object: nil)
if let view = self.view as! SKView? {
let scene = MenuScene(size: view.bounds.size)
scene.scaleMode = .aspectFill
view.presentScene(scene)
view.ignoresSiblingOrder = true
view.showsFPS = false
view.showsNodeCount = false
}
}
#objc func showIntersticialAd() {
if interstitial.isReady {
interstitial.present(fromRootViewController: self)
} else {
print("Ad not ready")
}
}
}
extension GameViewController: GADInterstitialDelegate {
func createAndLoadInterstitial() -> GADInterstitial {
let interstitial = GADInterstitial(adUnitID: "ca-app-pub-4828696079960529/6898313363")
interstitial.delegate = self
interstitial.load(GADRequest())
return interstitial
}
func interstitialDidDismissScreen(_ ad: GADInterstitial) {
interstitial = createAndLoadInterstitial()
}
}
I've searched a lot in the internet and in the admob documentation but didn't figure out why this is happening.
#objc func showIntertialAds() {
if !interstitial.isReady{
interstitial.load(GADRequest())
} if interstitial.isReady {
interstitial.present(fromRootViewController:(UIApplication.shared.keyWindow?.rootViewController)!)
}
}
I've integrated a banner view into a scene within my application however I'm failing to integrate a Interstitial ad within another scene.
Here is my code:
import SpriteKit
import GameKit
import GoogleMobileAds
class GameOverMenu: SKScene, GKGameCenterControllerDelegate, UIAlertViewDelegate {
var viewController: GameViewController!
var interstitial: GADInterstitial!
var myTimer = Timer()
override func didMove(to view: SKView) {
createAndLoadInterstitial()
startMyTimer()
}
func createAndLoadInterstitial() {
interstitial = GADInterstitial(adUnitID: "...")
let request = GADRequest()
request.testDevices = [ kGADSimulatorID, "..." ]
interstitial.load(request)
}
func startMyTimer() {
myTimer = Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(GameOverMenu.myFunction), userInfo: nil, repeats: false)
}
func myFunction(){
if interstitial.isReady {
interstitial.present(fromRootViewController: viewController)
} else {
print("Ad wasn't ready")
}
}
It is failing when it tries to load with "fatal error: unexpectedly found nil while unwrapping an Optional value". The problem lies below as if the code is displayed like this and I load the GameOver scene when the application launches it works fine. How can I fix this?
if let view = self.view as! SKView? {
// Load the SKScene from 'MainMenu.sks'
if let scene = MainMenuScene(fileNamed: "MainMenu") {
scene.viewController = self
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
}
if let scene3 = GameOverMenu(fileNamed: "GameOver") {
scene3.viewController = self
// Set the scale mode to scale to fit the window
scene3.scaleMode = .aspectFill
view.presentScene(scene3)
}
The problem is that when you transition between 2 scenes you loose the reference to GameViewController e.g
scene3.viewController = self
thats why it only works when you launch the application.
You are also using ! on those properties
var viewController: GameViewController!
var interstitial: GADInterstitial!
so if they are nil you will crash. So you should alway use ? when you are not 100% sure that something is there.
var viewController: GameViewController?
var interstitial: GADInterstitial?
and than in your code such as "myFunction" you will use the "?" and "if let" to not crash when the properties are nil.
if let ad = interstitial, let vc = viewController, ad.isReady {
ad.present(fromRootViewController: vc)
} else {
print("Ad wasn't ready")
}
The general fix for your problem is that you should really move all your AdMob code directly into GameViewController. Than you can use something like NotificationCenter or delegation to forward a message from your scenes to your ViewController to show the ad. Its not really the best practice to reference your ViewController in your SKScenes.
So move all the ad code to your ViewController and than in GameViewController outside the class implementation create this extension for the notification key
extension Notification.Name {
static let showAd = Notification.Name(rawValue: "NotificationShowAd")
}
class GameViewController: UIViewController {...
Than in GameViewController in ViewDidLoad you can add the observer
override func viewDidLoad() {
super.viewDidLoad()
createAndLoadInterstitial()
NotificationCenter.default.addObserver(self, selector: #selector(myFunction), name: .showAd, object: nil)
....
}
Now whenever you need to show an ad from any of your SKScenes you can call this
NotificationCenter.default.post(name: .showAd, object: nil)
To make your life even easier have a look at my helper on GitHub
https://github.com/crashoverride777/SwiftyAds
hope this helps
How do I show an interstitial ad from admob every x times the user has died or every x times the user does something like presses a button? This is how I showed an interstitial ad on my GameScene and limited ad impressions with a simple if statement.
This will only work if you have the GoogleMobileAds.sdk and have imported the googlemobileads module into your GameViewController, and GameScene, OR GameOverScene.
I'll be showing you cross-scene ad implementation and programmatically limiting ad impressions.
First, in your GameViewController:
import GoogleMobileAds
class GameViewController: UIViewController, GADInterstitialDelegate {
var myAd = GADInterstitial()
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(GameViewController.loadAndShow), name: "loadAndShow", object: nil)
}
Create two functions at the bottom of your GameViewController:
func loadAndShow() {
myAd = GADInterstitial()
let request = GADRequest()
myAd.setAdUnitID("ca-app-pub-3940256099942544/4411468910")
myAd.delegate = self
myAd.loadRequest(request)
}
func interstitialDidReceiveAd(ad: GADInterstitial!) {
if (self.myAd.isReady) {
myAd.presentFromRootViewController(self)
}
}
You are done with GameViewController. Now head to GameOverScene or GameScene, whatever you need.
Create a global int variable:
var playCount = Int()
In your DidMoveToView say:
playCount = 1
This part is sort of confusing, kinda, not really. Go to your touchesBegan and find where you add actions to a button if it's pressed. For example, a resetGame button resets the scene. Add this there and increment the playButton Int like so:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches{
let location = touch.locationInNode(self)
if resetGame.containsPoint(location) {
restartScene()
playCount += 1
}
Last step. Add these two functions to the bottom of the scene you want to show interstitial ads in:
func displayAd() {
NSNotificationCenter.defaultCenter().postNotificationName("loadAndShow", object: nil)
}
func checkAd() {
if playCount % 4 == 0 {
displayAd()
}
}
}
Now every fourth time that the user presses the reset game button or dies, an interstitial ad should show up. I hope this helps.
EDIT: I forgot to tell you to call the checkAd() function. Call this function wherever your players dies. So if you have a Bool variable called died or gameover call it in the same spot. For example..
if died == true {
checkAd()
}
import UIKit
import SpriteKit
import GoogleMobileAds
var playCount = Int()
class GameViewController: UIViewController, GADBannerViewDelegate {
#IBOutlet var banner: GADBannerView!
var myAd = GADInterstitial()
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(GameViewController.loadAndShow), name: "loadAndShow", object: nil)
let scene = MainScene(size: CGSize(width: 1536, height: 2048))
// Configure the view.
let 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)
banner.hidden = true
banner.delegate = self
banner.adUnitID = "ca-app-pub-8889875503423788/7902691359"
banner.rootViewController = self
banner.loadRequest(GADRequest())
}
func loadAndShow() {
myAd = GADInterstitial()
let request = GADRequest()
myAd.setAdUnitID("ca-app-pub-8889875503423788/7902691359")
myAd.delegate = self
myAd.loadRequest(request)
}
func interstitialDidReceiveAd(ad: GADInterstitial!) {
if (self.myAd.isReady) {
myAd.presentFromRootViewController(self)
}
}
func adViewDidReceiveAd(bannerView: GADBannerView!) {
banner.hidden = false
}
func adView(bannerView: GADBannerView!, didFailToReceiveAdWithError error: GADRequestError!) {
banner.hidden = true
}
override func shouldAutorotate() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .AllButUpsideDown
} else {
return .All
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
Okay, I've spent days now doing this. I've gone through admob's tutorials, youtube, stack exchange, everything and I cannot find a solution to my problem.
I'm trying to implement admob's interstitial ads into my iOS game application. The code compiles, builds, runs, the game is played, but ads will not show up. This is my GameViewController.swift
import UIKit
import SpriteKit
import GoogleMobileAds
class GameViewController: UIViewController, GADInterstitialDelegate {
//AD STUFF-----------------------------vBELOWv----------------------------------------------
//AD STUFF-----------------------------vBELOWv----------------------------------------------
var interstitialAd: GADInterstitial?
func createInterstitialAd() -> GADInterstitial {
let request = GADRequest()
let interstitial = GADInterstitial(adUnitID: "ca-app-pub-******")
request.testDevices = [kGADSimulatorID, "C966DEAC-A2FD-4FBA-80B5-802B7E394C6D", "F3E1897A-3556-4704-9C85-3D70B4672F44","090DCEE5-6B1C-4DFB-83CE-FE800A242008", "1B93E43B-E9B3-4482-8B11-4F3614A26EA2"]
interstitial.delegate = self
interstitial.loadRequest(request)
return interstitial
}
//func to show the ad
func showAd() {
if interstitialAd != nil {
if interstitialAd!.isReady{
interstitialAd?.presentFromRootViewController(self)
}
else {print("found not ready")}
}
}
//func to pre-load new ad - calls automatically when closes an ad
func interstitialWillDismissScreen(ad: GADInterstitial!) {
interstitialAd = createInterstitialAd()
}
//AD STUFF--------------------------^ABOVE^--------------------------------------------------
//AD STUFF--------------------------^ABOVE^--------------------------------------------------
override func viewDidLoad() {
super.viewDidLoad()
interstitialAd = createInterstitialAd()
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* 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
scene.size = self.view.bounds.size
skView.presentScene(scene)
}
}
This is how I'm calling it to my GameScene
//makes the restart button appear
func createRestartBTN(){
restartBTN = SKSpriteNode(color: SKColor.clearColor(), size: CGSize(width: 200, height: 100))
restartBTN.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
restartBTN.zPosition = 10
addChild(restartBTN)
createReplayText()
varForGVC.showAd()
}
So when the game is over, a function makes a restart button appear. Then I want my interstitial ad to pop up so the user has to exit the interstitial ad to hit the replay button. That variable varForGVC is an implemented earlier in the GameScene.swift by varForGVC = GameViewController()
I have my app synced with Firebase and admob. I have the GoogleService-Info.plist in my root. I have the list of required frameworks for admob in my root as well. I have pod installed with Firebase. I've tried putting the code I'm using in my GameViewController into my GameScene.swift and it did not work there either. I don't know if this matters, but my game has a Main Screen, a Rules screen only accessible via the main screen, then the user hits a Play button to begin playing the game in another Screen (the GameScene). My check for .isReady does not return found not ready. I need some help with this issue. For now, I'm going to try to reinstall the frameworks and see if that's it.
This is how I work with interstitial ads from AdMob - using extension. I think it's way more clean solution. Hope it will help you!
GameViewController.swift
class GameViewController: UIViewController {
var interstitial: GADInterstitial?
override func viewDidLoad() {
createInterstitial()
}
func presentInterstitial() {
guard let interstitial = self.interstitial where interstitial.isReady else {
return
}
dispatch_async(dispatch_get_main_queue(), {
interstitial.presentFromRootViewController(self)
})
}
}
GameViewController+GADInterstitialDelegate.swift
import GoogleMobileAds
extension GameViewController: GADInterstitialDelegate {
func interstitial(ad: GADInterstitial!, didFailToReceiveAdWithError error: GADRequestError!) {
print("\(#function): \(error.localizedDescription)")
}
func interstitialDidDismissScreen(ad: GADInterstitial!) {
// Recycle interstitial
createInterstitial()
unpauseGame() // Some method from GameViewController
}
func interstitialWillPresentScreen(ad: GADInterstitial!) {
pauseGame() // Some method from GameViewController
}
func createInterstitial() {
interstitial = GADInterstitial(adUnitID: interstitialAdUnitID)
interstitial!.loadRequest(AdMobHelper.createRequest())
interstitial!.delegate = self
}
}
And the last part - AdMobHelper.swift
import GoogleMobileAds
let interstitialAdUnitID = "Some Id"
class AdMobHelper {
static func createRequest() -> GADRequest {
let request = GADRequest()
request.testDevices = ["Check this in your logs"]
return request
}
}
I figured it out finally. I used NSNotifcationCenter. I put in an observer in my GameViewController.swift in my viewDidLoad function, then a receiver where I wanted it to pop up.
I made simple game with Swift and SpriteKit. Everything seems to work but I have a problem with my iAd Setup. I only want to show the ad banner in specific scenes (main menu and game over) not during the gameplay.
My iAd setup works but only if it is displayed all the time.
My last attempt to fix it was with the NSNotificationCenter method. I have done it as I have seen it in other question/answers here but the app crashes immediately after launch.
Hope that someone could help me. Just let me know if you need more of my code.
Thanks in advance.
GameViewController.swift
class GameViewController: UIViewController, ADBannerViewDelegate {
var adBannerView = ADBannerView(frame: CGRect.zeroRect)
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleNotification", name: "hideAd", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleNotification", name: "showAd", object: nil)
self.adBannerView.delegate = self
self.adBannerView.hidden = true
adBannerView.center = CGPoint(x: adBannerView.center.x, y: view.bounds.size.height - adBannerView.frame.size.height / 2)
view.addSubview(adBannerView)
if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
// Configure the view.
let skView = self.view as SKView
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
var scene: SKScene = MainMenu(size: skView.bounds.size)
scene.scaleMode = SKSceneScaleMode.AspectFill
skView.presentScene(scene)
}
}
func handleNotification(notification: NSNotification){
if notification.name == "hideAd"{
adBannerView.hidden = true
}else if notification.name == "showAd"{
adBannerView.hidden = false
}
}
//iAD Setup
func bannerViewWillLoadAd(banner: ADBannerView!) {
println("Ad loads")
}
func bannerViewDidLoadAd(banner: ADBannerView!){
println("Ad loaded")
self.adBannerView.hidden = false
}
func bannerViewActionDidFinish(banner: ADBannerView!) {
println("resume scene")
let skView = self.view as SKView
skView.paused = false
}
func bannerViewActionShouldBegin(banner: ADBannerView!, willLeaveApplication willLeave: Bool) -> Bool {
println("pause scene")
let skView = self.view as SKView
skView.paused = true
return true
}
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
println("Failed to load ad")
self.adBannerView.hidden = true
} more code....
MainMenu.swift
class MainMenu: SKScene{
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(size:CGSize){
super.init(size: size)
NSNotificationCenter.defaultCenter().postNotificationName("showAd", object: nil)
more code...
This works for me
In my view controller I have this code:
// Banner Ad
var SH = UIScreen.mainScreen().bounds.height
let transition = SKTransition.fadeWithDuration(1)
var UIiAd: ADBannerView = ADBannerView()
override func viewWillAppear(animated: Bool) {
var BV = UIiAd.bounds.height
UIiAd.delegate = self
UIiAd.frame = CGRectMake(0, SH + BV, 0, 0)
self.view.addSubview(UIiAd)
}
override func viewWillDisappear(animated: Bool) {
UIiAd.delegate = nil
UIiAd.removeFromSuperview()
}
func bannerViewDidLoadAd(banner: ADBannerView!) {
var BV = UIiAd.bounds.height
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(1) // Time it takes the animation to complete
UIiAd.alpha = 1 // Fade in the animation
UIView.commitAnimations()
}
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(1)
UIiAd.alpha = 0
UIView.commitAnimations()
}
func showBannerAd() {
UIiAd.hidden = false
var BV = UIiAd.bounds.height
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(1) // Time it takes the animation to complete
UIiAd.frame = CGRectMake(0, SH - BV, 0, 0) // End position of the animation
UIView.commitAnimations()
}
func hideBannerAd() {
UIiAd.hidden = true
var BV = UIiAd.bounds.height
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(1) // Time it takes the animation to complete
UIiAd.frame = CGRectMake(0, SH + BV, 0, 0) // End position of the animation
UIView.commitAnimations()
}
override func viewDidLoad() {
super.viewDidLoad()
self.UIiAd.hidden = true
self.UIiAd.alpha = 0
NSNotificationCenter.defaultCenter().addObserver(self, selector: "hideBannerAd", name: "hideadsID", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "showBannerAd", name: "showadsID", object: nil)
let scene = GameScene()
// Configure the view.
let 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 = .AspectFit
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
scene.size = skView.bounds.size
skView.presentScene(scene, transition: transition)
}
To display it in the scene I want (for me it's the GameOver scene) I did this:
override func didMoveToView(view: SKView) {
showAds()
// Rest of your code here...
}
func showAds(){
NSNotificationCenter.defaultCenter().postNotificationName("showadsID", object: nil)
}
Basically this code creates a banner off the scene, and when the banner is loaded and the scene gets called, it slides up from the bottom. When you switch scenes, it offsets it again and when you come back to that scene, it slides up from the bottom again. If the banner doesn't load, it's off screen so no worries about it showing a white bar. Also, it sets the alpha to 0 if it doesn't load just in case (makes it invisible). Hope this helps.
I USE THIS TO PRESENT THE iAd Banner in subview
var vc = self.view?.window?.rootViewController
vc?.view.addSubview(adView!)
When I want to hide it when in the game (because iAd reduce FPS) I call
adView!.removeFromSuperview()
When the user finished the Game just add
var vc = self.view?.window?.rootViewController
vc?.view.addSubview(adView!)
to show the iAd banner again
BUT YOU NEED TO IMPLEMENT THIS CODE IN GameViewController
class GameViewController: ADBannerViewDelegate (you needed this too)
also don't forget to import iAd at the top of gameviewcontroller
also import iAd framework to the project
override func viewDidLoad() {
self.ShowAd()
//Have this in view didLoad too
func ShowAd() {
super.viewDidLoad()
adView = ADBannerView(adType: ADAdType.Banner)
adView!.delegate = self
}
func bannerViewDidLoadAd(banner: ADBannerView!) {
self.view.addSubview(adView!)
self.view.layoutIfNeeded()
println("Pass")
}
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
adView!.removeFromSuperview()
/*var alert = UIAlertController(title: "iAd Message", message:
"iAd Fail To Load", preferredStyle: UIAlertControllerStyle.Alert)
self.presentViewController(alert, animated: false, completion: nil)
alert.addAction(UIAlertAction(title: "dismiss", style: UIAlertActionStyle.Default,
handler: nil))
self.view.layoutIfNeeded()*/
println("fail")
}