Turn off Lights - A
11
Turn Off Lights is a variation of the famous old Whack-a-Mole game: this one promoting the socially useful message of saving electricity. In the game, a light bulb (represented by an ImageSprite
) pops up at random positions on the screen. A player can score by touching the light bulb before it disappears.
This app uses animation, a clock, and randomness to move the ImageSprite
around the canvas. This tutorial guides you through the basic steps in creating the animation.
Objectives
- follow an instructor-led walk-through to create the
TurnOffLights
app on a mobile device - develop your understanding of how timing, animation, and randomness are programmed
- develop your understanding of procedures and procedural abstraction
- deepen your understanding of event-driven programming
Getting Ready & Video Tutorials
- Open App Inventor with the Turn Off Lights template. The template app just contains the images and sound files for the app, but no design or code yet.
- When the project opens, use the Save As option to rename it TurnOffLights.
- Then follow along with the following tutorial.
The Turn Off Lights User Interface (UI)
The UI for our this app will consist of four types of Components:
- Basic Components:
Canvas
,Label
,Clock
- Media Component:
Sound
- Animation Component: an
ImageSprite
- Layout Component:
Vertical Arrangement
The Canvas
serves as the background where the light bulb jumps around. The light bulb is represented by an ImageSprite
that is contained in the Canvas component. We'll come back to this. The Clock
is used as a timer to move the ImageSprite
to a random spot on the Canvas every time the Clock
ticks. The Sound
is used to vibrate the phone or play a sound whenever the player hits the ImageSprite
.
Adding the Canvas
To begin with, set the various Screen1
properties as shown in the UI screenshot above, including AppName
, ScreenOrientation
, Title
and others. Next, drag and drop a VerticalArrangement
component from the Layout
drawer and set its height and width to Fill parent
. The Canvas
and other component will be contained within this arrangement and will fill the screen. By using a VerticalArrangement
, we can guarantee that all of its components will be visible on the screen without scrolling.
You have used the Canvas
component for drawing in the Paint Pot tutorials. In this app Canvas
is used to support animation. App Inventor uses two types of components that can move for animation, Balls
and ImageSprites
, both of which are contained within a Canvas
component. First, we'll add the Canvas
component.
- Drag and drop a
Canvas
component from thePalette's Drawing and Animation
category into theVerticalArrangement
. . - Set the
Canvas
's width and height to fill-parent. - Set the
Canvas
'sBackgroundImage
tokitchen-picture.jpg
.
Adding the Save Electricity Label
Add the Label
to the UI, right underneath the Canvas in the VerticalArrangement
. Name the label LabelSaveElectricity
and set its text property to "Help Save Electricity!"
Adding the ImageSprite
As mentioned before, the light bulb is represented by an ImageSprite
, a component that can move (like a Ball) and can also display an Image (unlike a Ball). ImageSprites
can only be used on a Canvas
.
- Drag and drop an
ImageSprite
component from thePalette's Drawing and Animation
drawer onto theCanvas
. - Set the
ImageSprite
's picture property to thelightbulb.jpg
image file, which is included in the template'sMedia
panel. - Set the
ImageSprit
's height and width properties to 50 pixels each. - Rename the
ImageSprite
to "LightBulbSprite".
Adding the Sound
We will use a Sound
Component to vibrate the device when the player successfully touches the light bulb. Drag and drop a Sound
component from the Palette's Media
category into the Viewer. It will be named Sound1
and will appear as a non-visible component.
Adding the Clock
The ImageSprite
component has a Speed property, which controls the Sprite's movement. However, for this app, we won't be using that property. Instead, we will use the Clock
component to move the Sprite to a random location on the canvas whenever the clock ticks. The Clock
has a Timer
event that can be used to move the Sprite to a new random location whenever the Timer
event fires. In effect, every time the Timer
ticks, the Sprite will 'jump' to a new random location.
- Drag and drop a
Clock
component from the Palette'sSensors
category into the Viewer. It will be namedClock1
and it will appear at the bottom of the Viewer as a non-visible component. - Set the
Clock
'sTimerInterval
property to1000
milliseconds. This will make the clock tick every second.
Coding the App's Behavior
In order to get the Sprite moving, we'll first set up a Clock.Timer
event that will allow the Sprite to 'jump' to various positions randomly with the help of some Math
blocks that are used for randomness. Once the Sprite is moving we'll need to make the device vibrate each time the user touches the Sprite (light bulb).
Moving the Sprite
To construct this you will pull the when Clock1.Timer
block from the Clock1 drawer, the MoveTo
block from the LightBulb
drawer, and two number blocks from the Math
drawer.
The Clock.Timer
event is triggered periodically. If you have set the Clock
's TimerInterval
property to 1000, this event will trigger every 1000 milliseconds, or every second. If you are testing the app, you'll see that the Sprite jumps to the location x=100, y=100 and just stays there. The event keeps triggering, but after the first time the Sprite is already at 100,100 so it doesn't appear as if anything is happening.
What we really want is for the Sprite to move to a random location each time the clock triggers. For this, drag out two random integer blocks from the Math drawer, and plug them in as seen to the right.
Now you should see the Sprite move around randomly every 1000 ms., but stay within the top left section of the Canvas.
Of course the upper limit of 100 is incorrect. We really want the Sprite to jump around anywhere on the Canvas. For this, we can use the Width and Height properties of the Canvas. Pull out Canvas.Width
, Canvas.Height
from the Canvas drawer and modify your blocks as seen.
Now you should see the LightBulb jumping randomly all over the canvas.
Refactoring: The moveRandom Procedure
As we learned in an earlier lesson, having identical segments of code in two separate locations in the app is not a good programming practice. To fix that, let's define a moveRandom
procedure and call it to manage moving the ImageSprite
to a random location.
Abstraction
When you define a procedure in a programming language you are creating an abstraction. The procedure represents a particular algorithm. Once you define the procedure, it encapsulates the details of the algorithm (hides the details). To execute the algorithm, you only need to call the procedure. When calling the procedure, you aren't necessarily aware of the details of the algorithm. Thus, defining and using procedures helps reduce the complexity in our programs and makes them easier to read, modify, and maintain.
In our app, the Screen.Initialize
and Clock.Timer
events both do the same thing and call the same blocks to move the ImageSprite
randomly. Whenever you copy-paste, as we did, a little bell should go off in your head: I need a procedure! In this case, the moveRandom
procedure is appropriate.
- Drag out a
to procedure
block from theProcedures
drawer. - Name the procedure "moveRandom".
- Drag the blocks from
Screen.Initialize
and place them in "moveRandom".
Open the Procedures
drawer again. You should now see a block, "call moveRandom". Pull it out and place it in Screen.Initialize
. Take out the blocks for Clock.Timer
as well and replace them with a call moveRandom block.
Your app should work exactly the same, with the Sprite moving around randomly.
Handling the Touched Event
Similar to Paint Pot, the Turn Off Lights app will also use a Touched
event. However, this touched event is used differently. When a player touches the Sprite we want them to get some type of feedback for their accomplishment. This helps give the user feedback when taking actions within the app. For now, let's make the phone vibrate for 500 milliseconds (half a second) whenever the Sprite is touched:
- Get a
LightBulb.Touched
block from theLightBulb
drawer. - Get a
Sound1.Vibrate
block from theSound1
drawer. - Set the
Vibrate
interval to 500 millisecs. - Place the
Vibrate
block inside theTouched
block.
NOTE that not all Android devices contain a vibration component. If your device does not vibrate, replace the Vibrate
block with a Sound1.Play
block and set the Sound1.Source
property (in the Designer) to the beep.mp3
file.
Testing the App
Still Curious?
You can think of procedures like "shortcuts" in your code. Where a complicated process, such as taking the sum of a list of numbers, can simply be given a shortcut name like "sum". Once you make the procedure you will not have to worry about how it works, i.e. assume sum works correctly, instead simply use the call to the shortcut name in other code blocks.