Lander 2
Up

On this page you will learn about:

bullet

the use of the MessageBox as a function that asks a question and returns a value

bullet

user defined procedures (we make a procedure to handle the 'reset' event)

In the previous lesson you built a 'lunar flyer' but it could not be landed because there was no moon to land on (though the moon's gravity WAS present). Now you will learn how to add the lunar surface and a landing pad, and how to write code to detect landings, whether good or bad.

Add the lunar surface by making a label at the bottom of your form, stretching across the full width of the form, like this (except that your form is much bigger, and it's black!). You will need to set the label's autosize property to false before the label can stretch across the form. Also set the label's backcolor property to a light yellow (everyone knows the moon is made of cheese), the text property to nothing and the name property to lblMoon.

You can now add the landing pad, another label that you should place towards the right side of the moon. Call it lblPad, make it just a little wider than the lander, and make sure its top surface is in line with the lunar surface, as shown below (this time I will show the form with BackColor = black, as you should have).

Try running the program, and you will find that the lander falls straight through the lunar surface! This should not really come as a surprise, for we have written no code to detect the actual landing. It's easy for us to see when the lander has hit the ground, but how can we put this into code so that the stupid computer can detect what is so obvious to us clever humans? The lander has landed when the bottom of the picturebox reaches (or goes lower than) the top of the lunar surface.

We will use an IF structure to check whether the lander has landed, and if it has we will show a messagebox message. Change the code inside the tmr1_Tick subroutine as follows:

If picLander.bottom >= lblMoon.top Then
	tmr1.enabled =  false
	MsgBox ("You have landed")
	End
End If
picLander.left = picLander.left + vx
picLander.top = picLander.top + vy
vy = vy + 3

Note that a line has been included to disable the timer as soon as the landing has been detected, otherwise the messagebox will reappear constantly. Try running the program.

Our code can now detect a landing but cannot detect whether the landing occurred in the right place. This will be rather complex code for we have to check both the left edge of the picturebox and the right edge. What is the condition for a good landing?

Perhaps you can see that the condition for the left edge to be OK is

   picLander.left >= lblPad.left

Note the inequality 'greater than or equal to' in the above line.

The condition for the right edge is similar:

   picLander.Right <= lblPad.Right

so the condition for BOTH edges to be right is

(picLander.left >= lblPad.left) AND (picLander.Right <= lblPad.Right)

Note that conditions can be connected with words like AND or OR just as in English. I've used parentheses to make the above line more legible.

So now we are ready to modify the timer code. We only need to check the horizontal landing position if we have already detected a landing, so the new IF structure should be 'nested' inside the existing one. Note how the indentation of lines inside each IF structure helps to make the code more legible.


If picLander.Bottom >= lblMoon.Top Then
     tmr1.Enabled = False
     If
(picLander.Left >= lblPad.Left) And (picLander.Right <= lblPad.Right) Then
          MsgBox("The Eagle has landed")
          End
     Else

          MsgBox("You crashed!")
          End
     End
If
End
If
picLander.Left = picLander.Left + vx
picLander.Top = picLander.Top + vy
vy = vy + 0.2

Your lander program should be working fine now - this program is almost fun! There's just one annoying problem: it doesn't take long to land, and once you've landed (especially if you crashed) you would probably like the option of trying again. Our program doesn't give that option... yet... but we can add it without too much trouble. We will, for the first time, use a messagebox to ask a question. Look at the following line:

response = MsgBox("You crashed! Play again?", MsgBoxStyle.YesNo)

The last part of the line, MsgBoxStyle.YesNo, tells the program to display a 'yes' button and a 'no' button in the message box, instead of the usual 'OK' button. The user's response has to be stored somewhere, so the line begins with the name of a variable called response into which the response will be stored.

Since we have just introduced a new variable called response we will have to declare it. Put this code just under the declarations of vx and vy:

   Dim response as string

A 'string' variable contains a string of characters, in other words a block of text. After displaying the message box and putting the response into a variable called response, the next step is to add lines to test the response with an IF structure and act accordingly:

If response = MsgBoxResult.Yes Then
    Reset
Else
    End
End If

This is fine, except that VB does not understand the command 'Reset', so it will do nothing when it reaches that line. Actually it does do something - it generates a 'reset' event, but since there is no subroutine to handle this event, nothing happens. We will add a 'reset' subroutine later - for the time being, just modify the timer code like this:

If picLander.bottom >= lblMoon.top Then
     	tmr1.Enabled = False
	If (picLander.left >= lblPad.left) And (picLander.Right <= lblPad.Right) Then
		MsgBox ("The Eagle has landed")
		End
	Else
		response = MsgBox("You crashed! Play again?", MsgBoxStyle.YesNo)
		If response = MsgBoxResult.Yes Then
               		Reset
		Else
    			End
		End If
	End If
End If
picLander.left = picLander.left + vx
picLander.top = picLander.top + vy
vy = vy + 0.2

Now we will create a subroutine to handle the 'reset' event, so that the program knows what to do when it reaches that line. What exactly do we need to do in order to reset our program? We need to 
bullet

move the lander to the top left of the form

bullet

set the horizontal and vertical speeds back to zero

bullet

enable the timer control

Just above the bottom class wrapper line, End Class, add this line

Private Sub Reset()

Press the Enter key when you have finished typing the above line and and note that VB automatically gives you the bottom wrapper for this subroutine.

Now complete the Reset subroutine:

Private Sub Reset()
   picLander.left = 30
   picLander.top = 30
   vx = 0
   vy = 0
   tmr1.enabled = true
End Sub

Remember that VB uses a very small distance unit called a pixel, so 30 pixels is a small distance.

Here is the complete set of code for the lander project:

If you have followed all my instructions perfectly then
a) you are a genius
b) you should have a lander program that correctly resets if you crash the lander and then choose to try again

As icing on the cake, it would be nice to hear sound effects when the lander makes a good landing or a bad one. Look ahead to the Sound project to find out how to add sound to a VB program, then add appropriate sounds to the Lander program. You can download some useful sounds by clicking the links below - make sure you place the downloaded files in the same folder as your lander project. If left-clicking does not give you a file-save dialog then try right-clicking. To work with VB the sounds must be in the WAV format, as all the following sounds are.
bullet

applause

bullet

explosion

bullet

glass breaking

bullet

tada

bullet

ok

bullet

oh my

bullet

car brakes

Once you have placed a sound file in your project folder (the folder that contains the SLN file) you can play it anywhere in your program by adding the following line:

My.Computer.Audio.Play("mysound.wav")

You just need to change the name of the sound to make it match the name of the actual sound file you are using. This line of code will play the sound in the background meaning that other code can run while the sound is playing. It is also possible to
bullet

make the program wait until the sound has finished playing before running any more code

bullet

make the sound 'loop' i.e. play over and over again

bullet

force the sound to stop playing before it has reached the end

Check the VB help system if you need to use any of these tricks.

Now your program is working very well, but I'm sure you can think of ways of making it even better. This is your chance to earn EXTRA CREDIT by adding features that you will devise your own code for. Here are some suggestions:
bullet

So far, our program only gives you a reset option if you land badly. How about adding code so that you can also reset the program if you make a good landing?

bullet

In your car, you have a gauge that indicates your speed. Why not add labels to your form to continuously indicate the values of vx and vy?

bullet

Could you add a label to the form that indicates the lander's height above the ground?

bullet

If you think about it, you will realize that a safe landing does not imply only a landing in the right place, but also that the landing is made SLOWLY. Add code to check that vx and vy are both 'small' (you decide how small) when the landing occurs. You only have to make this check if the landing is in the correct position, of course.

bullet

You could modify the program so that if the user crashes (lands in the wrong place) then the image would change into a different one, like this (you can use this image if you like).
                           
The trick for changing the picture like this is to add two extra picturebox controls, one holding the standard lander picture and the other holding the damaged lander picture. Each of these two pictureboxes should be made invisible by setting the visible property to false - the purpose of each picturebox is to store the pictures which can then be copied at the appropriate time into the picLander.image property with a line of code such as (assuming one of the invisble pictureboxes is called picDamaged):

	picLander.image = picDamaged.image
bullet

Many users would prefer to fly the lander using keys on the keyboard rather than buttons to control the thrusters. Look ahead to the KeyPress project to see how the program can detect keypresses then modify this program accordingly.

bullet

Most Windows programs use menus. VB makes it very easy for you to add menus - look ahead to the menus program to see how menus can be added using the menu editor, then add menus to the lander program. You could start with a File menu containing items for Reset and Exit, and a Help menu with items for Instructions and About, and maybe a Gravity menu, with options for Strong, Medium and Weak. Add other menus... be creative!

bullet

You could add code so that the lunar surface and landing pad are always in the right place, even if the user resizes the form. Note that when the user does this, a Form_resize event occurs - you can make a procedure to handle this event (choose Form from the left pull-down list at the top of the code window, then resize from the right pull-down list). 

bullet

A real lander has a limited amount of fuel. You could add a variable called fuel to your program which is set to a certain value when the program starts (fuel = 40, for example) and then is decremented (reduced) every time you click a button (fuel =  fuel - 1). In order to make code happen when the program starts, you need to know that the first thing that takes place is that the form is loaded, triggering a Form_Load event. To put the code in the right place, choose 'Form' from the pull-down list at the top left of the code window, so that the subroutine looks like this:

Private Sub Form_Load()
fuel = 40
End Sub

Think about whether the fuel variable needs to be declared at the top of the code window like vx and vy and whether it needs to be reset when the user wants to start again...

The timer code should keep checking whether you have run out of fuel and if you have then... well, the rest is up to you...

 

Up