How to position SKTilemap Scene in the middle of screen in Swift - 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)
}
}
//...
}

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 solve that SKTransition effect doesn't work during transiting?

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.

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
}
}
}
}

how to access entity from code - gameplay kit

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