Chapter 7: PunchDroid: An Android Punch Bug Game

In This Chapter

• Using the TinyWebDB component for multi-handset communication

• Using a timer to poll a datasource to keep apps up-to-date

• Employing a choose block for variable situations

• Implementing a multiplayer game between handsets

• Using check boxes as radio buttons

Remember playing the Punch Bug game back in the day when Volkswagen Beetles were a rare sight? You know the game. Whenever someone sees the distinctive little car, she would punch the other player on the arm and yell “Punch Bug!” and get a point. Well, the game you played as a child is about to get an update. The PunchDroid project allows your user to play the same game regardless of the distance between the players. Whether you’re looking for VW Beetles or Android phones, PunchDroid is a fun little game that can be played between two phones.

This application introduces the TinyWebDB component. Previously you used the TinyDB component to store data between application settings. TinyDB stores its data on the local device as an .XML file in the Settings section of the Android file system. TinyWebDB, by contrast, uses either Wi-Fi or cell phone networks to communicate with a database running on a Web server. The TinyWebDB service runs on a Web server, accepts incoming data, and responds to requests for stored data from your application. TinyWebDB is an important part of your skill set for creating connected applications and devices.

Note.eps

The TinyWebDB component uses a service URL to connect to the TinyWebDB service running on a Web server. Google has provided a test TinyWebDB service that is used in this project. You share that TinyWebDB service with every other person testing the TinyWebDB service. If you want a TinyWebDB service to use for just yourself and your applications, set up your own TinyWebDB service using the instructions in Appendix B. If you do not set up your own TinyWebDB service, you can expect to have someone else using this project chapter to overwrite your data.

Creating the PunchDroid Application

The key concepts I introduce in this project include

• Using the TinyWebDB service

• Handling returns from the TinyWebDB service

• Creating test conditions and multiple test conditions for complex tests

• Creating check boxes that act as radio buttons to force a choice

• Keeping and storing data between multiple handsets

The TinyWebDB service as it is used in this project can be used for multiplayer games, reference applications, or data mining applications. The advanced use of the TinyWebDB service is integral to taking advantage of the networked nature of Android smartphones. Think of the TinyWebDB not just as a data storage component, but as a thread that can tie multiple devices together and as a gateway to other devices. Advanced hacks are available on the Google App Inventor forums that turn the TinyWebDB service into an even more powerful gateway to other data sources. Learning the basic fundamentals of how TinyWebDB works is the first step towards more advanced uses of the component.

Your design

Figure 7-1 shows the sketched user interface for the PunchDroid application. The application has two VirtualScreens: One for the main play interface and one for the Settings interface.

Figure 7-1: The PunchDroid design sketches

9781119991335-fg0701.tif

The PunchDroid application is a multiplayer game that can be played by two players across the Internet. The user inputs their name and player number to identify themselves uniquely. The user then has a button to tap whenever the user sees a VW Bug.

Your primitives

These are the core programming concepts for this app, broken down into simple statements to aid in programming the design goals:

• A button that increments the user’s score

• A method to distinguish between the local and the remote player

• A method to transmit data to the opposing player’s phone

• A method to store the user’s name and player number between sessions

• A method to start a new game

• A method to display the local and remote players’ scores

• A method to keep both players' scores up to date

Make sure you download the Chapter 7 project files from the companion Web site for this book and save them somewhere where you can find them easily during the project build. See this book's Introduction for more on downloading the files from the Web site.

Your progression

These are the basic steps you take in order to build the application:

1. Place the VirtualScreen1 user interface elements.

2. Place the VirtualScreen2 user interface elements.

3. Define the variables required for local information storage.

4. Build out the Screen1.Initialize event.

5. Build blocks to handle the events on the Settings page.

6. Build blocks to handle the events on the main play page.

Getting Started on the PunchDroid Application

The PunchDroid application introduces you to a major component for interacting with data across the Internet: TinyWebDB. TinyWebDB is very useful for getting information to multiple handsets and allowing data to persist beyond the state of the local application. The PunchDroid application is a fun proof-of-concept application that can be expanded to fit any number of entertaining game ideas. The PunchDroid app only allows for two players, Player1 and Player2. The user determines when they start the application for the first time whether they are going to be Player1 or Player2.

1. Start a new project from the My Projects window. Name the project PunchDroid1_0.

2. Select the Screen1 component in the Components column. Uncheck the Scrollable property and change the Title property to PunchDroid 1.0.

Note.eps

Make sure the Display Invisible Components in Viewer check box is selected. That keeps even your invisible screen arrangements visible in the Design view.

3. Click on the Icon property field to bring up the drop-down list. Click the Add button to bring up the Upload File pop-up. Click the Choose File button and navigate to your Chapter 7 project files. Double-click the punchdroid_ico.png file to select the icon file for upload. Click OK on the Upload File pop-up.

The PunchDroid application will have two screens. VirtualScreen1 is the main play screen, where the user can tap the I Got One! button to increment their score. VirtualScreen2 is the Settings screen, where the user can set whether they are Player1 or Player2 and enter their name.

4. From the Design view, drag and drop two VerticalArrangements onto the Viewer. Rename the first VerticalArrangement VirtualScreen1. Rename the second VerticalArrangement VirtualScreen2.

Tip.eps

As you build the user interface, remember to refer to the design sketches or Figure 7-2 if you get confused.

5. Uncheck the Visible property for both VirtualScreens. The Screen1.Intialize event decides whether the user should set their settings first or proceed to the main play screen. Set the Width and Height property of both VirtualScreens to Fill Parent.

6. Drag a HorizontalArrangement into the VirtualScreen1 component. This holds the two score boxes that display the score for the two players. Each score box is a VerticalArrangement that displays the player’s name label above their score label.

7. Set the Width and Height property of the HorizontalArrangement1 to Fill Parent.

8. Drag and drop two VerticalArrangements into the HorizontalArrangement1. These are the boxes to hold the labels for score display.

9. Set the Width and Height property on the VerticalArrangements1 and 2 to Fill Parent.

10. Drag and drop another HorizontalArrangement below the HorizontalArrangement1 that contains the score boxes. Set the Width property to Fill Parent. Don’t set the Height property to Fill Parent. You want the buttons pushed to the bottom of the screen. This arrangement holds the I Got One! button and the button used to access the settings page.

Now place all the Basic palette components:

1. Drag and drop a label into the VerticalArrangement1 that is the left box for your score display. (See Figure 7-2.) Rename the label lblThisPlayerName. Change the default Text property to Your Score:. This label is changed programmatically when your user inputs their name, but having this default text to begin with helps you if you have to troubleshoot. It also helps you get a feel for the overall layout as you build the user interface.

Figure 7-2: The completed user interface for PunchDroid

9781119991335-fg0702.tif

2. Drag and drop a second label below the label you just named lblThisPlayerName. Rename the new label lblThisPlayerScore. Change the Alignment property to Center with the Property drop-down list. Check the FontBold property check box. Set the FontSize property to 75 and the FontTypeFace to monospace. Delete the default text in the Text property field. This label displays the score of the local user.

Now set up the right score box in the same way:

1. Drag and drop a label into the VerticalArrangement2 that is the right score box in VirtualScreen1. Rename the label lblOtherPlayerName. Change the default Text property to Their Score:. Again this is mostly for your benefit as the actual text changes to the name of the other player.

2. Drag and drop a label directly below the lblOtherPlayerName label. Rename the new label lblOtherPlayerScore. Change the Alignment property to Center with the Property drop-down list box. Check the FontBold property check box. Set the FontSize property to 75 and the FontTypeSpace to monospace. Delete the default text in the Text property. This label displays the remote player’s score whether he is Player1 or Player2.

Now place the buttons for play and for the Settings screen:

1. Click on the HorizontalArrangement2 to highlight it in the Design view. Drag and drop a button into the Horizontal Arrangement2 component. Rename the Button btnGotOne. Set the FontSize property to 35. Change the default Text property to I Got One!.

2. Drag and drop another button to the right of the btnGotOne button. Rename the button btnSettings. Change the default Text property to Settings.

VirtualScreen1 is now completed and should look like VirtualScreen1 in Figure 7-2.

Follow the next steps to set up VirtualScreen2. VirtualScreen2 is the screen for your player settings. It contains check boxes to allow your user to specify whether they are Player1 or Player2. It also contains the Player Name setting and the Reset Game button:

1. Drag and drop a CheckBox component into the VirtualScreen2. Rename the CheckBox component chkPlayer1. Change the default Text property in the Properties column to Player1.

2. Drag and drop a second CheckBox component into the VirtualScreen2 below the chkPlayer1 check box. Rename the CheckBox component chkPlayer2. Change the default Text property to Player2. These two check boxes allow the user to select whether they are Player1 or Player2. Because they must be one or the other but cannot be both, you set up special logic that requires one to be checked but does not allow both to be checked.

3. Drag and drop a label below the chkPlayer2 check box. Rename the label lblName. This label marks the following text box as the spot for your user to put their player name. Change the default Text property to Name:.

4. Drag and drop a TextBox component below the lblName label. Rename the TextBox txtPlayerName. Set the Hint property to Enter Player Name. Change the default Hint property to Enter Player Name. This is the TextBox where the user can enter her name. That name is stored locally and uploaded to TinyWebDB.

5. Drag and drop a button below the txtPlayerName TextBox. Rename the button btnSaveSettings. This button is a major event in your application. It stores all the settings and initializes the game. You need one more button to give the players the option of resetting the score and starting a new game. However, you don’t want it to be accidently hit, so you use a little vertical space to separate it from the other elements on the Settings screen.

6. Drag and drop a label below the btnSaveSettings button. Rename the label padLabel1. This label acts as padding between the buttons. Remove the default text in the Text property. Set the Height property to Fill Parent. This pushes the maximum vertical space between the two buttons on the Settings screen.

Now you need to place all of the application’s non-visible components. You need to add TinyWebDB as the data component for communication between the player’s phones. You also need to add a TinyDB component to store the local user’s name and player number locally. You must add a notifier to provide pop-up notifications for several different application events you will program later. Finally, you should add a Clock component for keeping both players’ games up-to-date on a reasonable schedule.

1. Drag and drop a TinyWebDB component from the Not Ready for Prime Time palette. The TinyWebDB component makes URL calls against a Web database application running on a Web server. The TinyWebDB component has one very important property: The ServiceURL property tells the TinyWebDB component where the Web database and application are located.

Note.eps

The component has a default ServiceURL property value of appinvtinywebdb.appspot.com. This URL points to a testing Web database that Google has set up on the Google AppSpot servers. The testing database is for testing and development, not for apps you actually want to use. It is subject to going down frequently. It is also used by anyone else testing a TinyWebDB component in an application. This makes it slow sometimes and means that your data can accidently be overwritten. Appendix B shows how to set up your own private WebDB to work in conjunction with TinyWebDB.

For the purposes of learning and creating the PunchDroid application, the testing database at http://appinvtinywebdb.appspot.com is sufficient.

2. Drag and drop a TinyDB1 component from the Basic palette.

3. Drag and drop a Notifier component from the Other Stuff palette.

4. Drag and drop a Clock component from the Basic palette.

Your user interface should look like Figure 7-2. Make sure that the VirtualScreens do not have the Visible property checked. Make sure the Display Invisible Components in Viewer check box is selected. Check that all arrangements have the Fill Parent property set as the Width and Height properties.

Handling the Settings page events

Switch over to the Blocks Editor. Click the Open the Blocks Editor button if the Blocks Editor isn’t already open. The PunchDroid programming logic is almost entirely event-driven. The application does most of the work and then communicates the result to TinyWebDB for the PunchDroid application running on another device to download. You need to handle each of the button events on the user interface as well as one special event from TinyWebDB that is not user-generated.

You need to make provisions for storing several pieces of information locally on your application. For local storage, you use variables. When a variable value changes, you have to communicate it to TinyWebDB so that it can be accessed by the other player. You also need to make provisions for some information to be locally persistent. In other words, you use variables for storing immediate data locally, TinyDB for storing long-term user data, and TinyWebDB for storing persistent game information.

First you have to create all of the needed variables. You use typeblocking predominately in this project. I use the App Inventor syntax to represent the blocks. A quick review of App Inventor typeblock syntax: The set Label1.Text to block is referred to in App Inventor typeblocking as Label1.Text [to].

Typeblock and create the following variables.

Remember.eps

You create a new variable by typeblocking the keyword Variable and pressing Enter. You can then change the name to a unique name that is memorable to you. You need to plug a default value (usually a blank value) into the newly created variables.

varPlayerName: This stores the name of the player who is using the phone. Snap in a blank text block.

varPlayerName1: This stores Player1’s name, whether he is on this phone or another. Snap in a blank text block.

varPlayerName2: This stores Player2’s name, whether she is on this phone or another. Snap in a blank text block.

varPlayerNumber: This stores the user’s player number (Player1 or Player2).

VarPlayerScore1: This stores Player1’s score.

varPlayerScore2: This stores Player2’s score.

Because a single game may well last across multiple instances of the application, you need to store the user's player number and name locally in TinyDB. Otherwise, the user would have to initialize those settings every time the application starts.

The Screen1.Initialize event checks to see whether TinyDB has player number information stored. If it does, the user has set his settings previously. If the user has not set their settings, the settings page needs to be displayed. If the user has set their settings, all the variables need to be initialized and the main game screen displayed. Some of the variable information comes from TinyDB, such as PlayerName and PlayerNumber. The others are initialized with calls to TinyWebDB.

First build an IfElse block to test if TinyDB has stored information. The IfElse block handles two cases. The first directs the user to the Settings page; the second initializes the variables:

1. Typeblock the Screen1.Initialize event handler block.

The IfElse control block is your test decision-maker for the .Initilize event. With the .Initialize block selected, typeblock an IfElse block and snap it into the .Intialize event block.

2. Build the test for the IfElse block to test whether any data is stored in TinyDB. You do this by posing the question, “Does the contents of a specific TinyDB tag equal nothing?”

Throughout this project, you use the variable names for all of the database tags minus the variable prefix. So, varPlayerNumber stored in a database uses the tag playernumber and varPlayerName uses the tag playername. TinyDB and TinyWebDB tags are not case-sensitive, but using all lowercase characters can help differentiate them from variable names in your head.

3. Select the IfElse block and typeblock the equals comparison operator (=) and snap it into the test socket on the IfElse block. Typeblock a TinyDB1.GetValue block by typing TinyDB1.GetValue. Make sure it snaps into the first socket on the comparison operator. Now it needs a tag to try to pull data with. If the user has entered any settings, the playernumber tag contains data. Typeblock a text block and replace the default text with playernumber. Snap it into the .GetValue block. Typeblock a text block and remove the default text, leaving a blank text block. Snap the blank text block into the second socket on the comparison operator.

The then-do first case of the IfElse block is fairly straightforward to build. If the user needs to set their player information, you need to make the Settings screen visible and create a pop-up to inform the user what is expected of them.

4. Typeblock the VirtualScreen2.Visible [to] block and snap it into the then-do socket on the IfElse block. Typeblock a true block and snap it into the .Visible block.

5. Typeblock the Notifier1.ShowMessageDialog block and snap it into the then-do socket on the IfElse block. This block is set up to notify the user that they need to enter their player information. Typeblock a text block and replace the default text with You need to set your player information. Snap the text block into the message socket on the .ShowMessageDialog block. Typeblock another text block and set its text to First run!.

Snap that text block into the title socket on the .ShowMessageDialog block. Typeblock a third text block and set its text to OK. Snap this text box into the buttontext socket on the .ShowMessageDialog box.

The Notifier component has a special event handler for whenever you use a notification that has a button to press. The Notifier block you just placed has an OK button. Clicking the OK button signals the Settings screen to become visible. You build the instructions for the OK button press in the Notifier1.AfterChoosing event handler.

6. Typeblock the Notifier1.AfterChoosing event handler. Just to keep everything clean and symmetrical, you handle both VirtualScreens with the AfterChoosing event handler.

Note.eps

The .AfterChoosing block is the event called when the OK button is clicked. It is also the event that is called if you use a Yes/No or other multi-button notification. The .AfterChoosing event has a parameter that contains the results or choice that your user selected. In this notification, the user has only one choice: OK. In multi-button notifications, the user’s choice is contained in the parameter value block named whatever is snapped into the choice socket.

Tip.eps

App Inventor should automatically populate the choice socket on the .AfterChoosing event. However, sometimes the Blocks Editor glitches and that socket winds up empty. When that happens, you can populate the choice socket with a name block and change the name block name to something memorable. The default name block is named choice.

7. Typeblock the VirtualScreen1.Visible [to] block and snap it into the .AfterChoosing event handler. Typeblock a false block and snap it into the socket on the VirtualScreen1 block.

8. Typeblock the VirtualScreen2.Visible [to] block and snap it in below the previous block. Typeblock a true block and snap it into the VirtualScreen2 block.

9. Now whenever the OK button on the notification is tapped, the Settings screen becomes visible to enable the user to set the player settings.

That’s the complete first case of the IfElse block that is executed on start-up. Flip ahead to check out Figure 7-3 if you have any issues.

Next, you need to build the second case else-do socket on the IfElse block. If the settings have been set and stored in TinyDB, that information as well as whatever information exists in the TinyWebDB needs to be used to initialize your variables. You set the main play screen to visible and then start initializing variables with database calls:

1. Typeblock the VirtualScreen1.Visible [to] block and snap it into the else-do socket on the IfElse block in the Screen1.Initialize block. Typeblock a true block and snap it into the VirtualScreen1.Visible block.

2. Now for your first variable initialization call: Typeblock the varPlayerNumber [to] block and snap it under the VirtualScreen1 block.

3. To pull information out of TinyDB and place it into a variable or label, use the TinyDB1.GetValue block socketed directly into where you want the data to go.

4. Typeblock the TinyDB1.GetValue block and snap it into the varPlayerNumber [to] block. Now you need to tell the .GetValue block what tag to pull the data from. Typeblock a text block and replace the default text with playernumber. Snap the text block into the .GetValue tag socket. You populate the tag playernumber with the correct data when you handle the Save Settings button event on the VirtualScreen1.

5. When the user enters their player number and name, your Save Settings event stores all the information under the correct tags.

Now you will initialize the varPlayerName variable. Typeblock the varPlayerName [to] block and snap next in the else-do socket. Typeblock a TinyDB1.GetValue block and snap it into the varPlayerName [to] socket. Typeblock a text block and replace the default text with playername.

Now you need to create a series of calls to the TinyWebDB for the other variable values. TinyWebDB works differently than TinyDB. TinyDB stores information on the local handset in the Settings location for applications on your phone. TinyWebDB, on the other hand, is a simple database that runs on a server located on the Internet. (See Appendix B for information on how to set up your own private Web database.) That means that when you submit a call for data to the TinyWebDB, the request is sent over the Internet to the URL placed in the ServiceURL property. The response is then sent over the Internet back to the phone.

The upshot of this is that data calls to TinyWebDB are not instantaneous as they are with TinyDB. Whenever you use TinyWebDB, you make calls for data, but you can’t actually place that data or process that data until it actually comes back to the phone. You handle the responses to data request to the TinyDBWeb service using a special event provided by the TinyWebDB component. The TinyWebDB1.GotValue event is used to process any incoming data requested from any other blocks in your application. You build that event later; for now, you are just going to tell the TinyWebDB component to get the data for the variables based on the appropriate tags. You actually place that data in the variables in the .GotValue event handler.

You continue building the Screen1.Initialize event in the following steps by placing all of the .GetValue blocks in the IfElse block:

1. Typeblock a TinyWebDB1.GetValue block and snap it in the else-do socket on the IfElse block. Typeblock a text block for the tag and replace the default text with playername1. Snap the text block into the tag socket on the .GetValue block. This block sends the request across the Internet for the data stored under the tag playername1, which is the name of the player who declared himself as Player1.

2. Typeblock a TinyWebDB1.GetValue block and snap it in. Typeblock a text block for the tag and replace the default text with playername2. Snap the text block into the tag socket on the .GetValue block. This block sends the request for the data stored under the tag playername2.

3. Typeblock a TinyWebDB1.GetValue block and snap it in next. Typeblock a text block for the tag and replace the default text with PlayerScore1. Snap the text block into the tag socket on the .GetValue block.

4. Typeblock a TinyWebDB1.GetValue block and snap it in next. Typeblock a text block for the tag and replace the default text with PlayerScore2. Snap the text block into the tag socket on the .GetValue block.

After you’ve built all the calls to the TinyWebDB for the Screen1.Initialize block, you need to place the user’s player name in the Label in their score box.

Typeblock the lblThisPlayerName.Text [to] block and snap it in as the last block in the Screen1.Initialize event handler block. Typeblock the varPlayerName global variable block and snap it into the text block. This sets the score label in the score box on the right side to represent the local player’s name.

Your completed Screen1.Intialize block should look like Figure 7-3.

Figure 7-3: The completed Screen1.Initialize event handler

9781119991335-fg0703.tif

Next, get ready to build the programming logic for the components and events on the Settings screen of your application. You have several components to handle and two buttons to handle events for on the Settings page. You need to handle the Save Settings button event, and you also need to set up logic that ensures that one check box is selected but not both. You also need to handle the event for the New Game button.

The logic for the check boxes seems complex, but the check boxes come with a very useful event handler, the CheckBox.Changed event. This event is called whenever the value of the check box is changed. A check box is always either true or false. If that value changes, you can build logic to check on and or change the other check box. You need to build logic that says, “When the check box is changed, set the other check box to the opposite value.”

1. Typeblock the chkPlayer1.Changed event handler. With the chkPlayer1.Changed block selected, typeblock the chkPlayer2.Value [to] block.

Note.eps

This is the .Value [to] block opposite of the event handler. Make sure the .Value [to] block snaps into the event handler. The ChkPlayer1.Changed has the chkPlayer1.Value block and vice versa.

2. Typeblock a not block and snap it into the to socket on the chkPlayer2.Value block. Now typeblock the chkPlayer1.Value reporting block and snap it into the not block. The logic of these blocks now reads, Set the Value of chkPlayer2 to the opposite of chkPlayer1 whenever chkPlayer2 is changed. (See Figure 7-4.) You set up the same thing for the chkPlayer2 check box next.

3. Typeblock the chkPlayer2.Changed event handler. Typeblock the chkPlayer1.Value [to] block and snap it into the event handler. Typeblock a not block and snap it into the to socket on the .Value block. Typeblock the chkPlayer2.Value reporting block and snap it into the not block. Your Player1 and Player2 selection check box event handlers should now look like Figure 7-4.

Whenever one check box is selected, the other is automatically set to the opposite value, ensuring that one but not both are always selected.

Figure 7-4: The completed .Changed event handlers

9781119991335-fg0704.tif

You use a new block to create the logic in the event handler for the Save Settings button. The choose block allows your blocks to make a choice about which value to use in a string or logic pattern. In this case, you use the choose block to choose which number to store with the playernumber tag in TinyDB. If the user has selected the Player1 check box, your blocks will store the value 1 in with the tag playernumber. If the user has selected the Player2 check box, your blocks store the value 2 with the tag playernumber. That way, when the Screen1.Initialize event pulls the information from TinyDB, your application has the correct value in the varPlayerNumber variable:

1. Start off by typeblocking the btnSaveSettings.Click event handler. Typeblock a TinyDB1.StoreValue block.

The TinyDB1.StoreValue block allows you to save any data with a tag so that it can be retrieved later. You store the player number with the tag playernumber.

2. With the TinyDB1.StoreValue block selected, typeblock a text block and replace the default text with playernumber. Make sure the text block is snapped into the tag socket on the .StoreValue block.

3. Typeblock a choose block and snap it into the valueToStore socket on the .StoreValue block. The choose value block chooses which number to return to the valueToStore socket based on a test much like an IfElse block.

Now build the test for the choose block. The logic of your test goes like this: If the chkPlayer1 value is set to “true” then return the value in the first return socket; otherwise, return the value in the second return socket.

1. Typeblock an equals comparison operator (=) and snap it into the test socket on the choose block. Typeblock the chkPlayer1.Value reporting block and snap it into the first socket on the comparison operator. Typeblock a true block and snap it into the second socket on the comparison operator.

Now you need to set the values that the test case will choose between. If the test evaluates true, you want the value 1 to be stored because that is the number the player chose. If the test case evaluates to false, you want to return the value 2. You know that if chkPlayer1.Value is false, chkPlayer2.Value must be set to true because one of the check boxes must be checked.

2. Typeblock a numeral 1 block and snap it into the then-return socket on the choose block.

3. Typeblock a numeral 2 block and snap it into the else-return socket on the choose block.

Now you use the value just stored in TinyDB to set the value of the variable varPlayerNumber so the player can start playing.

1. Typeblock the varPlayerNumber [to] block and snap it in under the TinyDB1.StoreValue block. Typeblock a TinyDB1.GetValue block and snap it into the varPlayerNumber block. Typeblock a text block and replace the text with the tag text playernumber. Snap it into the tag socket on the .GetValue block. Because TinyDB instantly stores and returns data, we can populate the variable with TinyDB data immediately after storing it.

Next, store the text from the txtPlayerName text box in TinyDB so your application can remember your player’s name.

2. Typeblock a TinyDB1.StoreValue block and snap it in next in the btnSaveSettings.Click event handler. Typeblock a text block and replace the default text with playername. Snap it into the tag socket on the .StoreValue block. Typeblock the txtPlayerName.Text reporting block. Snap the text block into the valueToStore socket on the .StoreValue block. The text from the txtPlayerName text box is stored in TinyDB under the tag playername.

Next, place the player name in the varPlayerName variable and set the label on the main play screen to the player’s name.

3. Typeblock the varPlayerName [to] block and snap it in next under the .StoreValue block. Typeblock a TinyDB1.GetValue block and snap it into the to block on the varPlayerName block. Typeblock a text block and replace the text with playername. Snap the text block into the tag socket on the .GetValue block.

4. Now set the lblThisPlayerName label on the main play screen to represent the name just entered. Typeblock the lblThisPlayerName.Text [to] block and snap it in under the previous block. Typeblock the varPlayerName variable reporting block and snap it into the lblThisPlayerName.Text block.

Next, you need to store the player’s name in TinyWebDB under the tag that represents the player’s number. In other words, if the player chose to be Player1, the player’s name should be stored under the tag playername1 and playername2 if the player chose to be Player2. To accomplish storing the local player’s name with their selected player number, use a text join block to join the text playername with the value of the variable varPlayerNumber and use the resulting text string as the tag to store the value of the varPlayerName. If that is confusing, look at Figure 7-5. You can see that the player’s name is joined with the player’s selected number. That string is used to store the player’s name. For instance, if the player has entered his name as Joe and selected the Player2 check box, the following would be stored:

playername2 = Joe

The application uses TinyWebDB to retrieve the tags playername1 and playername2 and place them in the correct variable. In the Screen1.Intialize event, you created the TinyWebDB calls that retrieve those values. You handle those returns when you set up the .GotValue event a little later:

1. Typeblock a TinyWebDB1.StoreValue block and snap it into the btnSaveSettings event handler. Typeblock a join block and snap it into the tag socket on the .StoreValue block. Typeblock a text block and replace the text with playername. Snap the text block into the first socket on the join block. Next, typeblock the varPlayerName block and snap it into the second socket on the join block.

2. Typeblock the varPlayerName global variable block and snap it into the valueToStore socket on the .StoreValue block.

Now the player’s name is stored with the tag playername# with the number depending on what number is stored in the varPlayerNumber.

When the user taps the Save Settings button, you want to also retrieve the values stored in the TinyWebDB for both tags, playername1 and playername2. That way, no matter what player this player is, the variables are populated with the player’s name. Remember that for TinyWebDB, we can only make the calls to TinyWebDB with the tags. We must actually handle the data later when it is returned from the TinyWebDB service on the Internet.

3. Typeblock a TinyWebDB1.GetValue block and snap it in below the .StoreValue block in the btnSaveSettings.Click event. Typeblock a text block and replace the text with the tag text playername1. Snap the text block into the tag socket on the .GetValue block.

4. Typeblock another TinyWebDB1.GetValue block and snap it in next in the .Click event handler. Typeblock a text block and replace the text with playername2 this time. Snap it into the tag socket on the .GetValue block.

After storing all the user settings in the appropriate variables and databases, you need to make the main play screen appear and the Settings screen disappear:

1. Typeblock the VirtualScreen1.Visible [to] block and snap it in under the previous .GetValue block. Typeblock a true block and snap it into the .Visible block.

2. Typeblock the VirtualScreen2.Visible [to] block and snap it in as the last block in the event handler. Typeblock a false block and snap it into the .Visible block.

Your completed btnSaveSettings.Click event handler should look like Figure 7-5.

Figure 7-5: The completed btnSaveSettings.Click blocks

9781119991335-fg0705.tif

The btnNewGame.Click event handler is fairly easy to set up. To start a new game, you just have to reset all the score information stored locally in variables and stored in the TinyWebDB:

1. Typeblock the btnNewGame.Click event handler. Typeblock the varPlayerScore1 [to] block. Snap the variable block into the event handler. Snap a numeral 0 block into the to socket.

2. Typeblock the varPlayerScore2 [to] block and snap it in next in the btnNewGame.Click event handler. Snap a numeral 0 block into the to socket.

3. Typeblock the TinyWebDB1.StoreValue block and snap it next in the btnNewGame event handler. Use a text block to set the tag to playerscore1. Use a number block to set the value socket to 0.

4. Typeblock another TinyWebDB1.StoreValue block and snap it in under the previous block. Use a text block to set the tag to playerscore2. Use a number block to set the value socket to 0.

Now you need to reset the score display labels on the main play screen to display zero:

1. Typeblock the lblOtherPlayerScore.Text [to] block. Snap it in after the last TinyWebDB block. Use a numeral 0 block snapped into the to socket to set the variable to zero.

2. Typeblock the lblThisPlayerScore.Text [to] block and snap it in next. Use a numeral 0 block to set the variable value to zero.

Your completed btnNewGame.Click event handler should look like Figure 7-6.

Figure 7-6: The completed btnNewGame.Click blocks

9781119991335-fg0706.tif

Handling events on the main play screen

Now that you have handled the events on the Settings screen, it’s time to handle the events on the main play screen. There are two user events to handle on the main play screen: the Settings button, which allows the user to bring up the Settings screen, and the I Got One button, which is the main play event. Clicking the I Got One! is the digital equivalent of punching your friend on the shoulder and yelling “Punch Bug!”

To handle the Settings button, make the main play screen invisible and make the Settings screen visible.

1. Typeblock the btnSettings.Click event handler. If necessary, move it to a clear area of your workspace. Remember to right-click on the workspace to organize and handle your blocks.

Tip.eps

Using the “Right-click, select Collapse All Blocks, right-click again, and select Organize All Blocks” routine should become habit when you are dealing with a large number of large event handlers or long block routines.

With the btnSettings.Click block selected, typeblock the VirtualScreen1.Visible [to] block. Typeblock a false block and snap it into the .Visible block.

2. Typeblock the VirtualScreen2.Visible [to] block and snap it in under the previous block. Typeblock a true block and snap it into the .Visible block.

If the user has reopened the PunchDroid application from a previous game, the txtPlayerName text box might not have any text in it even though database calls have been used at the start of the application to populate the variable. You need to place the contents of the variable in the TextBox component so the user gets the sense of data and player persistence.

3. Typeblock the txtPlayerName.Text [to] block and snap it under the previous .Visible block. Typeblock the varPlayerName global variable block and snap it into the to socket on the text block.

Your completed btnSettings.Click event handler should look like Figure 7-7.

Figure 7-7: The btnSettings.Click blocks

9781119991335-fg0707.tif

The most important event on the main play screen is the I Got One! button that the user taps to indicate that they have just spotted whatever item the game is centered around. When the user taps the I Got One! button, the appropriate player score variable should increment and the appropriate score display label should display the new score. Also, the new score needs to be sent to the TinyWebDB. Before you increment the score, you use the event as an opportunity to send a request to the TinyWebDB for any updates to the other players score. You use the choose block again to determine which call should be made. In reality, you could just send a call for both PlayerScore1 and PlayerScore2, but for the purpose of this project, you use the choose block again for a little extra practice in using it:

1. Typeblock the btnGotOne.Click event handler and drag it to a clear workspace.

2. First build the TinyWebDB call to check on the other player’s score. Typeblock the TinyWebDB1.GetValue and snap it into the event handler. Typeblock a make text block and snap it into the tag of TinyWebDB1.GetValue. The make text block creates a single string for the tag from the text PlayerScore and the opposite of whatever number is in varPlayerNumber. Typeblock a text block and replace the default text with PlayerScore. Snap it into the text socket on the make text block. Typeblock a choose block and snap it into the next text block on the make text block.

Now build the test for the choose block that says, “If the varPlayerNumber value is 1, return the numeral 2 to the make text; otherwise, return the numeral 1.”

3. Typeblock an equals comparison operator (=) and snap it into the test socket on the choose block. Typeblock the varPlayerNumber global variable block and snap it into the first socket on the comparison operator. Typeblock a numeral 1 number block and snap it into the second socket on the comparison operator.

4. Now typeblock a numeral 2 number block and snap it into the then-return block.

5. Typeblock a numeral 1 block and snap it into the else-return block on the choose block.

Now the make text block concatenates the text PlayerScore and either the numeral 1 or 2 and uses it as one string for the TinyWebDB tag.

Next you need to increment the appropriate variable so that the player’s score goes up when the I Got One! button is clicked. If the varPlayerNumber is 1, varPlayerScore1 should increment. If the varPlayerNumber is 2, the varPlayerScore2 should go up. You can use a simple IfElse block to increment the right variable and then store the result in the TinyWebDB:

1. Typeblock an IfElse block and snap it into the btnGotOne.Click event handler. Build the test condition to check if the varPlayerNumber contains the value 1. If it does, the first case then-do socket should increment the varPlayerScore1. Otherwise, the second case else-do socket should increment the varPlayerScore2 variable.

2. With the IfElse block selected, typeblock an equals comparison operator. Typeblock the varPlayerNumber global variable block and snap it into the first socket on the comparison operator. Typeblock a numeral 1 number block and snap it into the second socket on the comparison operator.

Now build the then-do case for when the test evaluates to true. If the test is true, increment varPlayerScore1 and send the new value to the label and the TinyWebDB.

3. Typeblock the varPlayerScore1 [to] block and snap it into the then-do socket on the IfElse block. Typeblock an addition operator by typing a plus sign (+) and pressing Enter. Snap the additive operator into the to socket on the varPlayerScore1 block. Typeblock the varPlayerScore1 global reporting block and snap it into the first socket on the additive operator block. Typeblock a numeral 1 number block and snap it into the second socket on the additive operator block. This takes the value of varPlayerScore1, adds one, and stores it back into the variable.

4. Now update the label with the new score. If this player is Player1, you use the lblThisPlayerScore to display the new score.

5. Typeblock the lblThisPlayerScore.Text [to] block and snap it in under the varPlayerScore1 incrementing block. Typeblock the varPlayerScore1 global variable reporting block and snap it into the lblThisPlayerScore.Text block. This updates the label with the latest score.

Now store the value of the varPlayerScore1 because it has changed in TinyWebDB:

1. Typeblock the TinyWebDB1.StoreValue block and snap it in under the label set block. Typeblock a text block for the tag and replace the default text with PlayerScore1. Snap the text block into the tag socket on the .StoreValue block. Typeblock the varPlayerScore1 reporting block and snap it into the valueToStore block on the .StoreValue block. This sends the contents of the varPlayerScore1 variable to the TinyWebDB to be stored under the tag PlayerScore1.

Your first case then-do socket should look like Figure 7-8.

If the IfElse block test evaluates to false, you want to increment the Player2 score and update the label and store it as well.

2. Typeblock the varPlayerScore2 [to] block and snap it into the else-do socket on in the IfElse block. Typeblock the additive (+) block and snap it into the to socket on the varPlayerScore2 block. Typeblock the varPlayerScore2 global variable block and snap it into the first socket on the additive block. Typeblock a numeral 1 number block and snap it into the second socket on the additive block. Again, this is the typical variable increment routine.

Now update the label with the new score. In the previous case for the then-do socket, if the local player was Player1, the lblThisPlayerScore.Text would be populated with the value of the varPlayerScore1. If this player is Player2, you want to set the lblThisPlayerScore.Text to the value of the varPlayerScore2 variable.

3. Typeblock the lblThisPlayerScore [to] block and snap it into the else-do socket under the varPlayerScore2 block. Typeblock the varPlayerScore2 global variable block and snap it into the lblThisPlayerScore.Text block.

Now store the changed variable in the TinyWebDB. Typeblock the TinyWebDB1.StoreValue block and snap it last into the else-do socket on the IfElse block. Typeblock a text block for the tag and replace the text with PlayerScore2. Snap the text block into the tag socket on the .StoreValue block. Typeblock the varPlayerScore2 global variable block and snap it into the valueToStore socket on the .StoreValue block.

Your completed btnGotOne.Click event handler should look like Figure 7-8.

Figure 7-8: The completed btnGotOne.Click event handler blocks

9781119991335-fg0708.tif

Every time you make a TinyWebDB call, the Web service eventually returns the requested tag and data. The TinyWebDB1.GotValue event handler has two special name/value blocks associated with it. When the TinyWebDB service returns the value that has been called for, it returns it in one package made up of two pieces: the tag and the value. The first piece is represented by the tagFromWebDB1 value block. This value block contains the tag that was called for that initiated the tag/value return. If the tag that was used to initiate the call was PlayerName1, the contents of the tagFromWebDB1 are PlayerName1. The second piece of the return package is the actual data that was stored with the tag. This piece of the package is represented by the valueFromWebDB1 value block. If the tag that was used to initiate the call has the PlayerName1 data stored under that tag, it is returned in the valueFromWebDB1 block.

This method of handling data returning from the TinyWebDB1 service is an asynchronous service fulfillment. That means that the order you request tag/value combinations is not necessarily the order they return in. Because of delays with servers and Internet pathways, you cannot assume that data arrives in the order it was requested. The tag/value pairing allows you to open a return package and say “Aha! This is the PlayerName1 tag I requested! I want to place the value I stored with that tag in a certain variable.” When that data returns, you need to decide what data has been returned and what you want to do with it. You use a series of nested If and IfElse blocks for every possible tag and data pair that might be returned. So far, you have stored information in the TinyWebDB under the following four tags:

PlayerName1

PlayerName2

PlayerScore1

PlayerScore2

The player name tags test whether the returned value is the same as the name in VarPlayerName. If it is the same, you don’t want to do anything with the data. But if the value of the returned data for a player name tag is not the same as the name stored in VarPlayerName, lblOtherPlayerName should be set to the value.

For the player score tags, you need to check whether the returned value is empty. App Inventor doesn’t like to do calculations on variables that have a null value. If you set the value of one of the varPlayerScore variables to null, when the application tries to increment the value, the application crashes. If there is no data in the value returned from the TinyWebDB service, you want to discard the data. If there is in fact a value in the returned response, you should update the appropriate variable.

Finally you set the lblOtherPlayerScore.Text to the appropriate score using the contents of the appropriate variable:

Note.eps

If the TinyWebDB1.GotValue does not have name blocks in the tagFromWebDB and valueFromWebDB sockets on the .GotValue event handler, you need to populate the sockets with name blocks from the Definitions drawer and change their names accordingly.

1. Typeblock the TinyWebDB1.GotValue event handler. With the TinyWebDB1.GotValue block selected, typeblock an If block. Build the test for the If block by typeblocking an equals comparison operator and snapping it into the test socket of the If block. With the comparison operator selected, typeblock the tagFromWebDB1 value block and snap it into the first socket on the comparison operator. Typeblock a text block and replace the default text with playername1. Snap it into the second socket on the comparison operator.

This test checks to see whether the incoming tag is the PlayerName1 tag. If it is, you need to decide what to do with the value that is connected to the tag.

If the test in the If block evaluates to true, you need to test to see whether the current player name stored in varPlayerName is the same as the value coming in from the TinyWebDB service. If it is the same, you can discard it. This is information your application already knows.

You use an IfElse block in a special way for this operation. You can use an IfElse block to say, in essence, “If this is true, do nothing; otherwise, do something.” You do this by leaving one of the cases without any blocks to execute. If the value from the Web database is the same as the value in varPlayerName, you do nothing with the value.

2. With the If block selected, typeblock an IfElse block and make sure it snaps into the If block. Typeblock the equals comparison operator (=) and snap it into the test socket on the IfElse block. Typeblock the valueFromWebDB1 block and snap it into the first socket on the comparison operator. Typeblock the varPlayerName global variable block and snap it into the second socket on the comparison operator. This tests to see whether the contents of valueFromWebDB and varPlayerName are the same.

If the test evaluates to true, you don’t want to do anything with the data, so leave the then-do socket empty on the IfElse block.

If the test evaluates to false, the incoming name is the name of the other player and you want to place it the lblOtherPlayerName label on the main play screen.

3. Typeblock the lblOtherPlayerName.Txt [to] block and snap it into the else-do socket of the IfElse block. Typeblock the valueFromWebDB1 value block and snap it into the lblOtherPlayerName block. These blocks set the label to the other player’s name.

Next you set the exact same series of blocks again, but this time for when the incoming tagFromWebDB1 is PlayerName2:

1. Typeblock an If block and snap it in below your first If block. With the If block selected, typeblock the equals comparison operator (=). Typeblock the tagFromWebDB1 value block and snap it into the first socket on the comparison operator. Typeblock a text block and replace the default text with PlayerName2. These blocks test to see whether the incoming tag is the PlayerName2 tag.

With the If block selected, typeblock an IfElse block and make sure it snaps into your second If block.

2. Select the IfElse block and typeblock an equals comparison operator. Typeblock the valueFromWebDB1 value block and snap it into the first socket on the comparison operator. Typeblock the varPlayerName global variable block and snap it into the second socket on the comparison operator. Again, if the value incoming from the Web database is the same as that stored in the PlayerName variable, you discard it.

3. Leave the then-do socket empty on the second IfElse block.

4. Typeblock the lblOtherPlayerName.Text [to] block and snap it into the else-do socket on the second IfElse block. Typeblock the valueFromWebDB1 block and snap it into the socket on the lblOtherPlayerName.Text block.

Your next two nested If blocks check whether the incoming tag is the PlayerScore tag and then check to see whether the value is empty. You could handle the incoming PlayerScore tag/value in much the same way as you handled PlayerName; instead, you use nested If statements with a not block. So instead of building the logic as, “If the value from the WebDB is empty, do nothing; otherwise, do something,” you build the logic as, “If the value from the WebDB is not empty, do this.” You see that the method you use here is a slightly neater and more graceful way to handle the situation:

1. Typeblock an If block and snap it in as the third If block down in your TinyWebDB1.GotValue event handler. Build the test for the If block by typeblocking a comparison operator and snapping it into the test socket on the If block. Typeblock the tagFromWebDB1 value block and snap it into the first socket on the comparison operator. Typeblock a text block and replace the text with PlayerScore1. Snap the text block into the second socket on the comparison operator.

This test checks to see if the incoming tag is the PlayerScore1 tag. If it is, you need to make sure that the data content isn’t a null value. App Inventor hates doing math on a variable with a null value.

2. With your third If block selected, typeblock another If block and make sure it snaps into your third If block’s then-do socket.

You use the not block to execute this nested If block only when the value from the WebDB is not null.

3. Typeblock a not block and snap it into the test socket of your nest If block. Typeblock an equals comparison operator and snap it into the not block. Typeblock the valueFromWebDB1 block and snap it into the first socket on the comparison operator. Typeblock a text block and delete the default text, leaving an empty text block. Snap the text block into the second socket on the comparison operator.

4. This test says, “If the valueFromWebDB1 is not null, the test is true.” If the test is true, you want to store the value in the varPlayerScore1 variable. Typeblock the varPlayerScore1 [to] and snap it into your nested If block. Typeblock the valueFromWebDB1 value block and snap it into the varPlayerScore1 block.

If the incoming tag is PlayerScore1 and the incoming value is not blank, the value is placed in the varPlayerScore variable.

As you can probably see, you can write this same logic in a third way that is even tighter. You can use an And block to chain conditions. You can create a test that says, “If this test and this test and this test are true, execute these blocks.” You can create as many and clauses as you need. As you build this If block, refer to Figure 7-9 for this slightly more complex but neater way to check for two things at once:

1. Typeblock a fourth If block and snap it in below the third If block. Typeblock an and block and snap it into the test socket of your fourth If block. Typeblock an equals comparison operator and snap it into the test socket on the and block. It creates another test socket for every test you put in it. With your first comparison operator selected, typeblock the tagFromWebDB1 value block . Typeblock a text block and replace the text with PlayerScore2. Snap the text block into the second socket on the comparison operator.

2. Select the and block and typeblock a not block. Make sure it snaps into the next test socket. Typeblock an equals comparison operator (=) and snap it into the not block. Typeblock the valueFromWebDB1 value block and snap it into the first socket on the comparison operator. Typeblock a text block and delete the default text. Snap the empty text block into the second socket on the comparison operator.

Now you have a test that asks that two conditions evaluate as true before the contained blocks are executed.

3. Typeblock the varPlayerScore2 [to] and snap it into the then-do socket on your fourth If block. Typeblock the valueFromWebDB1 value block and snap it into the varPlayerScore2 block.

At this point, you have handled every possible incoming tag from the TinyWebDB component. When you are building large projects, it is sometimes helpful to keep a list of the tags/values you use throughout your application. Every time you request data from the TinyWebDB component, it has to be handled with the .GotValue event when it arrives from the Web database.

Finally, set the OtherPlayerScore label with the appropriate variable value:

1. Typeblock an IfElse block and snap it into the .GotValue block as the last block. Typeblock an equals comparison operator. Snap it into the test socket. Typeblock the varPlayerNumber global value block and snap it into the first socket on the comparison operator. Typeblock a numeral 1 number block and snap it into the second socket on the comparison operator. If the player number is 1, the lblOtherPlayerScore.Text should be set to the value of the varPlayerScore2. If the varPlayerNumber is not 1, the label should be set to the value of varPlayerScore1.

2. Typeblock the lblOtherPlayerScore.Text [to] block and snap it into the then-do case of your IfElse block. Typeblock the varPlayerScore2 global variable block and snap it into the lblOtherPlayerScore.Text block.

3. Typeblock another lblOtherPlayerScore.Text [to] block and snap it into the else-do socket of your last IfElse block. Typeblock the varPlayerScore1 global block and snap it into the lblOtherPlayerScore.Text block.

Your completed TinyWebDB1.GotValue event handler should look like Figure 7-9.

To keep your player opponents and scores up-to-date, create a clock timer event that regularly polls the TinyWebDB service to have it return an updated score. Reuse blocks you already have built to make the database call.

Locate the btnGotOne.Click event handler on your workspace. The first block in the btnGotOne.Click event handler is the TinyWebDB1.GetValue block, which uses a choose block to decide what tag to request. Click on the TinyWebDB1.GetValue block and copy it to memory by pressing Crtl+C. Close the btnGotOne.Click event handler. Click on an empty workspace and typeblock the Clock1.Timer event handler. Press Ctrl+V to paste a copy of the TinyWebDB1.GetValue block from the btnGotOne.Click event handler.

Figure 7-9: The complete TinyWebDB1.GotValue event handler blocks

9781119991335-fg0709.tif

Snap the copied blocks into the Clock1.Timer event handler. Your Clock1.Timer event handler should look like Figure 7-10.

Based on the timer value you entered in the TimerInterval property in the Design view, the Clock1 component periodically executes the .GetValue for the opponent’s score. A lower TimerInterval value means the application is more up-to-date, but repeated calls to the TinyWebDB service use up data and battery power on the phone.

Figure 7-10: The completed Clock1.Timer event handler

9781119991335-fg0710.tif

Installing the PunchDroid Application

You have completed the PunchDroid application. Install the application on your phone by clicking the Package for Phone button in Design view. Use the Download to this Computer option when you click the Package for Phone button to download the .APK file and send it to a friend with an Android device. The friend must have the Untrusted Install Locations setting enabled on their phone. (Setting the Allow Untrusted Install Locations option varies from Android device to device. Check your device manual or look for online instructions.) You can also test PunchDroid between your phone and the emulator. You can start the emulator by clicking the New Emulator button on the Blocks Editor. The emulator can connect to the Internet through your computer’s Internet connection.

The PunchDroid application has a lot of room for improvement. Some of the features you could include in future versions are

• Support for more players

• Checking to see whether a player number slot is taken already

• Adding sound or vibration when an opponent scores

• Adding a goal or win game target

If you've worked your way through all of the previous apps in this book, you should have enough knowledge to create some pretty incredible multiplayer games that are based on the concepts in this project but have nothing to do with the silly childhood Punch Bug game. Consider a timer-based resource management game or a location-based scavenger hunt, for example. The possibilities are limitless.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.238.227.73