How to detect the current scene in Swift SpriteKit app - swift

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

Related

tap gesture recognizer Xcode

I want to make a 3d game on Xcode, SceneKit. To test it out, I started a simple project about a box, that if you tapped, moves in X direction. My question is how do you make the tap gesture work in a 3d game? I've been trying to add it but it just doesn't work. Here is all the code that I've written in case I'm doing something wrong:
import UIKit
import QuartzCore
import SceneKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scene = SCNScene(named:"art.scnassets/Box.scn")
let newscene = self.view as! SCNView
newscene.scene = scene
let tap = UITapGestureRecognizer(target: self, action: #selector(taps(tap:)));newscene.addGestureRecognizer(tap)
tap.numberOfTapsRequired = 1
tap.numberOfTouchesRequired = 1
}
override var shouldAutorotate: Bool {
return true
}
override var prefersStatusBarHidden: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
#objc func taps(tap:UITapGestureRecognizer){
let newscene = self.view as! SCNView
let scene = SCNScene(named: "art.scnassets/Box.scn")
let box = scene?.rootNode.childNode(withName: "Box", recursively: true)
let place = tap.location(in: newscene)
let tapped = newscene.hitTest(place, options: [:])
if tapped.count > 0{
box?.runAction(SCNAction.repeatForever(SCNAction.moveBy(x: 5, y: 0, z: 0, duration: 1)))
}
}
}
Try this...
#objc func handleTap(recognizer: UITapGestureRecognizer)
{
if(data.isNavigationOff == true) { return } // No panel select if Add, Update, EndWave, or EndGame
if(gameMenuTableView.isHidden == false) { return } // No panel if game menu is showing
let location: CGPoint = recognizer.location(in: gameScene)
if(data.isAirStrikeModeOn == true)
{
let projectedPoint = gameScene.projectPoint(SCNVector3(0, 0, 0))
let scenePoint = gameScene.unprojectPoint(SCNVector3(location.x, location.y, CGFloat(projectedPoint.z)))
gameControl.airStrike(position: scenePoint)
}
else
{
let hitResults = gameScene.hitTest(location, options: hitTestOptions)
for vHit in hitResults
{
if(vHit.node.name?.prefix(5) == "Panel")
{
// May have selected an invalid panel or auto upgrade was on
if(gameControl.selectPanel(vPanel: vHit.node.name!) == false) { return }
return
}
}
}
}
If Airstrike - Tap drops a bomb on the screen where you touched, translating it to 3D
Else hittest looks for panels which may return multiple results

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

Accessing var from another class or scene in SpriteKit

I'm building a game in SpriteKit that includes a player to select a character out of 7 options. Scene 1 will include 7 images. Each image will represent one character. The player will select the character by pressing on the image selected and new scene 2 will be presented. I want the selected character image to appear on scene 2. I saved data of selected character in scene 1, but when scene 2 is presented, it always shows character 1 and not another. Please advise what is the correct line of code to access var from scene 1 and bring it over to scene 2.
import SpriteKit
class Scene1: SKScene {
var Player1 = SKSpriteNode()
var Player2 = SKSpriteNode()
var Player3 = SKSpriteNode()
var Player4 = SKSpriteNode()
var Player5 = SKSpriteNode()
var Player6 = SKSpriteNode()
var Player7 = SKSpriteNode()
var playerSelect1 = false
var playerSelect2 = false
var playerSelect3 = false
var playerSelect4 = false
var playerSelect5 = false
var playerSelect6 = false
var playerSelect7 = false
// in touches began..
if self.nodeAtPoint(location) == self.player1 {
playerSelect1 = true
NSUserDefaults.standardUserDefaults().setBool(true, forKey:"playerSelect1")
// then i present scene2
} else if self.nodeAtPoint(location) == self.player2 {
playerSelect2 = true
NSUserDefaults.standardUserDefaults().setBool(true, forKey:"playerSelect2")
//then i present scene2
} else if self.nodeAtPoint(location) == self.player3 {
playerSelect3 = true
NSUserDefaults.standardUserDefaults().setBool(true, forKey:"playerSelect3")
//then i present scene2
} else if self.nodeAtPoint(location) == self.player4 {
playerSelect4 = true
NSUserDefaults.standardUserDefaults().setBool(true, forKey:"playerSelect4")
//then i present scene2
// i didn't list all 7 characters to make code shorter
class Scene2: SKScene {
var playerSelected: Scene1()
// did move to view
var character1 = SKSpriteNode()
var character2 = SKSpriteNode()
var character3 = SKSpriteNode()
var character4 = SKSpriteNode()
//grab the selection value
let playerSelect1 = NSUserDefaults.standardUserDefaults().boolForKey("playerSelect1")
let playerSelect2 = NSUserDefaults.standardUserDefaults().boolForKey("playerSelect2")
let playerSelect3 = NSUserDefaults.standardUserDefaults().boolForKey("playerSelect3")
let playerSelect4 = NSUserDefaults.standardUserDefaults().boolForKey("playerSelect4")
if playerSelect1 == true {
self.addChild(character1)
} else if playerSelect2 == true {
self.addChild(character2)
} else if playerSelect3 == true {
self.addChild(character3)
} else if playerSelect4 == true {
self.addChild(character4)
}
}
About your question there are many ways you can follow to achieve what you want. Now I personally only use NSUserDefaults when I need to communicate with for example to a today extension. You might also want to save your data files at a later time so I thought to this:
class Settings: NSObject { // NSCoding in case you want to save your data
var playerGender: [Bool]! = [Bool]()
func encodeWithCoder(aCoder: NSCoder!) {
aCoder.encodeObject(playerGender, forKey: "playerGender")
}
init(coder aDecoder: NSCoder!) {
playerGender = aDecoder.decodeObjectForKey("playerGender") as! [Bool]
}
override init() {
super.init()
self.playerGender = Array(count:7, repeatedValue:false)
}
}
class GameManager: NSObject {
static let sharedInstance = GameManager()
var settings : Settings! = Settings()
}
class Scene1: SKScene {
let gameManager = GameManager.sharedInstance
var player : [SKSpriteNode]?
override func didMoveToView(view: SKView) {
// in touches began..
if self.nodeAtPoint(location) == self.player1 {
gameManager.settings.playerGender[0] = true
}
}
}
class Scene2: SKScene {
let gameManager = GameManager.sharedInstance
var character : [SKSpriteNode]?
override func didMoveToView(view: SKView) {
//grab the selection value
let playerSelect1 = gameManager.settings.playerGender[0] //it's true
}
}
Explaination:
Settings is a class where you can write your game settings, and in future , you can also save the property values to a file.
GameManager is a shared instance, you can call it where do you want.
So let's consider the first time the user selects player 1. You store the selected player as a Bool in NSUserDefaults. This value will persist in local storage until you change it, even after the app is closed. So the next time the user selects player 2, you set the Bool for playerSelect2. The problem is, that in Scene2 you start your if statement checking if playerSelect1 == true, which it is because you set it to true once and never changed it back. With your current method, you would have to set the desired playerSelect to true, but also reset all of the others to false. I would recommend changing your strategy to store a String corresponding to the selected player in NSUserDefaults. Something like this should get you started.
When you want to set the selected player:
if self.nodeAtPoint(location) == self.player1 {
NSUserDefaults.standardUserDefaults().setObject("playerSelect1", forKey: "selectedPlayer")
else if self.nodeAtPoint(location) == self.player2 {
NSUserDefaults.standardUserDefaults().setObject("playerSelect2", forKey: "selectedPlayer")
else if ...
When you want to determine which player was selected in Scene2:
//grab the value from local storage
let selectedPlayer = NSUserDefaults.standardUserDefaults().objectForKey("selectedPlayer") as! String
if selectedPlayer == "playerSelect1" {
//handle player 1 case
}
else if selectedPlayer == "playerSelect2" {
//handle player 2 case
}