Go to Wireframes Go to Hi-Fi Mockups
I was recently tasked with designing a dice game app from scratch, for iOS.
The client asked for a very brief turnaround, possibly a one digit number, possibly in the lower end.
The main requirements for the app were:
– show dice clearly on the screen.
– include the current score.
– when the user rolls a double (both dice hit the same number), display a “Double” message and double the score (i.e. if both dice hit 2, double it returns (2+2)x2).
– create appropriate deliverables so that both the dev team and the major stakeholders will understand the game.
With this list, I set out to create the best game I could.
Adding on to the requirements
The first question I asked myself was “Ok, but where’s the game exactly?”. I could not figure out where the app would generate any fun by simply rolling the dice.
To solve this issue in the simplest, fastest way possible I added a timer, with a fixed 60 seconds. The logic works as follows:
– the player must roll the dice as many times as possible during 60 seconds.
– the player must score higher than the previous Best Score.
This was simple enough to guarantee at least an engaging MVP.
With this modification, I quickly went back to the client and communicated the timer addition. It was approved right away, so I moved on to do some quick UX.
Research and Wireframes
To start off on the right foot, I decided I would take a quick look at what’s out there. With “out there” I mean, look at the games on my iPhone.
I don’t play games that much, but by looking at the ones on my phone one thing was clear: it’s really easy to understand the different interactive blocks of information.
I took inspiration from a couple of apps and I established an information scheme.
Before moving on to the wireframes, I felt something was missing: the actual name of the app. I had an idea and tagline in mind for the app:
All you can roll in 60 seconds!
It sounded silly and fun enough to incorporate that into the wireframes.
Now I felt confident enough to move forward with the wireframes.
It didn’t take long before I had the main game screens down. I added options for sound effects and animations too.
After that, I started thinking about a small navigation and I created an overlay menu for it.
With the wireframes on my side monitor, I opened Sketch.
I really needed to create mainly two screens: game screen and navigation screen.
I tried a very basic version one, but I wasn’t too happy with it. It looked flat and lacked some dynamics. I played around with background colors, and I redesign the dice.
I felt I was on to something, but to make sure I was, I took a 20-minute break to “freshen up” my eyes. This step is key: constant, non-stop, meticulous design creates a sort of “confirmation bias”. Everything looks great and impactful. You go to bed, go back to the computer the day after and no, it actually doesn’t look great.
After the break, I came back to the computer, liked what I saw and made a few last adjustments.
Of course, to present it to the stakeholders, I had to come up with an iPhone mockup.
This is also a very underestimated step.
Some clients are going to have a tough time seeing the screens in action. Now, to avoid any confusion or iterations based on this very problem, I created two iPhone 7 mockups to illustrate how cool the app would look once it’s up and running.
Long-term Strategies and Monetization
To add to the designs, I also presented a long-term gaming strategy and monetization option.
1. Add second touch point to increase the level of complexity of the game. it could be that every X points, a bubble appears on the screen at a random point (in the Dice section) and, if the user is fast enough to tap it, x points are added.
2. Add option to share the score to Facebook, Twitter and Instagram (screenshot).
3. Add multi-player option to challenge friends and family.
4. Add option to change the background image. I.e. top world locations monuments (Paris, NY, Tokyo, etc.), themes (Halloween, Christmas, etc.), current popular show/cartoons (Rick and Morty, Attack on Titan, etc.).
5. Create Dice Coins and place them on the screen for the users to collect.
6. Increase game engagements by allowing to tap each die directly. This will allow creating i.e. poisoned dice that, if tapped, decrease the points. Also, we could add dice up to 4, and this will force the game to be played in landscape mode.
7. Explore AR possibilities.
– Charge a small amount ($ or Dice Coins) for dice skins. I.e. skulls instead of dots, or emoji instead of dots.
– If 6. above is implemented, then it could be possible to add safety packs or Band-Aid for when i.e. a poisoned die is tapped OR to cancel any negative effect applied to the Current Score or Dice Coins.
Dice-A-Roni was a lot of fun to design. As I am typing this, the dev team is beginning the development of the app. I am very interested in testing it with actual users before it’s released.
7.9 Case Study: A Game of Chance; Introducing Enumerations
One popular game of chance is the dice game known as “craps,” which is played in casinos and back alleys throughout the world. The rules of the game are straightforward:
You roll two dice. Each die has six faces, which contain one, two, three, four, five and six spots, respectively. After the dice have come to rest, the sum of the spots on the two upward faces is calculated. If the sum is 7 or 11 on the first throw, you win. If the sum is 2, 3 or 12 on the first throw (called “craps”), you lose (i.e., “the house” wins). If the sum is 4, 5, 6, 8, 9 or 10 on the first throw, that sum becomes your “point.” To win, you must continue rolling the dice until you “make your point” (i.e., roll that same point value). You lose by rolling a 7 before making your point.
The app in Fig. 7.7 simulates the game of craps, using methods to define the logic of the game. The method (lines 24–80) calls the method (lines 83–94) as needed to roll the two dice and compute their sum. The four sample outputs show winning on the first roll, losing on the first roll, losing on a subsequent roll and winning on a subsequent roll, respectively. Variable (line 8) is declared , so it can be created once during the program’s execution and used in method .
Fig. 7.7 | class simulates the dice game craps.
In the rules of the game, the player must roll two dice on the first roll and must do the same on all subsequent rolls. We declare method (lines 83–94) to roll the dice and compute and display their sum. Method is declared once, but it’s called from two places (lines 30 and 54) in method , which contains the logic for one complete game of craps. Method takes no arguments, so it has an empty parameter list. Each time it’s called, returns the sum of the dice as an . Although lines 86 and 87 look the same (except for the die names), they do not necessarily produce the same result. Each of these statements produces a random value in the range 1–6. Variable (used in lines 86–87) is not declared in the method. Rather it’s declared as a variable of the class and initialized in line 8. This enables us to create one object that’s reused in each call to .
7.9.2 Method ’s Local Variables
The game is reasonably involved. The player may win or lose on the first roll or may win or lose on any subsequent roll. Method (lines 24–80) uses local variable (line 27) to keep track of the overall game status, local variable (line 28) to store the “point” if the player does not win or lose on the first roll and local variable (line 30) to maintain the sum of the dice for the most recent roll. Variable is initialized to to ensure that the app will compile. If you do not initialize , the compiler issues an error, because is not assigned a value in every of the statement—thus, the app could try to use before it’s definitely assigned a value. By contrast, does not require initialization because it’s assigned a value in every branch of the statement—thus, it’s guaranteed to be initialized before it’s used. However, as good practice, we initialize it anyway.
Local variable (line 27) is declared to be of a new type called , which we declared in line 11. is a user-defined type called an enumeration, which declares a set of constants represented by identifiers. An enumeration is introduced by the keyword and a type name (in this case, ). As with a class, braces ( and ) delimit the body of an declaration. Inside the braces is a comma-separated list of enumeration constants—by default, the first constant has the value and each subsequent constant’s value is incremented by . The constant names must be unique, but the value associated with each constant need not be. Type is declared as a member of class , because is used only in that class.
Variables of type should be assigned only one of the three constants declared in the enumeration. When the game is won, the app sets local variable to (lines 37 and 59). When the game is lost, the app sets to (lines 42 and 66). Otherwise, the app sets to (line 45) to indicate that the dice must be rolled again.
7.9.4 The First Roll
Line 30 in method calls , which picks two random values from 1 to 6, displays the value of the first die, the value of the second die and the sum of the dice, and returns the sum of the dice. Method next enters the statement at lines 33–49, which uses the value to determine whether the game has been won or lost, or whether it should continue with another roll.
The sums of the dice that would result in a win or loss on the first roll are declared in the enumeration in lines 14–21. These are used in the statement’s s. The identifier names use casino parlance for these sums. In the enumeration, we assign a value explicitly to each identifier name. When the is declared, each constant in the declaration is a constant value of type . If you do not assign a value to an identifier in the declaration, the compiler will do so. If the first constant is unassigned, the compiler gives it the value . If any other constant is unassigned, the compiler gives it a value one higher than that of the preceding constant. For example, in the enumeration, the compiler implicitly assigns to , to and to .
7.9.6 Underlying Type of an
You could also declare an ’s underlying type to be , , , , , , or by writing
where typeName represents one of the integral simple types.
7.9.7 Comparing Integers and Constants
If you need to compare a simple integral type value to the underlying value of an enumeration constant, you must use a cast operator to make the two types match—there are no implicit conversions between and integral types. In the expression (line 33), we use the cast operator to convert the value in to type and compare it to each of the constants in . Lines 35–36 determine whether the player won on the first roll with () or (). Lines 39–41 determine whether the player lost on the first roll with (), () or (). After the first roll, if the game is not over, the case (lines 44–48) saves in (line 46) and displays the point (line 47).
Additional Rolls of the Dice
If we’re still trying to “make our point” (i.e., the game is continuing from a prior roll), the loop in lines 52–69 executes. Line 54 rolls the dice again. If matches in line 57, line 59 sets to , and the loop terminates because the game is complete. In line 64, we use the cast operator to obtain the underlying value of so that we can compare it to . If is equal to (), line 66 sets to , and the loop terminates because the game is over. When the game completes, lines 72–79 display a message indicating whether the player won or lost, and the app terminates.
Control Statements in the Example
Note the use of the various program-control mechanisms we’ve discussed. The class uses two methods— and (called twice from )—and the , , ... and nested control statements. Also, notice that we use multiple labels in the statement to execute the same statements for sums of and (lines 35–36) and for sums of , and (lines 39–41).
Code Snippets for Auto-Implemented Properties
Visual Studio has a feature called code snippets that allows you to insert predefined code templates into your source code. One such snippet enables you to easily create a statement with s for all possible values for an type. Type in the C# code then press Tab twice. If you specify a variable of an type in the statement’s expression and press Enter, a for each constant will be generated automatically.
To get a list of all available code snippets, type Ctrl + k, Ctrl + x. This displays the Insert Snippet window in the code editor. You can navigate through the Visual C# snippet folders with the mouse to see the snippets. This feature also can be accessed by right clicking in the source code editor and selecting the Insert Snippet... menu item.