2.5hours
iOS
Easy>Medium
Swift
Gamification. It continues to be a buzzword these days. Every company out there is trying to engage users and capture their attention by turning mundane applications into games with rewards, scores, and more. The ethics are questionable but there is an overwhelming amount of evidence that it works.
The best way to solidify programming knowledge is by creating something interactive and fun. Most programming course work starts from the command line and doesn't hit working with user interfaces and more advanced topics until years into the major. Some people might not consider that curriculum motivational.
The goal for this tutorial series is to walk a new developer to the platform through the steps required to make a game. In the end, every fundamental iOS programming paradigm is touched upon, including:
- Auto Layout and Animating constraints
- CABasicAnimations
- CATransactions
- CALayers
- Delegates and Closures
- Embedded UIViewControllers
- UserDefaults
- Timers
The tutorial is broken into several parts focused on different aspects of the build. Check out each part:
- Part 2: Write the Swift code connecting the Interface Builder design from Part 1 of the series to the engine and logic that will drive it
- Part 3: Use CALayers and CABasicAnimation to build firing animations and finalize the interface using UIView animations
- Part 4: Wrap up the game code by adding final polishing including animations to the win screen and connecting the screens using an unwind segue.
Download the complete project and code for this tutorial from the repo on bitbucket.org or build it up on based on the sequence of steps below and in the subsequent related articles.
Let the gamification commence!
Overview
The concept for the game is very basic: it's a two player game that requires no skill from the players except the will to smash a single button faster than the opponent. There will be two blocks of different colors opposite each other, each with a fire button.
When the game starts, each press of the button will cause a laser to shoot across to the other side, making the opposite player's block slightly smaller. Whoever gets the opponent's block-size to 0 wins that round.
This is what the game will look like when finished:
Application Architecture
Developers new to iOS programming may be wondering where to start. The game is basic and simple to understand but how does a newbie understand the strategy to design it, what classes should be used, the complexity of the graphics required, and more.
The next sections should help since they deal with application architecture and design.
Designing an Application
The best way to solve challenging problems is break them down into their smallest components, think about how to solve each of them, then reassemble each individual component into a final design and product.
Given that strategy, what is required for this app. Refer to the list:
- Main screen where the game is played.
- Countdown to start the game while simultaneously preventing the user from firing
- Square "Base" on each side of the main screen
- Firing Buttons inside each "Base"
- Drawing the "laser" effect onto the screen for each button tap
- Animating the size of the "Base" down on each laser hit.
- Make a unique sound for each player as they fire their weapon.
- Keep track of the size of each base and when one hits zero first, declare the winner.
- Win screen showing the number of victories for each side
- Way to restart the game again from the Win screen and reset the scores if the player has a new opponent.
Breaking each of those steps into more detail:
- Main screen where the game is actually played: That's the base UIViewController core to any single-screen application. It's provided free when the project is started.
- Countdown to start the game while preventing the user from firing: The word countdown is the hint. Use a Timer class and modify text based on it. Where will the rendering of the countdown take place? Create an "Overlay" UIView that'll sit on top of the main UIViewController to darken the screen and a UITextField or UILabel to render the number in the countdown on top of that darkened overlay.
- Square "Base" on each side of the main screen : The base is just a colored block. For demonstration of the principles of extensibility, modularity, and separation of responsibility, use embedded UIViewControllers for each base. They'll reside in containers within the main UIViewController and some of the logic from the Main screen can be handed to them.
- Firing Buttons inside each "Base": use two UIButtons. The looks can be improved by adding an image for each button. Place these inside the embedded UIViewController.
- Drawing the "laser" effect onto the screen for each button tap: The origin and destination of a shot is required. Each time a shot hits the opponent's base, its height will shrink, and that's an ever changing target that must be kept track of. The rendering will be done by adding a path onto the screen, a start and end point, and then drawing a line between the two points.
- Animating the size of the "Base" down on each laser hit: Place a height constraint on each embedded UIViewController and then subsequently animate the constraint for each shot.
- Make a unique sound for each player as they fire their weapon: iOS has a very robust audio player subsystem that's very simple to integrate.
- Keep track of the size of each base and when one hits zero first, declare the winner: track the height of the impacted base and if it hits zero then trigger the win screen.
- Win screen showing the number of victories for each side: An additional screen to segue into from the main game screen. In other words, another UIViewController will be necessary.
- Way to restart the game again from the Win screen: button on the Win screen that will trigger an unwind segue back to the main screen. The countdown will be recycled for the start of another game.
Based on all the details above, the following information will need to be kept track of and possibly passed between the embedded, main, and Win screen UIViewControllers:
- Total Wins for each player: should this be saved permanently? What the players should reset the count? If saving it, take advantage of UserDefaults to store the simple win tally. Reset button? Sounds like it's a good idea but that will require another UIButton
- Which base won the current game: the main UIViewController will know the point at which a base is reduced to size zero and logic can be put in place to stop the game and trigger the Win screen.
- How will the Win screen know which base won though? Pass that data to it using a class Variable or some other means of communication between UIViewControllers. Shameless plug for another article on this website around that topic here.
- Shame, penalize, or indicate which side was the losing player when a new game is started? In this case, it is not required to pass information back to the originating UIViewController. It already knows which player won and informed the Win screen UIViewController in the first place. While not required, the tutorial will cover passing data backwards using a closure defined in the Win screen UIViewController.
Final Thoughts on Architecture
The planning/design stage may feel a little boring but proceeding down this path will make life far easier. Writing code is fun but putting in the effort up front to design the architecture of the app properly is far more critical.
If this were a production project, these rough notes would be turned into a requirements document. That requirements document would be used to create storyboards that detail the flows of the app with rough diagrams. All well before getting near a line of code.
While none of that will be covered here, keep this design process in-mind going forward. It's a high-level overview of how software engineering is actually done in real life.
Summing up the individual components, we'll need the following:
- One UIViewController for the main game screen.
- Another UIViewController for the Win screen
- 2x UIViewController for the bases, one per user.
- 2x UIButton for the fire mechanism
- 2x UIButton for the Reset Scores and Play Again mechanism.
Setting up the core screens in Interface Builder is covered in the next few sections of this tutorial.
Interface Builder: Add Core Components
The list of components is complete and must be adopted into the interface to show to the players. The rest of this tutorial will demonstrate Interface Builder setup for the game. Subsequent tutorials in the series will show how to connect the Interface Builder objects to code.
The only thing needed to start working through this is a new project with a single UIViewController in-place.
Continue onto Part 2, Part 3, and Part 4 to complete the app and get it into a playable state.
Player Bases: Embedded UIViewControllers
Setting up the embedded UIViewControllers is straightforward. Select the Library button and type in "Container" to get the Container View option. This should be visible:
Drag two of them out onto the main screen UIViewController, resizing each to 375 pixel width and 350 pixel height. Position them on each side of the main UIViewController screen. That should look like this:
Each embedded Container View has a segue pointing to an UIViewController. Position these next to the main screen UIViewController and assign the preferred color to each base as a background color. Orange and blue are the choices for the downloadable project. The storyboard should now look like this:
Don't worry if it's not exact. Several contraints must be applied to the Container Views. Select the top Container View and then the "Add New Constraints" button. Apply a top, left, and right constraint of 20, a width of 375, and height of 350. The values should look like this:
The image shows "Height" is not selected and an Add 1 Constraint button - that's not exactly right. Make sure the height is selected and the add constraints button should indicate Add 5 Constraints at the bottom then select it and add them.
Now apply the same constraints to the bottom UIViewController except move the top constraint down to the bottom. No top constraint is set to be added for the bottom VC.
Finally, give identifiers to the segues that were generated for each of the embedded UIViewControllers. Click the upper segue arrow, select the Attributes Inspector, and give the Identifier the name "uppercontainer". Do the same for the lower segue, giving the name "lowercontainer".
Fire Buttons
Now add the fire buttons to the embedded UIViewControllers.
Select the library button, type 'button', and drag that UIButton object out onto opposite ends of each of the embedded UIViewControllers. Each of them should now have a generic UIButton sitting on opposite ends, as far away as possible from each other. The storyboard should look similar to this:
Next, change the image for the UIButton to something larger and more interesting. In the downloadable project for this tutorial (available in the repo on bitbucket.org ) a sample image is included for use. More options are available at FlatIcon.
Download transparent .png files so the tintcolor can be modified from application code. The image will be resized and put inside the Assets.xcassets file inside Interface Builder.
Select the Assets.xcassets file, then the image imported there. Select the Attributes Inspector, then select the dropdown "Render As", and Template Image. This is what it should look like:
Delete the default title "Button" and select the dropdown Image button. Then select the image name assigned in the .xcassets file:
Next, add constraints onto the buttons to make them an appropriate size. Select each button individually, choosing the "Add New Constraints" option for each, and then assign each a width and height of 70.
Finally, add the Auto Layout constraints to position them. The bottom and top constraint for the respective bottom and top UIButton is set to zero. Select each button individually, applying a "Horizontally in Container" constraint (centerX) using the align button.
When complete, the storyboard should look similar to this for the embedded UIViewControllers:
Countdown Overlay
The next task is making an overlay that is used to display the countdown in the beginning of the game. The overlay is a UIView with a dark background color and a UILabel to display the current countdown number.
That UIView overlay will sit atop the primary main game screen. Perform the following steps:
- Select the Library button
- Type in "UIView"
- Drag the View onto the main game screen UIViewController
- Select each side of the UIView and drag each out all the way to the top and sides of the parent UIViewController.
- Set the background color to black.
- Select the "Add New Constraints" button and apply a size of 0 to all sides.
If the "View As" option in Interface Buider is set to iPhone XR or other notched device, there is a white gap shown at the top and bottom. The top and bottom constraints are assigned to the Safe Area, a pre-designated "barrier" that helps prevent rendering application graphics where they cannot be shown to the user.
Change that to Superview instead of Safe Area. Notice in the image below that the Second Item constraint of the Overlay top constraint is set to Superview.Top.
Select the main screen UIViewController then select the dropdown to open up the main constraints. Choose the appropriate Superview.top or Superview.bottom constraint for the Overlay.
Select a UILabel and drag it to approximatly the center of the Overlay UIView. Give it a height of 300 and width of 200 using the "Add New Constraints" button, then give it a "Horizontally in Container" (centerX) and "Vertically in Container" (centerY) constraint using the Align button.
Add some flair by giving the UILabel a cartoonish and large font. Change the text to "3" and apply a Marker Felt Wide font of size 200 points. The storyboard should look like this:
Now the underlying controls are blocked from selection in the GUI due to the overlay. If direct access is required under the overlay select the overlay then deselect the "Installed" option in Attributes Inspector. Refer to the image below:
Win Screen
The final piece of the Interface Builder puzzle is creating the Win Screen that the players will be directed to when a player is victorious.
Go to the Library, type in "View Controller", and drag the object out onto the storyboard below the main screen UIViewController. A segue is used to show the Win Screen. Select the main screen UIViewController and then control drag from the uppermost leftmost button on the object down to the Win Screen. Refer to the screenshot below:
A segue selector will popup when the connection is made. Select "Show":
Make sure to give the segue an identifier by clicking on the segue arrow drawn between the Main Screen and Win Screen UIViewControllers, selecting the Show Attributes Inspector, and typing "winScreenSegue" into the identifier area.
The two screens are connected together, now what? Refering to the totally informal "Design Document" made in the planning stage (see section above), the following is needed:
- Way to show each player their current win count
- Label indicating who won the actual game
- Button to reset the scores
- Button to play another game
First, drag two UIButtons onto the Win Screen, positioning them to the bottom of the UIViewController. The Play Again button will be positioned on the left and Reset Scores button on right.
Refer to more detailed descriptions above on how to select the objects, drag them onto the screen, and set constraints.
Type the UIButton names into the Title area in Attributes Inspector, and assign a width and height constraint to the size that's already generated and a bottom constraint of zero using the Add New Constraints option.
Next, use the Align button to give each a Horzontally in Container (centerX) value of zero. Select the actual constraint "line" in Interface Builder for the one created, and play with the constant by increasing or decreasing its value in the Attributes Inspector until the positioning is correct.
It should look like the screenshot below:
Next, get three UILabels onto the Win Screen; the center UILabel is modified at run-time to display the winner's name (blue or orange), and a top as well as bottom UILabel will display the win count for each player, calculated at run-time.
For the top player score UILabel give a top, left and right constraint of 0. For the bottom player score, a bottom, left, and right constraint of zero. Lastly, assign a height of 300 to each, change the font to Marker Felt Wide of 200 points, and the text in each to 0.
Finally, position the middle UILabel in the center, in between the scores, assign it a font of size 35 in Attributes Inspector then click on Align and assign a Horzontal in Container (centerX) and Vertical in Container (centerY) constraint of 0 so it's dead center in the UIViewController. The Win screen UIViewController should now look like this:
Wrap Up...For Now
Reviewing the accomplishments from Part One,:
- Came up with a concept for a game.
- Broke the concept down into smaller pieces and assessed what is needed to build the application.
- Determined the information that will flow between UIViewControllers and what must be stored.
- Turned the rough sketch into an actual storyboard with all components in-place.
Here are the links to the next tutorials in the series, in case the reader would like to jump ahead:
- Part 2: Write the Swift code connecting the Interface Builder design from Part 1 of the series to the engine and logic that will drive it
- Part 3: Use CALayers and CABasicAnimation to build firing animations and finalize the interface using UIView animations
- Part 4: Wrap up the game code by adding final polishing including animations to the win screen and connecting the screens using an unwind segue.
Finally, some key links to subjects discussed in this tutorial. The focus is on Auto Layout and Software Engineering Principles: