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 using the shape tool to draw a rectangle 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!).

Your new shape will look black, because it is transparent. To make it yellow (the moon is made of cheese, as you know) set its properties:

bullet

Name = shpMoon

bullet

BackStyle = opaque

bullet

BackColor = yellow (choose it from the palette of colors)

You can now add the landing pad, another simple rectangular shape control that you should place towards the right side of the moon. Call it shpPad, 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. There is no property that directly indicates where the bottom of the picturebox is located, but we can find out where it is by adding picLander.Top + picLander.Height, as shown in the following picture. In this picture, we know that the lander has not landed because picLander.Top + picLander.Height is still less than shpMoon.Top.

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

Private Sub tmr1_Timer()
If picLander.Top + picLander.Height >= shpMoon.Top Then
	MsgBox ("You have landed")
	End
End If
picLander.Left = picLander.Left + vx
picLander.Top = picLander.Top + vy
vy = vy + 3
End Sub

Note that a line has been included to stop the program as soon as the landing has been detected, otherwise the messagebox will reappear constantly (type Ctrl-pause on the keyboard if you ever find yourself in a situation where you cannot stop your program). 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? Look at the picture below.

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

picLander.Left >= shpPad.Left

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

The condition for the right edge is more complicated. To make it easier, let's make two new variables to give the position of the right edge of the picturebox and the shpPad shape:

LanderRight = picLander.Left + picLander.Width
PadRight = shpPad.Left + shpPad.Width

Note that the variables on the left are not properties, so it would be wrong to use dot notation for them - do NOT put dots in these variable names. The new variables do not need to be declared with DIM like vx and vy since the new variables are used only in this subroutine.

Now the condition for the right edge to be OK is

LanderRight <= PadRight

and the condition for BOTH edges to be right is

(picLander.Left >= shpPad.Left) AND (LanderRight <= PadRight)

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.

Private Sub tmr1_Timer()
If picLander.Top + picLander.Height >= shpMoon.Top Then
    LanderRight = picLander.Left + picLander.Width
    PadRight = shpPad.Left + shpPad.Width
    If (picLander.Left >= shpPad.Left) And (LanderRight <= PadRight) 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 + 3
End Sub

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! Try again?", vbYesNo)

The last part of the line, vbYesNo, 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 into which the response should be stored.

The next line should test the response with an IF structure and act accordingly:

If response = vbYes 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:

Private Sub tmr1_Timer()
If picLander.Top + picLander.Height >= shpMoon.Top Then
	LanderRight = picLander.Left + picLander.Width
	PadRight = shpPad.Left + shpPad.Width
	If (picLander.Left >= shpPad.Left) And (LanderRight <= PadRight) Then
		MsgBox ("The Eagle has landed")
		End
	Else
		response = MsgBox("You crashed! Try again?", vbYesNo)
		If response = vbYes Then
			Reset
		Else
			End
		End If
	End If
End If
picLander.Left = picLander.Left + vx
picLander.Top = picLander.Top + vy
vy = vy + 3
End Sub

Oooooo - the code is starting to look pretty complicated - show it to your parents if you want to impress them! As previously stated, notice how the indentation (the inclusion of spaces at the beginning of some lines) helps to make the structure of the program easier to see. We can easily match each 'if' with its 'else' (if it has one) and with its 'end if'. In the above subroutine I've coloured the background of these words to emphasize this, but VB won't do that for you. We can see that there are 'if structures' inside 'if structures' inside 'if structures' - this is called 'nesting' and is very common in computer programs so get used it!

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

Underneath ALL your existing code, type this line

Private Sub Reset()

When you press the Enter key to start the next line, note that VB has automatically given you the bottom wrapper for this subroutine.

Now add the code to reset the program:

Private Sub Reset()
picLander.Left = 300  'recall that VB uses a tiny unit called the 'twip'
' replace this comment with a line to move the lander close to the top
vx = 0
' replace this comment with a line to set the vertical velocity to zero
End Sub

The above subroutine includes 'comments' (in green) for you to replace. Anything that follows an apostrophe in VB code becomes a 'comment' that VB will ignore but that will help you understand the code when you come back to work on it in the future. Professional programmers put lots of comments in their code and you should do the same.

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
 

You may be able to see the lander running in a web page by following this link. Or you may not, since certain since security settings may block it. This was done by making an ActiveX control in VB6 (not 5) and then incorporating that into the web page. If you'd like to know how to convert your own projects into ActiveX controls so that you can put them on your web site you can get advice also by following the same link.
 

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 (let's called that picturebox 'picIntact') and the other holding the damaged lander picture (let's called that one 'picDamaged) . 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.picture property with a line of code such as:

	picLander.picture = picDamaged.Picture
bullet

Many users would prefer to fly the lander using keys on the keyboard rather than command buttons to control the thrusters. Do the next project, the KeyPress project (a separate project which you should save as such) and then, once you've seen how VB can respond to key presses, return to the Lander project and modify it accordingly. Don't assume you can simply copy code from the Keypress project into the lander project and see everything working. You'll have to use your brain cells (if you have any) and think about what should happen when you press the left arrow key, for example.

bullet

Most Windows programs use menus. VB makes it very easy for you to add menus - look ahead to the Menus project 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

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.
bullet

applause

bullet

explosion

bullet

glass breaking

bullet

tada

bullet

ok

bullet

oh my

bullet

car brakes

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 command button (fuel =  fuel - 1). What should happen when the fuel level reaches zero? The simplest approach is to modify the code for each button so that if the fuel level is less than one the code that modifies the velocity simply does not run.

How do you set the fuel variable to contain 40 when the program starts? You need to know that the first thing that happens when the program starts is that the form is loaded into the computer's memory, 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...

bullet

Of course the lander program is never 'finished' or perfect - it can always be improved. Use your own creativity to add other features to the lander program. I'll be impressed if you try to add original features.  But don't overestimate you ability to write correct VB code - you've only been doing VB for a few hours and it's not possible to become fluent in a foreign language in just a few hours, whether that language is Italian, Chinese, VB or Java...

 

Up