The previous pages were a little heavy, let's do something light now:
Filling the barrel with gunpowder
We want a scrollbar control to set the amount of gunpowder and a button to fire the cannon. Create the scrollbar for the gunpowder similar to what you did for the elevation slider:
Select the scale tool, drag a scale-type scrollbar, call it Powder. Set the endvalue of the Powder scrollbar to 100.
Set the script of the Powder scrollbar to:
onScrollbarDragfValue
put fValue into field "Powder"
end ScrollbarDrag
Make a label field, call it hPowder, set its contents to Powder. Using the inspector, select and set the text alignment to right align:
Create a normal field, call it Powder, make it transparent, lock its text.
Create a button, set its name to Fire, set its label to Fire! (with the exclamation mark). This is new: the button will display its label on the screen, but it will be called Fire (its name) when we use it in handlers.
Make the cannonball
Create a small circle graphic, call it Ball, set its fill colour to black and its border colour to red. I made mine 5 pixels by 5 pixels.
Arrange the objects
Place all this stuff so it looks like this:
The ball is very small. If you have problems dragging the ball around, then maybe just use the inspector to set its location. If you have problems selecting it, use the Application Browser.
Save your work. It may be a good idea to quit Revolution and to make a copy of Cannon.rev into the archive folder. Then double-click Cannon.rev again to continue.
Making it work
The only scripts we have so far are those that let us decide the elevation and the amount of gunpowder. There are only three lines of code so far!
When we press the Fire button we want to let the ball start from the nozzle. It has to fly off in the direction the barrel points to and with a speed that depends on the amount of powder.
In object-oriented programming, it is the ball that knows how to move. The Fire button only tells it to do so. Each object should do only things it knows about.
The ball knows its location and speed but it should not know about the cannon. The script of the ball should have a handler called Fly. To that handler we must give the position and the speed at which the ball starts, but nothing else.
The button Fire should look at the barrel. It should find the elevation and the amount of powder, calculate the location of the nozzle and the ball speed, and then it should tell the ball to fly.
The barrel does not know anything yet. We will have to invent two new properties that the barrel image does not have: elevation and powder. Change the handler of the Elevation scrollbar to this:
onScrollbarDragfValue
settheangleofimage"CannonBarrel.png"tofValue
put fValue into field "Elevation"
set the pElevation of image "CannonBarrel.png"tofValue
end ScrollbarDrag
(I use the prefix letter p for properties).
Change the handler of the Powder scrollbar to this:
onScrollbarDragfValue
put fValue into field "Powder"
set the pPowder of image "CannonBarrel.png" to fValue
end ScrollbarDrag
Run the program (). Drag the elevation to 30 degrees and set the powder to
around 60. Select the selection tool (, this also
stops the program) and look at the inspector for the barrel image. Select Custom Properties:
The barrel has two new properties!
The Fire button needs to give four values to the Fly handler: the x and y position of the nozzle and the x and y components of the speed. Right-click on the Fire button and select Edit Script. Type this handler:
on MouseUp
put the pElevation of image "CannonBarrel.png" into lElevation
put item 1 of the loc of image "CannonBarrel.png" into lCentreX
put item 2 of the loc of image "CannonBarrel.png" into lCentreY
put (the formattedwidth of image "CannonBarrel.png")/2 into lLength
put lCentreX + lLength*cos(lElevation*pi/180) into lNozzleX
put lCentreY - lLength*sin(lElevation*pi/180) into lNozzleY
set the loc of graphic "Ball" to round(lNozzleX),round(lNozzleY)
end MouseUp
Press the Apply button on the script window, save your work and select the browse tool to run the program. Set the elevation to something, then press the Fire button. The ball should move to the nozzle. Select other elevations and press Fire. The ball should always position itself at the barrel nozzle.
Note: this is of course a useless script for the Fire button, but it shows us that our computations for the nozzle are correct. The line set the loc of graphic "Ball" to lNozzleX,lNozzleY has no use later on. It is a temporary line that lets us make progress and check that we are still doing well.
Let's look at this handler in detail:
The first line is a comment. Comments start with -- and run until the end of the line.
The next line just takes the barrel's new property to store it into a local variable for later use. I used spaces to line up the code so it is easier to see that four statements deal with the barrel.
The next two lines take the components of the location of the barrel's centre and put those into local variables.
The fifth line uses the property formattedwidth. Why could we not just use width ? The barrel is originally 85 pixels wide by 33 pixels high when it points horizontally. But when it points upwards, it will be 33 pixels wide and 85 high! At angles in between it will have strange widths and heights. The formattedwidth always gives the original width of the unrotated image, and that's what we need.
Then there is another comment and two lines where we use all the info to compute the location of the nozzle. I repeat here the diagram of how we thought this up:
Note: the second line for computing the location of the nozzle uses a minus-sign, because the y-coordinate is measured from the top. When the barrel swings up, the y of the nozzle diminishes! (I told you that we would be irritated by this stupid screen convention).
Now, why did we not get the elevation and the powder directly from the fields Elevation and Powder?
We could indeed have done that, but I'm teaching you object-oriented programming, not shortcuts.
Ugly
Ah, but if objects ought to know things about themselves, then should the barrel not know where its nozzle is? Yes, it should! The handler we just wrote is quite ugly, most of its statements should be in the script of the barrel. We put them there in a computed property.
Computed Properties
We have given two new properties to the barrel: its elevation and its powder content. You can see their values in the inspector. We can't however make the location of the nozzle a simple property: we must compute it. Revolution allows us to do that: we will put the statements to compute the nozzle location inside the barrel script, as a special getProp handler.
Select the barrel image and edit its script:
getprop pNozzleLoc
put the pElevation of me into lElevation
put item 1 of the loc of me into lCentreX
put item 2 of the loc of me into lCentreY
put ( the formattedwidth of me )/2 into lLength
put lCentreX + lLength*cos(lElevation*pi/180) into lNozzleX
put lCentreY - lLength*sin(lElevation*pi/180) into lNozzleY
return round(lNozzleX),round(lNozzleY)
end pNozzleLoc
The getProp is a handler for making a computed property. Revolution has three handler types:
- procedures or routines, we have already met them, they start with the word on.
- functions, they start with the word function and we will meet them in another tutorial.
- computed property definitions, they start with the word getProp.
Note how the word me is used in the handler: me means the object where the handler sits.
Note how we use return to hand back the coordinate pair as the result of our calculations.
Note how the systematic use of prefix letters helps distinguish the different Elevations.
Now the handler in the Fire button can be elegantly simple:
on MouseUp
set the loc of graphic "Ball" to the pNozzleLoc of image "cannonBarrel.png"
end MouseUp
This statement looks just like any other statement that uses a property of an object. I used the prefix letter p so that we can see from the name that it's not a usual property.
Object-Oriented programming note
We have written very few lines of code. All code is nicely tucked away in small handlers. The objects behave in the way we want and we know exactly where to look in case of problems.
|