TapWar

In this tutorial you’ll learn to make a simple iOS game using the most basic components: UIView, UILabel, UITapGestureRecognizer.

You can do it really quick and the result will impress all of your friends. So, let’s start!

Tap War

Our game involves two players: red and green. When a player taps his part of the screen it will grow a bit in size and when one player overpowers the other (his part covers the game board) he’ll win the round.

Before each round a countdown timer is displayed in the middle of the screen, and you can see the score of each player on their side.


Step 1: Create a new project

Open Xcode and choose to create a new Xcode project. Select the Single View Application template, give it the name TapWar and select where to save it.

creationProcess

Hit “create” and you’re on!


Step 2: creating the interface

As you can see now, all the files of your project are visible in the left side of Xcode in the Project Navigator. The ones we’ll be touching today are ViewController.swit and Main.storyboard. Click on Main.storyboard and you will see the first screen from your app. In the right bottom corner you have a list of components called the Component Library.

For this game we will need UIViews and UILabels. Search for UIView and label in the Components Library:

Grab a View and drag it on the upper half of the screen. Now you should see some basic options for your view in the upper right side of the screen in the Attributes Inspector.

Change the background of the View to DarkGreen, drag another View on the lower side of the screen and change its background to DarkRed. Choose for both to be multiple touch enabled and change the tag number for the red one to 1.

Now we have to set the constraints for these two views; meaning that we have to set some layout rules so that Auto Layout will know how to draw them on the screen when we run the project. We want the green View to cover the upper half of the screen and the red on the other half. We can also make the status bar to disappear.

Sounds fun, let’s begin!

Click on the green view from Main.storyboard and hold Control(), now drag the pointer on top of the main view (the superview).

You will now see a list of possible constraints that you can set. As you can see the options are mentioning about a margin; and we don’t want that. The margin sets a padding on big and very big screens and our goal is to cover the whole half of the screen with our green view. If you press and hold Option() you will get another list of options:

Basically the constraints refers to the visual object’s positions: left (leading), right (trailing), top and bottom. There are also constraints about width an height of the visual object we want to position. So we need to set the top Vertical Spacing to Top Layout Guide , left Leading Space to Container and right Trailing Space to Containerconstraints to 0, and the height can be 1/2 of the screen we are using for simulating the game (the height constraint will be set dynamically from the code later on). After creating the constraints for the green view we will set the constraints to 0 from the right sided menu, and the height constraint from the bottom-right sided bar:

set all the constants to 0and the height to 300 and click Add 1 Constraint

Great! Now to apply the changes, click the green view on the storyboard and press Command+Option+=(++=). Do the same for the red view but add a bottom constraint (Vertical Spacing to Bottom Layout Guide) instead of the top one.

Now you should see two views and some space between them on the center of the screen.

To make the scores and countdown labels, add three labels, one for each of the two views and one on the white space between them. Click on the label from the green view and drag the pointer on the green view to add constraints for Center Horizontally in Container and Center Vertically in Container:

This will center the label inside the green view. We have to do the same for the other two labels.

For the label that’s on the red view, the same constraints needs to be applied, and for the third one that’s on the superview we have to set the same constraints but in relation with the the main view. We’ll do that by dragging the pointer on the superview (from the left list) or on the white space (which also represents the superview):

We’re almost set; we just need the gesture recognizers and the referencing outlets and we can start creating the game logic. Go to the Component Library and search for Tap Gesture Recognizer and drag it on the green view. Add another one on the red view.

Good, now click on the green view and select the outlet section that’s represented by an arrow pointing to right; and above it, click on the circles button from the top right corner of Xcode to open the Assistant Editor, it should open the ViewController.swift file for you so can link the outlets.

For the views and labels we need New Referencing Outlet, and for the height constraints we also need referencing outlets but that constraints will be under the superview->view->constraints in the left sided menu with the project files hierarchy. Click on the element that you want to link, then click and drag from the New referencing outlet plus sign to the ViewController.swift and give every outlet a suggestive name:

After linking the redView, greenView, topScoreLabel, bottomScoreLabel, topConstraint, bottomConstraint and countdownLabel we’re ready to create the game itself!

Quick tip
You can hide the status bar by adding these lines of code:

class ViewController: UIViewController {
                            ...
    override var prefersStatusBarHidden: Bool {
           return true
     }
                              ...
}

Step 3: creating the logic

For a right start, we need to answer a set of basic questions that will define our game logic:

  • how many players will be there?
  • who and when wins or loses?
  • when does the game stop?

and any other logic related questions that you think are relevant for the game you are building.

For TapWar, we will always have two players, by default the player who has ten taps more than the other wins the round and the game will end when they decide so (and close it). Alright, we have now the interface and the referencing outlets for every element!

Back to the ViewController.swift, in the ViewController class we need some properties:

class ViewController: UIViewController {
    ...

    // match scores
    var topScore = 0 {
        didSet {
            topScoreLabel?.text = "\(topScore)"
        }
    }  

    var bottomScore = 0 {
        didSet {
            bottomScoreLabel?.text = "\(bottomScore)"
        }
    }

    let maxScore = 20

    var gameOn = false



    ...
}

Setting the maxScore to 20 will help us to get the current difference of taps later on. We have to override the viewDidLoad function and add some ordered instructions that will be processed immediately the superview loads:

class ViewController: UIViewController {
...
    override func viewDidLoad() {
        super.viewDidLoad()  

        // reset match score
        topScore = 0
        bottomScore = 0 

        // start a new game
        newGame()   
    }
...
}

As you can see, we are setting the values of the player’s scores, calling the newGame function.

We need set a minimum size for each side, so we’re adding a padding property:

class ViewController: UIViewController {
...
var padding: CGFloat = 70
...
}

A player wins when the difference of taps is bigger than 10, instead of keeping two variables for each of the players score from the current round, we are going to remember only the difference. But instead of starting the difference at 0 we are going to start at 10. In this way if the difference is 0 the red player wins and if the difference if 20 the green one wins. Another interesting thing about this representation is that it also makes it easy to compute the percentage of each players territory.

Add the score property to represent the difference:

class ViewController: UIViewController {
...
  var score = 0 {
        didSet {
            guard let viewHeight = view?.frame.height else {
                return
            } 
            let availableSpace: CGFloat = viewHeight - 2 
                                          * padding
            topConstraint.constant =
                max(min(viewHeight, CGFloat(score) /              
                CGFloat(maxScore) * 
                availableSpace + 
                padding), 0)

            bottomConstraint.constant =
                max(min(CGFloat(maxScore-score) / 
                CGFloat(maxScore) *
                availableSpace + 
                padding, viewHeight), 0)

            UIView.animate(withDuration: 0.1,
                           delay: 0,
                           options: .allowUserInteraction,
                           animations: {
                self.view.layoutIfNeeded()
            }, completion: nil)
        }

...
}

In the score didSet property observer we are going to update the constraints for the top and bottom side of the screen and animate the change.

Let’s create the newGame function that will tell the app what to do when a new game is started:

class ViewController: UIViewController {
    ...

    func newGame() {
        // reset scores
        score = maxScore / 2

        // show scores and fade out 
        topScoreLabel.alpha = 1
        bottomScoreLabel.alpha = 1
        UIView.animate(withDuration: 2) {
            self.topScoreLabel.alpha = 0
            self.bottomScoreLabel.alpha = 0
        }
    }   

    ...
}

Last but not least, we have to link the events from the Tap Gesture Recognizers. We’ll do that by linking the selector of the gesture recognizer to our ViewController.swift: Okay, do you remember that we’ve given views different tags? We will use that difference right now. Let’s write some code into the didTapView function:

class ViewController: UIViewController {
...
 @IBAction func didTapView(_ gestureRecognizer: UITapGestureRecognizer) {
        guard let sender = gestureRecognizer.view else {
            print("no view on gesture recognizer!")
            return
        }

        // ignore taps unless gameOn        
        guard gameOn else { return } 

        // update score difference
        if sender.tag == 0 {  
            score += 1 
        } else { 
            score -= 1 
        }

        // check if Green wins
        if score > maxScore {
            print("Top wins")
            topScore += 1
            gameOn = false
            newGame()
        }

        // check if Red wins
        if score < 0 {
            print("Bottom wins")
            bottomScore += 1
            gameOn = false
            newGame()
        }   
    }
    ...
}

First we are checking if the game is on by guarding the boolean variable named gameOn. Keeping in mind that we are in a function that gets called when a View (green or red) gets tapped, we can create the difference between red and green taps by using the tag that we gave to the views (0 for green view and 1 for red one).

Setting the maxScore to 20 and the score to maxScore / 2 helps us to check if the score will become 20 or -1, which means one of the players won the round.

Afterwards, if the difference of taps (score) is 21 or -1 and one of the players won the round, we’ll update the top or bottom score and we’ll start a new round by calling the newGame() function.

Your game should be functional now!


Finishing up

First of all, congratulations for getting here!

We can add some code to make the game more refined. We are going to leave this as an exercise for you: Add two lines to delimit the zone where a round is win/lost and an animation for the counterLabel that gets shown before the game starts.

Zone Delimiters

Let’s begin with the two lines. Add two more views to the screen, choose one and give it the height = 1, black color and some constraints: leading and trailing space to container and set them to 0, then add Vertical Spacing to Top Layout Guide constraint and set it to 20. This will be the line for the greenView, now make another one for the red one and set the constraint just the same but with Vertical Spacing to Bottom Layout Guide equal to 20 instead of the top one. Create references for each new view, then one reference for the top and bottom constraints and name them topLineConstraint and bottomLineConstraint. Go into the ViewController.swift and alter the padding var by adding this:

var padding: CGFloat = 75 {
        didSet {
            guard view != nil else {
                return
            }

            topLineConstraint.constant = padding
            bottomLineConstraint.constant = padding
        }
    }

Now the lines will be placed right where a round ends. To check if you’ve done everything right, edit your label by changing the text color to white, make the font bigger and your app should look like this:

Counter Animation

Get back to the newGame function and add the new animation:

func newGame() {
 ...
let stepTime: TimeInterval = 0.5
self.countdownLabel.alpha = 1
        self.countdownLabel.text = "3"
        UIView.animate(withDuration: stepTime, animations: {
            self.countdownLabel.alpha = 0
        }, completion: { _ in
            self.countdownLabel.alpha = 1
            self.countdownLabel.text = "2"

            UIView.animate(withDuration: stepTime, animations: {
                self.countdownLabel.alpha = 0
            }, completion: { _ in
                self.countdownLabel.alpha = 1
                self.countdownLabel.text = "1"

                UIView.animate(withDuration: stepTime, animations: {
                    self.countdownLabel.alpha = 0
                }, completion: { _ in
                    self.countdownLabel.alpha = 1
                    self.countdownLabel.text = "GO!"

                    UIView.animate(withDuration: stepTime, animations: {
                        self.countdownLabel.alpha = 0
                    }, completion: { _ in
                        self.gameOn = true
                    })
                })
            })
        })
}

Well done! Your game is ready and you can play it with your friends.

Congratulations!
Please, really please, don’t break your phone while playing! I’m going to post more tutorials very soon on We ❤ Swift.

Conclusion

By using a hand-full of basic components from the iOS SDK you can make a game in a few minutes. We hope you enjoyed making this game and learning new things.

Thank you for your time!

1 Star2 Stars3 Stars4 Stars5 Stars (7 votes, average: 4.86 out of 5)
Loading...

  2 comments for “TapWar

  1. March 8, 2017 at 9:35 pm

    A good tutorial. I got stuck in a couple spots for example when adding the didTapView function it just shows adding the green but says nothing about adding the red. It’s obvious in the logic but it doesn’t clearly state that and how to implement. I finally got it working but it was a little challenging. Also I’d maybe avoid using the words “finally”, “game should be functional now” and “finishing up” before the tutorial is over. It’s a little misleading. Sorry for the harsh feedback I do like the tutorial it was just slightly frustrating for me to make it work.

    • George Retegan
      March 15, 2017 at 4:21 pm

      Hi Joe,

      Sorry for the delayed response and thank you for the feedback. (I don’t think it’s harsh at all!)

      Yes, you are right about the didTapView function, the logic behind it was only shown in the code part (comments between lines of code) but now I’ve explained better what that code does. I also have a question; in this function, we are just getting the feedback from the gesture recognizers, creating the score difference, checking if the round is won by a player and… that’s all. Regarding that you said that the process of adding the redView got you stuck, I’ve also check it and didn’t find any missing text or code part.
      So the question is: where do you think is the missing explanation that got you stuck?

      About the words, you might be right and I’ll try to use concludent words only in the conclusion part.

      Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *

Subscribe
We send about one email per week with our latest tutorials and updates
Never display this again :)