Transition between scenes is slow on first time - ios

I tried to move between GameScene to SettingsScene and is works perfect expect first time.
In first time the game is "stuck" for 1-3 seconds and presented the SettingsScene without the transition, after first time the transition and game works perfect.
The back (between SettingsScene to GameScene) works good also in first time.
The transition code between GameScene to SettingsScene
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
self.removeAllActions()
// Get the location of the touch in this scene
let location = touch.location(in: self)
// Check if the location of the touch is within the button's bounds
//Play Button
if PlayButton.contains(location) {
bannerView.isHidden = true
let newScene = PlayScene(size: (view?.bounds.size)!)
newScene.scaleMode = .aspectFill
let reveal = SKTransition.crossFade(withDuration: 1.0)
self.view?.presentScene(newScene, transition: reveal)
//Settings Button
}else if SettingsButton.contains(location) {
bannerView.isHidden = true
settingsscene?.scaleMode = .resizeFill
let reveal = SKTransition.push(with: SKTransitionDirection.down, duration: 0.7)
self.view?.presentScene(settingsscene!, transition: reveal)
}
}
}
GameScene:
var settingsscene: SKScene?
override func didMove(to view: SKView) {
/* Setup your scene here */
//
let size = self.size
DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async {
[weak self] () -> Void in
self?.settingsscene = SettingsScene(size: (view.bounds.size))
}
SettingsScene:
override func didMove(to view: SKView) {
//Settings Background
settingsBackground.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
settingsBackground.zPosition = -1
}
override init(size: CGSize) {
super.init()
//Settings Label
settingsLabel.text = "Settings"
settingsLabel.fontName = "AvenirNext-Bold"
settingsLabel.fontColor = UIColor.white
settingsLabel.horizontalAlignmentMode = .center
settingsLabel.verticalAlignmentMode = .center
adjustLabelFontSizeToFitRect(labelNode: settingsLabel, rect: CGRect(x:self.frame.midX, y:self.frame.midY, width: 180, height: 110), center: true)
settingsLabel.setScale(self.frame.width * 0.001)
settingsLabel.position = CGPoint(x: self.frame.midX, y: self.frame.maxY - settingsLabel.frame.height)
settingsLabel.zPosition = 1
addChild(settingsLabel)
/**** Labels ****/
//Sounds Label
soundsLabel.text = "Sounds"
soundsLabel.fontName = "AvenirNext"
soundsLabel.fontColor = UIColor.white
adjustLabelFontSizeToFitRect(labelNode: soundsLabel, rect: CGRect(x:self.frame.midX, y:settingsLabel.position.y - 20, width:110, height: 70), center: true)
soundsLabel.setScale(self.frame.width * 0.001)
soundsLabel.zPosition = 1
//Rate Label
rateLabel.text = "Rate Game"
rateLabel.fontName = "AvenirNext"
rateLabel.fontColor = UIColor.white
adjustLabelFontSizeToFitRect(labelNode: rateLabel, rect: CGRect(x:self.frame.midX, y:50, width:120, height: 110), center: true)
rateLabel.setScale(frame.width * 0.001)
rateLabel.zPosition = 1
//Twitter Label (Follow me)
twiterLabel.text = "Follow me"
twiterLabel.fontName = "AvenirNext"
twiterLabel.fontColor = UIColor.white
adjustLabelFontSizeToFitRect(labelNode: twiterLabel, rect: CGRect(x:self.frame.midX, y:10, width:17, height: 110), center: true)
twiterLabel.setScale(frame.width * 0.001)
twiterLabel.zPosition = 1
//iAP Label (Remove Ads)
iapLabel.text = "Remove Ads"
iapLabel.fontName = "AvenirNext"
iapLabel.fontColor = UIColor.white
adjustLabelFontSizeToFitRect(labelNode: iapLabel, rect: CGRect(x:self.frame.midX, y:70, width:40, height: 110), center: true)
iapLabel.setScale(frame.width * 0.001)
iapLabel.zPosition = 1
//Restore iAP Label
restoreLabel.text = "Restore Purchase"
restoreLabel.fontName = "AvenirNext"
restoreLabel.fontColor = UIColor.white
adjustLabelFontSizeToFitRect(labelNode: restoreLabel, rect: CGRect(x:self.frame.midX, y:308, width:102, height: 110), center: true)
restoreLabel.setScale(frame.width * 0.001)
restoreLabel.zPosition = 1
//About Label
aboutLabel.text = "Restore Purchase"
aboutLabel.fontName = "AvenirNext"
aboutLabel.fontColor = UIColor.white
adjustLabelFontSizeToFitRect(labelNode: aboutLabel, rect: CGRect(x:self.frame.midX, y:18, width:123, height: 312), center: true)
aboutLabel.setScale(frame.width * 0.001)
aboutLabel.zPosition = 1
//Back Button
backButton.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
backButton.zPosition = 1
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func SettingsSetup() {
addChild(settingsBackground)
addChild(myLabels)
addChild(backButton)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch:AnyObject in touches {
let location = touch.location(in:self)
//BackButton Action
if backButton.contains(location) {
let newScene = GameScene(fileNamed: "GameScene")
newScene?.scaleMode = .aspectFill
let reveal = SKTransition.push(with: SKTransitionDirection.up, duration: 0.7)
self.view?.presentScene(newScene!, transition: reveal)
}
}
}
How can I fix it?

Related

UIButton: Click sound when Button is clicked in swift

I am trying to play a sound when the UIButton: "StartButton" is clicked. However, I get an error that reads:
Argument type 'CGPoint' does not conform to expected type 'UIFocusEnvironment'
I created the button as below and assigned a CGRect poisiton to it, and I am trying to use the touchesBeagn function to detect the location of the touch and determine if it matches the location of the Button and if so call playButtonSound() function.
This is my code in the MainMenu.swift file:
// MainMenuScene.swift
import Foundation
import SpriteKit
import AVFoundation
let startButton:UIButton = UIButton(type: UIButtonType.roundedRect)
class MainMenuScene: SKScene
{
var buttonSound = AVAudioPlayer()
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "background")
background.size = self.size
background.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
background.zPosition = 0
self.addChild(background)
let menuLabel1 = SKLabelNode(fontNamed: "The Bold Font")
menuLabel1.text = "Hit"
menuLabel1.position = CGPoint(x: self.size.width * 0.45, y: self.size.height * 0.78)
menuLabel1.fontSize = 300
menuLabel1.fontColor = UIColor.yellow
menuLabel1.colorBlendFactor = 1
menuLabel1.zPosition = 1
self.addChild(menuLabel1)
let menuLabel2 = SKLabelNode(fontNamed: "The Bold Font")
menuLabel2.text = "&"
menuLabel2.position = CGPoint(x: self.size.width * 0.55, y: self.size.height * 0.64)
menuLabel2.fontSize = 350
menuLabel2.fontColor = UIColor.red
menuLabel2.colorBlendFactor = 1
menuLabel2.zPosition = 1
self.addChild(menuLabel2)
let menuLabel3 = SKLabelNode(fontNamed: "The Bold Font")
menuLabel3.text = "Go"
menuLabel3.position = CGPoint(x: self.size.width * 0.60, y: self.size.height * 0.52)
menuLabel3.fontSize = 300
menuLabel3.fontColor = UIColor.yellow
menuLabel3.colorBlendFactor = 1
menuLabel3.zPosition = 1
self.addChild(menuLabel3)
startButton.frame = CGRect(x: (self.view?.center.x)! * 0.5 , y: (self.view?.center.y)! * 1.5 , width: 200, height: 50)
startButton.backgroundColor = .red
startButton.setTitleColor(.white, for: .normal)
startButton.titleLabel?.adjustsFontSizeToFitWidth = true
startButton.layer.cornerRadius = 10
startButton.clipsToBounds = true
startButton.setTitle("Start Game", for: .normal)
startButton.titleLabel!.font = UIFont(name: "The Bold Font", size: 60)
startButton.addTarget(self, action:#selector(self.startButtonClicked), for: .touchUpInside)
self.view?.addSubview(startButton)
}
#objc func startButtonClicked() {
let sceneToMoveTo = GameScene(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTransition = SKTransition.reveal(with: .right , duration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTransition)
}
#objc func playButtonSound(filename: String) {
let url = Bundle.main.url(forResource: "button.mp3" , withExtension: nil)
guard let newURL = url else {
print("Could not find file: \(filename)")
return
}
do {
buttonSound = try AVAudioPlayer(contentsOf: newURL)
buttonSound.numberOfLoops = -1
buttonSound.prepareToPlay()
buttonSound.setVolume(0.50, fadeDuration: 0.1)
buttonSound.play()
} catch let error as NSError {
print(error.description)
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
var pointOfTouch = touch.location(in: self)
if startButton.contains(pointOfTouch)
{
playButtonSound(filename: "button.mp3")
}
}
}
}
What is the reason for the error?
The problem is that you are calling the wrong contains method.
You are calling the method on UIButton, which has this signature:
public func contains(_ environment: UIFocusEnvironment) -> Bool
What you probably meant to call was the method on the button's frame:
if startButton.frame.contains(pointOfTouch)
You may want to read the docs to make sure that using the frame is what you want.

TouchesBegan does not work

In the last label lblTryAgain I want to return to another class named GameScene but the tap action does not enter to the function touchesBegan.
I am just following a tutorial to learn how to create games with SpriteKit if someone want to follow the tutorial or see the entire code is available in https://www.raywenderlich.com/87231/make-game-like-mega-jump-sprite-kit-swift-part-1
import SpriteKit
class EndGameScene: SKScene {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(size: CGSize) {
super.init(size: size)
// Stars
let star = SKSpriteNode(imageNamed: "Star")
star.position = CGPoint(x: 25, y: self.size.height-30)
addChild(star)
let lblStars = SKLabelNode(fontNamed: "ChalkboardSE-Bold")
lblStars.fontSize = 30
lblStars.fontColor = SKColor.white
lblStars.position = CGPoint(x: 50, y: self.size.height-40)
lblStars.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.left
lblStars.text = String(format: "X %d", GameState.sharedInstance.stars)
addChild(lblStars)
// Score
let lblScore = SKLabelNode(fontNamed: "ChalkboardSE-Bold")
lblScore.fontSize = 60
lblScore.fontColor = SKColor.white
lblScore.position = CGPoint(x: self.size.width / 2, y: 300)
lblScore.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.center
lblScore.text = String(format: "%d", GameState.sharedInstance.score)
addChild(lblScore)
// High Score
let lblHighScore = SKLabelNode(fontNamed: "ChalkboardSE-Bold")
lblHighScore.fontSize = 30
lblHighScore.fontColor = SKColor.cyan
lblHighScore.position = CGPoint(x: self.size.width / 2, y: 150)
lblHighScore.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.center
lblHighScore.text = String(format: "High Score: %d", GameState.sharedInstance.highScore)
addChild(lblHighScore)
// Try again
let lblTryAgain = SKLabelNode(fontNamed: "ChalkboardSE-Bold")
lblTryAgain.fontSize = 30
lblTryAgain.fontColor = SKColor.white
lblTryAgain.position = CGPoint(x: self.size.width / 2, y: 50)
lblTryAgain.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.center
lblTryAgain.text = "Tap To Try Again"
lblTryAgain.isUserInteractionEnabled = true
addChild(lblTryAgain)
}
func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
// Transition back to the Game
let reveal = SKTransition.fade(withDuration: 0.5)
let gameScene = GameScene(size: self.size)
self.view!.presentScene(gameScene, transition: reveal)
}
}
touchesBegan is a override func which only works in self.viewand it will not work in other UIs
try this
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("working")
}
make sure isUserInteractionEnabled is checked and the print statement may not work. add or update sceneDidLoad and print isUserInteractionEnabled

No sound without any errors

I tried to add sound when button pressed but when I press the button no sound. No mistakes in terminal.
I already tried compile on another computer, and debug on iPhone.
No sound persist.
Please help me solve this problem.
class MenuScene : SKScene {
let txtAtlas : SKTextureAtlas = SKTextureAtlas(named: "UI.atlas")
let txtAtlas2 : SKTextureAtlas = SKTextureAtlas(named: "mapbc.atlas")
let startBut = SKSpriteNode()
let tssound = SKAction.playSoundFileNamed("tssound.wav", waitForCompletion: false)
override func didMove(to view: SKView) {
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.backgroundColor = UIColor.black
let logo = SKLabelNode(fontNamed: "Helvetica")
logo.text = "Train Control"
logo.position = CGPoint(x: 0, y: 100)
logo.fontSize = 60
logo.fontColor = UIColor.green
self.addChild(logo)
startBut.texture = txtAtlas.textureNamed("stbut.png")
startBut.size = CGSize(width: 230, height: 85)
startBut.position = CGPoint(x: 0, y: -20)
startBut.name = "StartBtn"
self.addChild(startBut)
let startText = SKLabelNode(fontNamed: "AvenirNext-HeavyItalic")
startText.text = "Start Manage!"
startText.verticalAlignmentMode = .center
startText.position = CGPoint(x: 0, y: 2)
startText.fontSize = 30
startText.name = "StartBtn"
startBut.addChild(startText)
let bcmap = SKSpriteNode()
bcmap.texture = txtAtlas2.textureNamed("mapbc.png")
bcmap.zPosition = -1
bcmap.size = CGSize(width: 400.0, height: 350.0)
bcmap.position = CGPoint(x: -100, y: 0)
bcmap.alpha = 0.7
self.addChild(bcmap)
let stbutaction = SKAction.sequence([SKAction.fadeAlpha(to: 0.7, duration: 0.9),
SKAction.fadeAlpha(to: 1.0, duration: 0.9)])
startBut.run(SKAction.repeatForever(stbutaction))
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches) {
let location = touch.location(in: self)
let nodeTouched = atPoint(location)
if nodeTouched.name == "StartBtn" {
// Player touched the start text or button node
self.view?.presentScene(GameScene(size: self.size))
self.run(tssound)
}
}
}
}
You should probably initialise and run your sound inside GameScene e.g. when the scene loads. You are trying to run the sound on MenuScene which doesn't exist after you have transitioned to GameScene

How to recognise which image was touched

I am developing an application which the user will be able to drag and drop items on a canvas and when he releases the image it is drawn on the canvas.
This is my DragImage class which handle the touches:
class DragImages: UIImageView {
var originalPos : CGPoint!
var dropTarget: UIView?
override init (frame : CGRect){
super.init(frame: frame)
}
required init?(coder aDecoder : NSCoder){
super.init(coder : aDecoder)
}
override func touchesBegan(_ touches : Set<UITouch>,with event: UIEvent?){
originalPos = self.center
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first{
let position = touch.location(in: self.superview)
self.center = CGPoint(x : position.x, y : position.y)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first, let target = dropTarget{
let position = touch.location(in: self.superview)
if target.frame.contains(position){
NotificationCenter.default.post(Notification(name: Notification.Name(rawValue: "onTargetDropped"), object: nil))
}else {
self.center = originalPos
}
}
print(self.center.x, self.center.y)
self.center = originalPos
}
func getEndPosX() -> CGFloat{
return self.center.x
}
func getEndPosY() -> CGFloat {
return self.center.y
}
}
In my ViewController class I added this piece of code to handle the touches etc:
ornament1.dropTarget = xmasTree
ornament2.dropTarget = xmasTree
ornament3.dropTarget = xmasTree
ornament4.dropTarget = xmasTree
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.itemDroppedOnTree(_:)), name: NSNotification.Name(rawValue: "onTargetDropped"), object: nil)
}
func itemDroppedOnTree(_ notif : AnyObject){
}
I managed to get the X and Y position when the image is dragged on the canvas but i cant find a way to recognise which of the 4 images is being dropped in order for me to draw that specific one!
You could add the sender to your notification (and also the position):
NotificationCenter.default.post(Notification(name: Notification.Name(rawValue: "onTargetDropped"), object: self, userInfo: ["position":position]))
and get it later in itemDroppedOnTree:
func itemDroppedOnTree(_ notif : NSNotification){
let position = notif.userInfo["position"]
let sender = notif.object as! DragImage
if sender === dragImage1 {
//...
} else if sender === dragImage2 {
//...
}
}
I recommend against it though and plead to use a delegate to inform the ViewController instead. (Opinion based: In general, use Notifications for to-many broadcasts only.)
The delegate function should have the sender as first parameter. According to func tableView: tableView:UITableView, cellForRowAt indexPath:IndexPath).
This way you know which image is sending its new position and can compare it to your property like in the above example:
if dragImage === dragImage1 {...
Your code plus working delegate to paste to Playground:
import UIKit
import PlaygroundSupport
protocol DragImageDelegate: class {
func dragimage(_ dragImage:DragImage, didDropAt position:CGPoint)
}
class DragImage: UIImageView {
weak var delegate: DragImageDelegate?
var originalPos : CGPoint!
var dropTarget: UIView?
override init (frame : CGRect) {
super.init(frame: frame)
isUserInteractionEnabled = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(_ touches : Set<UITouch>,with event: UIEvent?){
originalPos = self.center
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first{
let position = touch.location(in: self.superview)
self.center = CGPoint(x : position.x, y : position.y)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first, let target = dropTarget {
let position = touch.location(in: self.superview)
if target.frame.contains(position){
print(self.center.x, self.center.y)
guard let delegate = self.delegate else {
print("delegate not set")
return
}
print(self.center.x, self.center.y)
delegate.dragimage(self, didDropAt: position)
return
}
}
self.center = originalPos
}
}
class MyVC: UIViewController, DragImageDelegate {
let dragImage1 = DragImage(frame: CGRect(x: 0.0, y: 0.0, width: 30.0, height: 30.0))
let dragImage2 = DragImage(frame: CGRect(x: 0.0, y: 100.0, width: 30.0, height: 30.0))
override func viewDidLoad() {
let target = UIView(frame: CGRect(x: 200.0, y: 400.0, width: 30.0, height: 30.0))
target.backgroundColor = .black
view.addSubview(target)
dragImage1.backgroundColor = .white
dragImage2.backgroundColor = .white
dragImage1.dropTarget = target
dragImage2.dropTarget = target
view.addSubview(dragImage1)
view.addSubview(dragImage2)
dragImage1.delegate = self
dragImage2.delegate = self
}
private func move(_ view:UIView, to position:CGPoint) {
view.frame = CGRect(x: position.x, y: position.y, width: view.frame.size.width, height: view.frame.size.height)
}
// MARK: - DragImageDelegate
func dragimage(_ dragImage: DragImage, didDropAt position: CGPoint) {
if dragImage === dragImage1 {
move(dragImage1, to: position)
} else if dragImage === dragImage2 {
move(dragImage2, to: position)
}
}
}
var container = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 300.0, height: 600.0))
let myVc = MyVC()
myVc.view.frame = CGRect(x: 0.0, y: 0.0, width: 300.0, height: 600.0)
myVc.view.backgroundColor = .green
container.addSubview(myVc.view)
PlaygroundPage.current.liveView = container
Result:

Game Center "Done" button issue?

The "Done" button in Game Center will not dismiss the controller. My code to dismiss the view controller is exactly the same everywhere I look. I've looked at similar questions on here but no luck. Can anyone help me?
GameScene.Swift (This serves as my start menu )
class GameScene: SKScene, GKGameCenterControllerDelegate {
var startBtn = SKSpriteNode(imageNamed: "play button")
var title = SKLabelNode(fontNamed: "AvenirNext-UltraLight")
var leaderboardButtonImage = SKSpriteNode(imageNamed: "leaderboard button")
override func didMoveToView(view: SKView) {
let bounds = UIScreen.mainScreen().bounds
self.scene?.size = CGSize(width: bounds.size.width, height: bounds.size.height)
scene?.backgroundColor = UIColor.whiteColor()
startBtn.position = CGPoint(x: self.frame.width / 2 - 100, y: self.frame.height / 2)
startBtn.size = CGSize(width: 140, height: 55)
self.addChild(startBtn)
leaderboardButtonImage.position = CGPoint(x: self.frame.width / 2 + 100, y: self.frame.height / 2)
leaderboardButtonImage.size = CGSize(width: 140, height: 55)
self.addChild(leaderboardButtonImage)
}
func showLeaderboard() {
let vc = self.view?.window?.rootViewController
let gc = GKGameCenterViewController()
gc.viewState = GKGameCenterViewControllerState.Leaderboards
gc.leaderboardIdentifier = "I removed this part"
vc!.presentViewController(gc, animated: true, completion: nil)
}
func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController) {
gameCenterViewController.dismissViewControllerAnimated(true, completion: nil)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in (touches ) {
let location = touch.locationInNode(self)
if (startBtn.containsPoint(location)) {
self.view?.presentScene(GamePlayScene(), transition: SKTransition.crossFadeWithDuration(1.0))
GamePlayScene().scaleMode = .ResizeFill
startBtn.removeFromParent()
title.removeFromParent()
leaderboardButtonImage.removeFromParent()
}
if (leaderboardButtonImage.containsPoint(location)) {
showLeaderboard()
}
}
}
}
In my func showLeaderboard(), I had to add the line gc.gameCenterDelegate = self which made the "Done" button work.
I hope other people who have this same problem see this potential fix.
func showLeaderboard() {
let vc = self.view?.window?.rootViewController
let gc = GKGameCenterViewController()
gc.viewState = GKGameCenterViewControllerState.Leaderboards
gc.gameCenterDelegate = self
gc.leaderboardIdentifier = "I removed this part"
vc!.presentViewController(gc, animated: true, completion: nil)
}

Resources