Search code examples
iosiphoneswifttouchesbegan

Implementing a game with segues, crashing when restarted. Swift


I am developing a game where the boat and stone collide. The game is over When the boat collides the stone. I implemented intersects() method for the intersection of boat and stone. For each coin I collect, I increment the score by +1. I am performing a segue when both boat and stone collide. The segue is performed and the game score is displayed with the restart option. Everything is smooth until I restart the game and when the game restarted there are some problems. The following are the problems :

At first the game is smooth, the GameOver controller is shown when the collision happens but when I choose to restart.

Problem #1 When the boat collides the stone, the segue is shown twice and after restarting again

Problem #2 When I am playing for the 3rd time it does not detect an intersection and it is throwing an error

Here is the screen shot of the error.

enter image description hereI can play the game for the first time, but I cannot play the game when I restart. The game is not working correctly when restarted.

This is happening when segues are used, when I use UIAlertView the game is working smoothly. I want to implement segues.

here is the ViewController.swift

   func movingStone() {


    stone = UIImageView(image: UIImage(named: "stones.png"))
    stone.frame = CGRect(x: 0, y: 0, width: 5.0, height: 5.0)



    stone.bounds = CGRect(x:0, y:0, width: 5.0, height:5.0)
    stone.contentMode = .center;



    stone.layer.position = CGPoint(x: boat.center.x - 5, y: 0.0)
    stone.transform = CGAffineTransform(rotationAngle: 3.142)


    self.view.insertSubview(stone, aboveSubview: myView)



    UIView.animate(withDuration: 5, delay: 0, options: UIViewAnimationOptions.curveLinear, animations: { () -> Void in
        self.stone.frame.origin.y = self.view.bounds.height + self.stone.frame.height + 10
    }) { (success:Bool) -> Void in

        self.stone.removeFromSuperview()
        self.movingStone()

    }



}

    func movingFood() {

    food = UIImageView(image: UIImage(named: "fishcoin2.png"))
    food.frame = CGRect(x: 0, y: 0, width: 5.0, height: 5.0)
    var stone3 = leftS + arc4random() % rightS


    food.bounds = CGRect(x:0, y:0, width: 5.0, height: 5.0)
    food.contentMode = .center;
    food.layer.position = CGPoint(x: boat.center.x + 20, y: 0.0)
    food.transform = CGAffineTransform(rotationAngle: 3.142)


    self.view.insertSubview(food, aboveSubview: myView)


    UIView.animate(withDuration: 5, delay: 0, options: UIViewAnimationOptions.curveLinear, animations: { () -> Void in
        self.food.frame.origin.y = self.view.bounds.height + self.food.frame.height - 50
    }) { (success:Bool) -> Void in


        self.food.removeFromSuperview()
        self.movingFood()

    }



}

   func intersectsAt(tap2 : Timer) {
  if(boat.layer.presentation()?.frame.intersects((food.layer.presentation()?.frame)!))!{
        food.layer.isHidden = true

        coins = +1
        collectedCoin.text = "\(coins)"
        if coins > 100 {
            super.performSegue(withIdentifier: "GameOverIdentifier", sender: self)

        }
  }

    if(boat.layer.presentation()?.frame.intersects((stone.layer.presentation()?.frame)!))! {
        stopGame()


    }

}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "GameOverIdentifier" {
        let gameOver = segue.destination as? GameOver
        gameOver?.coin = coins


    }
}

func stopGame() {

    tapTimer2.invalidate()

    boat.image = UIImage(named: "wreckboat.png")

    super.performSegue(withIdentifier: "GameOverIdentifier", sender: self)

}

Here is the GameOver.swift which has the restart button, implementing segue to previous controller.

import UIKit

class GameOver: UIViewController {

var coin = Int()


var restart : ViewController!

@IBOutlet weak var go: UILabel!
override func viewDidLoad() {
    super.viewDidLoad()

    go.text =  "\(self.coin)"
    // Do any additional setup after loading the view.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}


@IBAction func restartGame(_ sender: Any) {
    performSegue(withIdentifier: "goBack", sender: self)
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "goBack" {
        if let back = segue.destination as? ViewController {

            back.startGame()
    }

    }
}



/*
// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.
}
*/

}

I think the problem is when I use touchesBegan and touchesMoved.

Any advice will be valuable.

Thank You.


Solution

  • From your code What I can see that you trigger a segue from ViewController.swift to GameOver.swift which is ok and it means you're presenting the GameOverViewController. The problem is you should not segue from GameOverViewController back to your ViewController.swift you have to dismiss your gameOverViewController

    inside your restart function

    @IBAction func restartGame(_ sender: Any) {
       self.dismiss(animated: true, completion: nil)
    }
    

    The second problem I can see is you're trying to call a function from your prepare for segue

    You shouldn't use this segue back to your ViewController

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "goBack" {
        if let back = segue.destination as? ViewController {
            // this is wrong
            back.startGame()
         }
    
      }
    }
    

    You can use unwindSegue, delegate, notification... to trigger your startGame() function