Learn to Program using Alice
Appendix G
By Popular Demand, Interesting Projects
Baldwin shows you how to complete several interesting programming
projects for which he has received requests for information.
Published: November 6, 2007
Updated: October 27, 2007
By Richard G. Baldwin
Alice Programming Notes # 930
Preface
This document serves as an appendix to a series of
programming tutorial lessons that are designed to teach you how to program using
the Alice programming environment under the
assumption that you have no prior programming knowledge or experience. In
case you have happened upon this document as a result of a web search, an index
to all of the material contained in the series is available in
Resources.
A work in progress
From time to time I receive email requests, or see requests posted in the
Alice forum for information on how
to solve a particular programming problem using Alice. My plan is to
respond to some of those requests by providing sample programs in this appendix.
Therefore, this document is a work in progress that will be updated as such
requests are received and honored.
I recommend that you open another copy of this document in a separate
browser window and use the following links to easily find and view the figures
and listings while you are reading about them. Note that because material
will be added to this document from time to time, the figures and the listings
may not be in numeric order in the body of the document.
- Figure 1. Presentation of multiplication problem
to student.
- Figure 2. Multiplication program output for
correct answer.
- Figure 3. Multiplication program output for
incorrect answer.
- Figure 4. Drag race game at startup.
- Figure 5. Drag race game during running of
Christmas tree lights.
- Figure 6. Drag race game at end of game.
- Figure 7. Trajectory code.
- Figure 8. Jumping fish going after a bug.
- Figure 9. Projectile aiming phase.
- Figure 10. Projectile in flight.
- Figure 11. Projectile impact on target.
- Figure 12. Formula for
computing arc length.
- Figure 13. Trajectory of a cruise missile.
- Figure 14. Screen shot from the program named ObjectParameters01.
- Listing 1. Two-dimensional projectile
trajectory program.
- Listing 2. Multiplication game program.
- Listing 3. Addition game program.
- Listing 4. Subtraction game program.
- Listing 5. Drag race game
program.
- Listing 6. Approximate trajectory program.
- Listing 7. An improved approximate trajectory
program.
- Listing 8. Testing an Object parameter for
equality to a specific object.
- Listing 9. Calling custom methods on a
parameter of type Object.
- Listing 10. The program named ObjectParameters01.
- Listing 11. The program named
ObjectListItem01.
- Listing 12. Program listing
for Vector01.
- Listing 13. The main method
for Vector01.
- Listing 14. The method named vectorMoveAbs.
- Listing 15. The method named vectorMoveRel.
- Listing 16. The program named Library01.
- Listing 17. The main method for the program
named Library01.
Once you have mastered Alice, I recommend that you also study the other lessons in my extensive collection
of online programming tutorials. You will find a consolidated index at
www.DickBaldwin.com.
Discussion
and sample code
One of the earliest computer games that I recall seeing during my lifetime is
a simple game involving the trajectory of a projectile. The computer-game
magazines of the sixties contained numerous such programs written in BASIC and
other early programming languages. I have recently seen several requests
for information on how to program the trajectory of a projectile.
Many applications
The use of a ballistic trajectory has many applications in programming games.
For example, the path of a basketball after leaving the player's hands is
probably a very close approximation to an idealized ballistic trajectory.
The same holds true for the path of the human cannonball at the circus,
(which I just witnessed this past weekend). An interesting animation
would be to create a human cannonball using a Coach object from the
gallery and finding something in the gallery that looks like a net to catch the
coach.
|
General Ballistic Trajectory
The motion of an object under the influence of gravity is determined
completely by the acceleration of gravity, its launch speed, and launch
angle provided air friction is negligible. The horizontal and vertical
motions may be separated and described by the general
motion
equations for constant acceleration.
|
|
The equations of motion
The first challenge in writing such a program is understanding the equations
of motion for a projectile. An excellent treatment is of the equations
of motion is referenced in the accompanying sidebar.
The animation program
The Alice animation program shown in Listing 1 causes a safe (of the money
storage variety) to be shot as a
projectile across the screen in a two-dimensional universe. The user
specifies the launch angle relative to the horizontal axis. Screen shots
from this program are somewhat less than exciting, so I didn't provide any.
You can download a zip file containing the Alice world
for this program, extract the world file from the zip file, load the world into
your Alice programming environment, and play it to see the animated trajectory.
Improvements to the program
Paraphrasing the opening scene from an old TV show, your challenge, if you
choose to accept it, is to extend this projectile-trajectory program into three
dimensions. If you are really ambitious, you could also add the effects of
wind at some fixed velocity blowing from some particular direction in 3D space.
If you load and run the 2D projectile trajectory
program you may find
it to be a little jerky, depending on the speed of your computer. This is
because computing and rendering the parabolic trajectory at a large number of closely
spaced points places a significant computational load on the computer.
A smoother animation
This program produces an approximate trajectory that should run smoothly on
most computers that are capable of running Alice programs. While this
trajectory is circular rather than being parabolic like a true
ballistic trajectory, it is close enough that it should be suitable for most
Alice animations. When viewed in three dimensions, it is
very difficult to tell the difference between this approximate trajectory and a
true ballistic trajectory unless the viewer's line of sight is exactly perpendicular to
the plane of the trajectory.
The source code
The source code for this program is shown in Listing 6. The actual
trajectory is produced by the two statements shown in Figure 7. These two
statements were copied from the method named jump belonging to the
fish class in Listing 6.
Figure 7. Trajectory code.
fish.move(FORWARD,length meters);
style = BEGIN_AND_END_ABRUPTLY
duration = 2.5 seconds
fish.turn(FORWARD,0.25 revolutions);
style = BEGIN_AND_END_ABRUPTLY
duration = 2.5 seconds
|
Note that the two statements in Figure 7 (along with some other code)
are inside a doTogether
block so that they will execute concurrently:
Catching a bug
Figure 8 shows an image of a fish that has launched itself from the water in
a long arc-shaped trajectory in an attempt to catch and eat a bug.
Figure 8. Jumping fish going after a bug.
The launch angleInitially, the blue (forward)
axis of the fish is rotated upward by 45 degrees. The first statement in
Figure 7 causes the fish to launch itself from the water at an angle of 45
degrees.
What produces the circular arc?
As the fish moves forward, the second statement in Figure 7 causes the fish
to continuously rotate its blue axis downward toward the water. Because it
is continuously moving in the direction of the blue axis, this motion describes
a circular arc with the fish entering the water at an angle of 45 degrees.
This approximate trajectory is circular instead of parabolic as in the case of a
true ballistic trajectory.
However, this approach takes advantage of several primitive capabilities of
Alice resulting in a smoother animation than is the case with the
earlier trajectory program.
A rather long program
Although the code that actually creates the trajectory is very short, the
entire program shown in Listing 6 is rather long. This results from
the creation and animation of five similar bugs that fly around above the water
as shown in Figure 8. The bugs fly in a random manner, but one of the bugs
has a predisposition to get closer and closer to the water. When that bug
lands on the water, the fish launches itself from some distance away and eats
the bug.
You can download and play this world (see Resources).
The previous discussion of an
approximate trajectory program was designed to be a lot of fun, but was short on mathematics and technical details. Furthermore, some of the
parameters used in the animation were designed to simply look good and were not
based on mathematical accuracy. Now it is time to step back and take a
look at the mathematics involved and to show you how to compute the required
parameters as a function of target distance and launch angle.
Implementing a parabolic arc in Alice
Recall that you learned
earlier that the
ballistic
trajectory of a projectile under the influence of gravity (ignoring air
resistance) is a parabolic arc. However, Alice does not provide a primitive
method for moving a projectile in a parabolic arc. Therefore, it is
necessary to write the code to move the projectile in short increments so that
the overall shape of the trajectory is a parabolic arc. This places a
significant computational load on the computer and may cause the performance to
be poor on some computers.
|
Limit launch angle to 45 degrees max
Note that for launch angles greater than 45 degrees, the
circular arc produced by this program is a very poor
approximation of a parabolic trajectory. |
|
A reasonable approximation
Alice provides two primitive methods that, when used
together, make it possible to move a projectile in a circular arc with very
smooth animation on most computers. The circular arc is a reasonable
approximation of a parabolic arc, particularly when viewed as a
three-dimensional animation. In 3D, it is difficult to visually
distinguish between the circular arc and a parabolic arc unless the viewer's
line of sight is perpendicular to the plane of the arc.
Computing the arc length
In order to use the circular arc as an approximation of a parabolic
trajectory, it is necessary to compute the length of the arc as a function of
the launch angle and the distance to the target. This program will show
you how to do that.
Some screen shots
Before getting into the technical details, lets look as some screen shots
taken from the program while it is running. Figure 9 shows shows the projectile in the
aiming phase during which the launch angle is being established.
Figure 9. Projectile aiming phase.
What are those objects?For this program, the flashlight object on the left in Figure 9 represents the
projectile. The flashlight object on the right represents the target, and
the vertical magician's wand on the left is there simply to mark the launching point of
the projectile for later reference.
The projectile in flight
Figure 10 shows the projectile in flight. In Figure 10, the projectile
is tangent to an invisible circular arc that intersects the point where the wand intersects
the white line on the left (the launching point) and also intersects the point where the target
intersects the white line on the right.
Figure 10. Projectile in flight.
The center, radius, and chord of the circleThe center point of the circle
(of which the circular arc is a part) is
somewhere below the white line midway between the launching point and the target. The
radius of the circle is greater than the distance between the launching point and the
target.
The segment of the white line that connects the launching point
to the target is a chord of the
circle. The actual distance that the projectile must travel along the
circular arc to reach the target is the
arc length or
length of the arc.
Projectile impact on target
Figure 11 shows a screen shot of the projectile after it has impacted the target.
Figure 11. Projectile impact on target.
The ideal caseIdeally the projectile, the target, and the white line would intersect at
exactly the same point. The fact that they don't probably indicates some
computational inaccuracies in the evaluation and execution of the two statements shown in
Figure 7.
The required parameters
In order to cause the projectile to impact the target for a given launch
angle and target distance, the value of length shown in the first statement in Figure 7 must
equal the arc length. The turning angle in the second statement in Figure
7 must be twice the launch angle.
Formula for computing arc length
If we represent the distance between the launching point and the target to be
a chord of the circle, and represent the launch angle as angle, we can compute the arc length using the formula given in
Figure 12.
Figure 12. Formula for computing arc length.
arcLength = (pi * chord * angle)/(180 * sin(angle))
|
If you are familiar with
trigonometry, you should be able to derive this formula on your own.
If not, just accept it as fact.
Behavior of the program
This program requests the target distance and the launch angle from the user.
Then it computes the arc length and causes the projectile to move in a circular arc to
impact the target as shown in the screen shots of Figures 9, 10, and 11.
Remember, even though the projectile will impact the target for launch angles
greater than 45 degrees, the trajectory produced by such launch angles is a very
poor approximation of the parabolic trajectory of a ballistic missile.
You can demonstrate this by running the program with a target distance of
eight meters and a launch angle of 120 degrees as shown by the screen shot in
Figure 13. In this case, the projectile is launched in the wrong direction
but still impacts the target.
Figure 13. Trajectory of a cruise missile.
You can download and run this program (see Resources).
I recently received an email message from a man who was thinking about
teaching his twelve-year old son how to program using Alice. As an example
of the kinds of programs that he might teach his son to write, he asked if Alice
would be suitable for writing a simple game that can be used to teach a younger
child how to learn their multiplication tables. I responded in the
affirmative and decided to write such a program myself and present it to my
young grandchildren who will be learning their "times tables" in preschool this
year. A complete listing of the program is provided in Listing 2. A
downloadable version of the world is provided in Resources.
|
In retrospect ...
Considering the age of the students who will be playing the game, it may have
been better to simplify the text in Figure 1. |
|
Posing the multiplication problem
The program begins by presenting a multiplication problem to the student,
expecting the student to enter the product of the two numbers in the text field
as shown in Figure 1.
Figure 1. Presentation of multiplication problem to
student.
A correct answer
If the student enters the correct answer, the program plays an audible moo
sound, the cow moves her head up and down in an affirmative gesture, and the
word Correct appears on the screen as shown in Figure 2.
Figure 2. Multiplication program output for correct
answer.
An incorrect answer
If the student enters an incorrect answer:
- The program cackles like a chicken (instead of mooing like a cow)
- The cow moves her head from side to side in a negative gesture
- Text similar to that shown in Figure 3 appears on the screen
Figure 3. Multiplication program output for incorrect
answer.
A new multiplication problem
After a short time period, the program presents a new multiplication problem
to the student as shown in Figure 1. The student should enter a value of
1000 to terminate the program.
Not a game of Doom ...
For teenagers accustomed to modern video games, the behavior of this game is
clearly very simplistic. However, the behavior of this game was not
intended for teenagers, it was intended for preschoolers.
Illustrates fundamental programming concepts
An examination of the code in Listing 2 will show that this simple program
illustrates many fundamental programming concepts including types, variables,
loops, relational operators, and if-else selection statements. Therefore,
while the behavior of the game may be simplistic, the structure of the program
should be useful in teaching students (even teenagers) how to write Alice
programs.
Listing 3 provides the source code for an addition game based on the concepts
explained for the multiplication game above.
Listing 4 provides the source code for a subtraction game based on the
concepts explained for the multiplication game above.
Listing 5 shows the source code for a drag race game. Note that the
first two events in Listing 5 should read as follows:
- When mouse is clicked on redCar
- When mouse is clicked on yellowCar
Unfortunately, the word mouse is not preserved in this source code listing
format.
Drag race game at startup
Figure 4 shows an image of the game at startup. Two cars are at the
starting line with instructions to the user to click on a car to start the race.
Figure 4. Drag race game at startup.
By clicking a particular car, the player not only starts the race, but also
predicts which car will win the race.
Drag race game during running of Christmas tree
lights
From what I read, drag races are started by running a drag strip "Christmas
tree" through several colors of lights with the green light being the signal to
start moving down the track.
When the user clicks on one of the cars, a simulated drag strip "Christmas
Tree" cycles through red, yellow, and green as shown by the yellow circle in
Figure 5.
Figure 5. Drag race game during running of
Christmas tree lights.
Go on greenWhen the Christmas tree turns
green, the two cars start moving down the track.
A while loop and a random number generator are used to control the motion of
each car. During each iteration of the while loop, each car moves forward
by a random distance ranging from 0.5 to 1.5 meters.
Crossing the finish line
When one (or both) car crosses the finish line during an iteration of
the while loop, the loop terminates and the motion of both cars stops. The
winner is determined to be the car that went the greatest distance beyond the
finish line before stopping as shown in Figure 6.
Figure 6. Drag race game at end of game.
And the winner is ...
The text at the bottom of Figure 6 shows which car won, and also shows
whether or not the player correctly predicted the winner by clicking on the car
that won the race.
Method parameters of type Object
When you define a new method, you can declare the method parameters to be any of the
following types:
- Number
- Boolean
- Object
- Other
- String
- Color
- TextureMap
- Sound
- Pose
- Position
- Orientation
- PointOfView
- ReferenceFrame
- Transformable
- Light
- Direction
A parameter of type Object
If you need to pass the light object, the ground object, or any
object that was created from the gallery to a method, you need to define the parameter in your
method as type Object.
You can only pass objects that appear in the object tree or <None> to the
method. You cannot pass the world object to the method.
What can the code in the method do with the
parameter?
The issue under discussion in this section centers on what the code in
the method can do with the incoming parameter of type Object when the method is called
and begins execution.
One of the things that the code in the method can do is to directly call any
one of sixteen primitive methods on the parameter.
Another thing that the
code in the method can do is to use the parameter to set the values for the
following five properties:
- color
- opacity
- vehicle
- skin texture
- filling style
Another thing that the code in the method can do is to set the value of the
parameter so that it actually represents a different object from the object that
was passed as a parameter when the method was called.
There is at least one other thing that the code in the method can do with the
parameter that I will explain shortly.
What the code in the method cannot do with the
parameter
One of the things that the code in the method cannot do is to use the
parameter to directly call
any of the custom methods belonging to the object that was passed as a
parameter. This is a serious restriction. As you will see later, however, there is a way to
at least partially work around
this restriction.
A similar situation in Java?
In an attempt to put this situation in perspective, I am going to discuss and
explain a similar situation in Java, which is the underlying language for Alice.
Java also contains a class named Object. This class is the root
of the entire class inheritance hierarchy. The Object class defines
eleven methods and no variables. It is perfectly legal to pass an object's
reference to a method as a parameter of type Object. However, I often tell my students that
when that is done there are only twelve things that
the code in the method can do with the incoming parameter:
- 1 - 11. Use the parameter to call any of the eleven methods defined in the Object
class.
- 12. Attempt to use a cast operator to change the type of the
parameter, which in turn may make it possible to call
other methods belonging to the object or to access variables belonging to
the object. (Note that there are a number of restrictions that apply
to such a casting operation, which I won't get into here.)
What is not possible?
It is not possible to use the method parameter of type Object in Java
to call any of the methods defined in the class from which the object was
instantiated (other than the eleven methods inherited from Object)
without first using a cast operator to change the type of the parameter.
The mechanics of the cast operation
If the actual type of the incoming object reference is known when the program
is written, it is a simple matter to cast it to the actual type of the object.
If the actual type of the incoming object reference is not known when the
program is written, but it is known that the actual type is one of a finite
number of possible types, it is possible to use the
instanceof operator to
determine the actual type of the object reference and to apply an appropriate
cast operator to change the type of the reference to the actual type of the
object at runtime.
Getting back to Alice...
Earlier in this document, I explained some of the things that the code in an Alice method can do
with an incoming parameter of type Object, and I promised to explain one
other option later. The time has come to explain that other option.
While there is no cast operator and no instanceof operator in Alice,
it is possible to use the Alice equality operator (==) to determine if an incoming
method parameter of type Object refers
to a specific object. This is illustrated in Listing 8 where
a test is made to determine if the parameter named obj refers to an
object named cow.
Listing 8. Testing an Object parameter for
equality to a specific object.
if ( ( obj == cow ) ) { |
|
cow.dance ( ); |
} else { |
Calling custom methods
If the parameter does refer to a specific object, code can then be written to call custom methods on that object. In
Listing 8, the custom method named dance is called on
the cow object after determining that the method parameter refers to the
cow object.
Similar to the use of instanceof in Java
Note that this is similar to, but different from using the instanceof
operator in Java to determine the actual type of an incoming parameter before
applying a cast operator followed by a call to a custom method. The Java
instanceof operator is used to determine if the incoming parameter is of
a particular type (not to determine if the parameter refers to a specific
object). The Alice code in Listing 8 determines if the incoming
parameter refers to a specific object.
Java approach is somewhat more general
Because objects can be created and destroyed at runtime in Java on the basis
of many different runtime conditions, the Java
programmer doesn't know about specific objects when the program is
written. Therefore, the Java programmer must deal with objects at the more
general class level when resolving parameters of type Object.
All Alice objects are known to the programmer
However, all objects in an Alice world must be created when the program is
written and before the program is run. Therefore, the Alice programmer knows about the existence of
every object in the Alice world.
Although it may be somewhat cumbersome, the Alice
programmer can use code similar to that shown in Listing 8 to determine which of
the objects in the world was actually passed as a parameter to the method, and
can then call custom methods on that specific object in the body of the
if-else statement.
A program named ObjectParameters01
Figure 14 shows a screen shot of a program named ObjectParameters01.
Figure 14. Screen shot from the program named ObjectParameters01.
In this program, the world was populated with a cow and two penguins. A
custom method named dance was defined for each of those three objects.
The behavior of the dance method belonging to each object is different
from the behavior of the dance method belonging to the other two objects.
Program behavior
The main method calls a world method named makeObjectsDance four
times in succession. The first three calls to the method pass the cow and
each of the two penguins as parameters of type Object. The fourth call
passes the ground as a parameter of type Object.
The makeObjectsDance method
The makeObjectsDance method begins by calling the primitive turn method
directly on the incoming parameter each time it is called. This code is shown
in Listing 10, which contains the entire program listing. Nothing special
is required to use the incoming parameter of type Object to call any of
the sixteen primitive methods mentioned earlier.
Identifying the objects and calling the custom
methods
Then the
makeObjectsDance method uses the incoming parameter to identify the object
that is referenced by the incoming parameter. It calls the custom
dance method on the cow and each of the two penguin objects after
determining that those are the objects referred to by the incoming parameter of
type Object. This is shown by the code fragment in Listing 9.
Listing 9. Calling custom methods on a parameter
of type.
if ( ( obj == cow ) ) { |
|
cow.dance ( ); |
} else { |
|
if ( ( obj == penguin1 ) ) { |
|
penguin1.dance ( ); |
} else { |
|
if ( ( obj == penguin2 ) ) { |
|
penguin2.dance ( ); |
} else { |
|
// Cause the parameter to point to the camera |
// instead of the ground. Note that the ground |
// was caused to turn at the beginning of this method. |
ground .say( No match. Make the parameter point to camera instead of ground. ); duration = 3 seconds |
obj .set( value , camera ); |
obj .turn( LEFT , 1 revolution ); asSeenBy = cow |
} |
} |
} |
The dance method belonging to each object causes the object to step
forward, do a little dance, and then return to its position in the line.
The program also causes some text bubbles to appear on the screen to explain
what is happening as the program executes.
Changing the value of the parameter
The last call to the makeObjectsDance method in the main method
passes the ground object as a parameter of type Object. This
results in the final else clause in Listing 9 being executed. The
code in the else clause calls the set method on the incoming
parameter to change its value. Following the call to the the set
method, the parameter refers to the camera instead of the ground,
which was actually passed as a parameter.
Then the primitive turn
method is called on the modified parameter. This causes the camera
(not the ground)
to fly around the cow, making one complete revolution around the cow and
ending up where it started.
List items of type Object
When you store objects in a list as type Object and later extract
items
from the list, you encounter a situation very similar to that discussed above
regarding a parameter of type Object. You cannot directly call
custom methods on the list items when you extract them from the list.
However, you can use the same technique shown above to identify the object
referenced by a list item and then call custom methods on that object.
This is illustrated by the program named ObjectListItem01, which is presented in
Listing 11.
This program is very similar to the program named ObjectParameters01
that I explained above. Therefore, the program named ObjectListItem01 shouldn't require further
explanation.
You can download a zip file containing both of these programs (see
Resources).
To progress beyond Alice in 3D programming, students will need to get beyond
thinking in terms of moving forward, backward, left, right,
up, and down. They will need to start
thinking in terms of the 3D coordinates x, y, and z. However, the
methodology for programming using coordinates in Alice is somewhat obscure.
Furthermore, there is an apparent bug in
Alice that makes programming using 3D coordinates even more complicated than it
would otherwise need to be.
In this section, I will show you how to
move an object to a new location in 3D space using either absolute or relative
coordinates. Movement in absolute coordinates refers to movement relative
to the origin of the world.
Movement in relative coordinates refers to movement
relative to the object's current position along each of the three axes in 3D
space. Unlike the use of the
primitive move method, neither absolute nor relative movements depend on
the current orientation of the object. Also, the orientation of the object
is not changed when the object is moved.
The program named Vector01
Listing 12 contains a complete listing of a program named Vector01.
I won't provide a screen shot for this program because all it would show is a
penguin and a magician's wand in 3D space. You need to run the program and
view the animation to appreciate it. See Resources
for a link to download a zip file containing this program.
The purpose of this program is to illustrate the use of the following methods
to move an object on the basis of 3D coordinates:
- vectorMoveAbs
- vectorMoveRel
Behavior of the program
This program places the ground, a penguin, and a magician's wand at the
origin of the world. The wand is used solely to provide a visual marker
for the origin of the world when the penguin moves away from the origin.
The main method
The main method is shown in Listing 13.
Listing 13. The main method for Vector01.
public void main ( ) {
|
|
//
Illustrates how to program in Alice using vectors. |
//
Moves a penguin in 3D space using both absolute |
//
and relative vectors. |
//
Initialize the penguin to the origin. |
world.setTheStage ( ); |
//
Move penguin away from origin with absolute move. |
world.vectorMoveAbs (
obj =
penguin ,
x = 3
, y =
2 ,
z = -1
,
duration = 1 ,
style = 1
); |
wait(
1
second ); |
//
Move back to origin with absolute move. |
world.vectorMoveAbs (
obj =
penguin ,
x = 0
, y =
0 ,
z = 0
,
duration = 0.25 ,
style = 3
); |
wait(
1
second ); |
//
Move away from origin again with absolute move. |
world.vectorMoveAbs (
obj =
penguin ,
x = -1
, y =
2 ,
z = 3
,
duration = 1 ,
style = 4
); |
wait(
1
second ); |
//
Move back to origin in two relative moves with a pause in
between. |
world.vectorMoveRel (
obj =
penguin ,
x = 0.75
, y =
-1 ,
z = -1
,
duration = 3 ,
style = 1
); |
wait(
1
second ); |
world.vectorMoveRel (
obj =
penguin ,
x = 0.25
, y =
-1 ,
z = -2
,
duration = 1 ,
style = 2
); |
} |
|
Making an absolute move
The program begins by setting the stage to cause the camera and the other
objects to assume their initial positions. Then it calls the method named
vectorMoveAbs to cause the penguin to move to a position specified by the
following coordinates:
x = 3, y = 2, z = 1
The duration and style of the move are controlled by parameters passed to the
method.
Pause the action
Then the program pauses for one second to allow the user to visually absorb
what has just happened. (Pauses are inserted at several points during
the running of the program for that purpose.)
Move back to the origin
Following the pause, the main method calls the vectorMoveAbs
method again to cause the penguin to move back to the origin specified by the
following coordinates:
x = 0, y = 0, z = 0
Another absolute move
Following another pause, the vectorMoveAbs method is called to cause
the penguin to move to:
x = -1, y = 2, z = 3
Move relative
Then the vectorMoveRel method is called to cause the penguin to move
by the following incremental amounts relative to its current position:
dx = 0.75, dy = -1, dz = -1
Another pause
This is followed by another pause. Then the vectorMoveRel
method is called again to cause the penguin to move by the following incremental
amounts relative to its current position:
dx = 0.25, dy = -1, dz = -2
That relative move causes the penguin to end up back at the origin..
The method named vectorMoveAbs is shown in Listing 14. The
purpose of this method is to move an object to a position in 3D space specified
by x, y, and z coordinates without changing the orientation
of the object.
Listing 14. The method named vectorMoveAbs
public void vectorMoveAbs ( Object obj, Number
x, Number y, Number z, Number duration,
Number style) {
Posistion theVector =
Vector3(
0, 0, 0 ) ; |
|
theVector
.set( value , ( getVector(
right = x
, up
= y ,
forward =
z ) ) );
duration =
0 seconds |
if ( (
style
==
1 ) ) { . . } |
|
obj
.moveTo(
world
);
position of = theVector
duration =
duration seconds
style =
BEGIN_AND_END_GENTLY |
} else { |
|
if ( (
style
==
2 ) ) { |
|
obj
.moveTo(
world
);
position of = theVector
duration =
duration seconds
style =
BEGIN_GENTLY_AND_END_ABRUPTLY |
} else { |
|
if ( (
style
==
3 ) ) { |
|
obj
.moveTo(
world
);
position of = theVector
duration =
duration seconds
style =
BEGIN_ABRUPTLY_AND_END_GENTLY |
} else { |
|
obj
.moveTo(
world
);
position of = theVector
duration =
duration seconds
style =
BEGIN_AND_END_ABRUPTLY |
} |
} |
} |
} |
|
Dealing with the style
The most complicated thing about this method is the code
required to handle the style (treatment at the beginning and the end of the
move). Had I been satisfied to accept the default style value for the
moveTo method, the entire vectorAbsMove method would have
consisted of only about three statements.
Map style to numeric values
It is not possible to declare a parameter of
type style so that the user can pass the desired style directly to the
vectorAbsMove method. Therefore, it was necessary to map the four different
style types into the following numeric values:
- style = 1: BEGIN_AND_END_GENTLY
- style = 2: BEGIN_GENTLY_AND_END_ABRUPTLY
- style = 3: BEGIN_ABRUPTLY_AND_END_GENTLY
- style = 4 or any other value: BEGIN_AND_END_ABRUPTLY
The nested if-else statements in Listing 14 test the value of the
incoming style parameter and use that value to call the moveTo
method with the corresponding style.
Method parameters
The vectorAbsMove method requires the following six incoming
parameters:
- obj: A reference to the object to be moved.
- x: The value of the x-coordinate on the world's red
(horizontal) axis that the object will be moved to.
- y: The value of the y-coordinate on the world's green
(vertical) axis that the object will be moved to.
- z: The value of the z-coordinate on the world's blue
(depth) axis that the object will be moved to.
- duration: The elapsed time in seconds that will transpire
while the object is being moved.
- style: The treatment of the beginning and the end of the
movement as defined above.
Construct a vector and make the move
The incoming x, y,and z values are used to construct a
vector, which is used in a subsequent call to the moveTo method to
establish the position, (relative to the origin of the world), that the
object will be moved to by the moveTo method.
The method named vectorMoveRel is shown in Listing 15. The
purpose of this method is to move an object by an incremental amount in 3D space
relative to its current position. Values are specified for x, y,
and z, which will cause the object to be moved by those incremental
distances along the corresponding axes. The orientation of the object is
not changed.
Listing 15. The method named vectorMoveRel
public void vectorMoveRel ( Object obj, Number
x, Number y, Number z, Number duration,
Number style) {
Posistion currentVector =
Vector3(
0, 0, 0 ) ; Number newX =
0
; Number newY =
0
; Number newZ =
0
; |
|
currentVector
.set( value , (
obj
.getPosition()
asSeenBy =
world ) );
duration =
0 seconds |
newX
.set( value , ( ( (
currentVector .getDistanceRight() ) +
x
) ) );
duration =
0 seconds |
newY
.set( value , ( ( (
currentVector .getDistanceUp() ) +
y
) ) );
duration =
0 seconds |
newZ
.set( value , ( ( (
currentVector .getDistanceForward() ) +
z
) ) );
duration =
0 seconds |
world.vectorMoveAbs (
obj = obj
, x =
newX ,
y = newY
, z =
newZ ,
duration =
duration ,
style =
style ); |
} | |
Method parameters
This method requires the following six parameters:
- obj: A reference to the object to be moved.
- x: The incremental distance along the world's red
(horizontal) axis that the object will be moved.
- y: The incremental distance along the world's green
(vertical) axis that the object will be moved.
- z: The incremental distance along the world's blue
(depth) axis that the object will be moved.
- duration: The elapsed time in seconds that will transpire
while the object is being moved.
- style: The treatment of the beginning and the end of the
movement as defined above.
The method in action
The method begins by calling the getPosition method on the object to
get its current absolute coordinates. Then it uses those coordinates along
with the incoming parameter values to compute a new set of absolute coordinates
for the object. Finally, it calls the vectorMoveAbs method
described above to move the object.
Therefore, the vectorMoveRel method must have access to the
vectorMoveAbs method to function properly.
As mentioned earlier, you
will find a link in Resources to download a zip file
containing this program.
I have seen several postings on the Alice Community Forum asking how to save
a method or function and use it in future programs. One solution is to create and save
a library class containing methods, functions, and properties. It is a simple
matter to save the class in the local gallery. Then you can add an object of that
class to future programs that need access to the methods, functions, and
constants stored there.
The procedure for creating a library class
The procedure for creating a library class is as follows. Choose the smallest class
that you can find in the gallery. The smallest class that I have been able
to find in the gallery is a MagicianWand, which has a size of 9 kb.
Create an object from that class and add it to an Alice world. Make the
object invisible by setting its isShowing property to false. Rename
the object to Library in the object tree.
Create a folder (thumbnail) in the local
gallery
Create a new folder named A-Custom on your hard disk with the following path:
C:\...\Alice\Required\gallery\A-Custom
This will create a new folder (thumbnail) in your local gallery in which you can store
your new Library class and other new classes that you may create as well.
Create new methods, functions, and properties and
save the class
Create new methods, functions, and properties for the Library object.
Once you have created and tested your methods, functions, and properties, right
click on Library in the object tree. Select save object... from the
popup menu. Follow the instructions to save your new class in the folder
named A-Custom as described above.
The next time you start Alice, your new library class should appear in your
local gallery in a thumbnail named A-Custom.
Updating the library
As time goes on, you can add new methods, functions, and properties to the
class by:
- Creating an object of the Library class in a new world
- Adding new methods, functions, and properties to the Library object
- Saving the class in the folder named A-Custom as described
above.
Over time, you should be able to create a very useful collection of custom methods,
functions, and properties.
Using methods, functions, and properties from the
library
To use these custom methods, functions, and properties in a future program,
simply create a new object of the Library class and add it to the new world.
I can envision at least three different kinds of methods and functions that
would be appropriate for saving in the Library class.
Methods and
functions that apply to all objects...
Methods and functions that require an object as a parameter and can be
applied to any object created from the gallery would be
appropriate for including in the Library class. The sample program
named Library01 (see Listing 16) contains two such methods named
vectorMoveAbs and vectorMoveRel. (I developed and
explained these two methods earlier
in this lesson.)
Functions
that apply
to no object in particular...
Functions that don't apply to any object in particular but are similar
to the primitive functions belonging to the world would also be appropriate for
inclusion in the Library class. See the function named
degreesToRev in Listing 16 for example. This function accepts an
incoming parameter in degrees and returns a value in revolutions corresponding
to that number of degrees. (Recall that one revolution equals 360
degrees.)
Methods and functions that apply to a specific
object
The methods and functions described above can be called directly on the
Library object and are very convenient to use. It may also be useful
to include methods and functions in the Library class that apply to a
specific object . They would be less convenient to use, but would still be
more convenient than re-writing them every time they are needed. In this
case, it would be necessary to use the Alice clipboard and copy the method or
function from the Library object to some other object.
In this case, the Library class would serve simply as a convenient
repository for saving a method that will need to be copied to another object
later. The ability to create an object of the Library class in the
new world would
facilitate the ability to copy the method or function to another object.
Once the method or function has been successfully copied to another object in
the new world, the object of the Library class could be deleted from the
world to reduce its overall memory requirements.
Library properties
The Library class is also useful for storing frequently used
constants, such as the mathematical constant PI. Curiously, there is no
world function named PI or anything similar, although the value of PI can be
easily obtained by calling the Math.toRadians method on the world passing
180 degrees as a parameter. (I wonder how many Alice students are aware
of the relationship between degrees and radians.)
The program named Library01
The program named Library01 demonstrates the use of a Library
class containing:
- Two methods named vectorMoveAbs and vectorMoveRel, which
can be applied to any object as described
above.
- A function named degreesToRev that doesn't apply to any object as
described above.
- The mathematical constant named pi as described
above.
The program doesn't demonstrate the use of methods or functions that must be
copied to some other object as described
above.
Listing 17 shows the main method for the program named Library01.
Listing 17. The main method for the program named Library01.
public void main ( ) {
|
|
// Demonstrates use of Library methods, functions, and constants. |
// Chicken was manually placed at the origin of the world. |
// ----------------------------------------------------- |
// Print a constant that is stored in the Library object. |
print( ( Library.pi .toString() ) ); |
// Move chicken away from origin using absolute coordinates. |
Library.vectorMoveAbs ( obj = Chicken , x = -1 , y = 2 , z = 3 , duration = 1 , style = 4 ); |
// Move chicken back to origin in two incremental relative |
// moves with a turn in between. |
Library.vectorMoveRel ( obj = Chicken , x = 0.75 , y = -1 , z = -1 , duration = 3 , style = 1 ); |
// Call a Library function to execute the 90-degree turn. |
Chicken .turn( LEFT , ( Library.degreesToRev ( degrees = 90 ) ) ); |
Library.vectorMoveRel ( obj = Chicken , x = 0.25 , y = -1 , z = -2 , duration = 1 , style = 2 ); |
} |
|
Description of the program
You can download a zip file containing this program (see
Resources).
The world initially consists of a chicken object that has been aligned with
the pointOfView of the world. In addition to the chicken, an
invisible object of the class named Library was added to the world.
Behavior of the program
The program begins by printing the value of the Library property named
Library.pi.
Then the program calls the library method named Library.vectorMoveAbs
to move the chicken to a new position on the basis of the absolute coordinate
values (-1, 2, 3). The duration for the move is one second, and the style
is specified as 4 using the mapping shown earlier.
Then the program calls the method named Library.vectorMoveRel to move
the chicken to a new position on the basis of relative coordinate values (0.75,
-1, -1).
Turn the chicken
Following that relative move, the program calls the library function named
Library.degreesToRev to convert 90 degrees to revolutions and to pass that
value as a parameter to the primitive turn method belonging to the
chicken object.
Return to the origin of the world
Finally, the program once again calls the method named
Library.vectorMoveRel to move the chicken to a new position on the basis of
relative coordinate values (0.25, -1, -2). When this move is completed,
the chicken is back at its original position at the origin of the world, but the
chicken has a different orientation. It is no longer facing in the
direction of the world's blue axis, but is now facing in the opposite direction
from the world's red axis.
Run the programs
I encourage you to either download the zip file
containing the programs, or copy the code from
the listings into your Alice development
environment and play the programs. Experiment with the code, making
changes, and observing the results of your changes.
General resources
Resources from earlier lessons in the series titled "Learn to Program using
Alice"
- Table of Contents page
-
Slide index for classroom lecture use
-
Practice tests
- 100 Getting Started
- 105 Setting the Stage
- 110 Objects in 3D Space
- 115 Setting the Stage Manually, Part 1
- 120 Setting the Stage Manually, Part 2
- 125 Your First Alice Program
- 130 The Program Development Cycle
- 135
Functions that return values
- 140 Data
Types and Variables
- 145
World-Level Methods
- 150
Class-Level Methods and Inheritance
- 155 Syntax,
Runtime, and Logic Errors
- 160
Expressions and Operators
- 165
Sequence, Selection, and Loop Structures
- 170
Relational and Logical Operators
- 175 Counter
Loops, Nested Loops, and Sentinel Loops
- 180 Arrays
- 185 Lists
- 190 Events
and Interactive Programming
- 195 The
Transition to Java
-
900 Appendix A,
Behavior of
Primitive Methods
- 920
Appendix E, Restrictions and Limitations for Alice 2.0
Downloads
Complete program listings
Complete listings of the programs discussed in this lesson are shown below.
Listing 1. Two-dimensional projectile trajectory
program.
Trajectory01's CodeCreated by: Dick BaldwinworldEvents
When the world starts |
Do: |
|
Methods
public void main ( ) {
Number v0x =
0
; Number v0y =
0
; Number angleInDegrees =
0
; Number angleInRadians =
0
; |
|
world.setTheStage ( ); |
//
Get and process user input |
angleInDegrees
.set( value , ( NumberDialog(
question =
Angle in degrees? ) ) );
duration =
0 seconds |
angleInRadians
.set( value , ( Math.toRadians(
angleInDegrees ) ) );
duration =
0 seconds |
v0x
.set( value , ( (
10
* ( Math.cos(
angleInRadians ) ) ) ) ); |
v0y
.set( value , ( (
10
* ( Math.sin(
angleInRadians ) ) ) ) ); |
//
Fire the projectile |
world.fireTheProjectile (
vx = v0x
, vy
= v0y ); |
} |
public void setTheStage ( ) {
|
|
doInOrder { |
|
ground
.setPointOfView(
world
);
duration = 0 seconds |
safe
.setPointOfView(
world
);
duration = 0 seconds |
safe
.turn(
LEFT ,
0.25
revolutions );
duration = 0
seconds |
camera
.setPointOfView(
ground
);
duration = 0 seconds |
camera
.turn(
RIGHT ,
0.5
revolutions );
duration = 0
seconds |
camera
.moveAwayFrom(
target = safe
, amount
= 10 meters );
duration = 0
seconds |
camera
.move(
UP ,
.0001
meters );
duration = 0
seconds |
camera
.move(
BACKWARD ,
10 meters
);
duration = 0 seconds |
camera
.move(
RIGHT ,
5 meters
);
duration = 0 seconds |
} |
} |
public void fireTheProjectile ( Number vx, Number
vy) {
Number t =
0
; Number x =
0
; Number y =
0.01
; Number deltaT =
0.01
; Number tSquared =
0
; Number g =
32
; Number oldX =
0
; Number oldY =
0
; Number deltaX =
0
; Number deltaY =
0
; |
|
//
Equations of motion |
doInOrder { |
|
//
See http://hyperphysics.phy-astr.gsu.edu/hbase/traj.html#tra12 |
//
ax = 0 |
//
vx = v0x |
// x
= v0x * t |
//
ay = -g |
//
vy = v0y - g * t |
// y
= vy * t - 0.5 * g * t * t |
// g
= 32 feet per second per second |
} |
//
Action |
doInOrder { |
|
while ( (
y
> 0
) ) { |
|
t
.set( value , ( (
t
+
deltaT ) ) );
duration = 0
seconds |
tSquared
.set( value , ( (
t
* t
) ) );
duration = 0
seconds |
oldX
.set( value ,
x
);
duration = 0 seconds |
x
.set( value , ( (
t
* vx
) ) );
duration = 0
seconds |
deltaX
.set( value , ( (
x
- oldX
) ) );
duration = 0
seconds |
oldY
.set( value ,
y
);
duration = 0 seconds |
y
.set( value , ( (
t
* ( (
vy
- ( (
tSquared
* ( (
g
* 0.5
) ) ) ) ) ) ) ) );
duration = 0
seconds |
deltaY
.set( value , ( (
y
- oldY
) ) );
duration = 0
seconds |
doTogether { |
|
safe
.move(
FORWARD ,
deltaX
meters );
style =
BEGIN_AND_END_ABRUPTLY
duration =
.01 seconds |
safe
.move(
UP ,
deltaY
meters );
style =
BEGIN_AND_END_ABRUPTLY
duration =
.01 seconds |
} |
} |
} |
} | |
Listing 2. Multiplication game program.
MultTables01's CodeCreated by: Dick BaldwinworldEvents
When the world starts |
Do: |
|
Methods
public void main ( ) {
Number multiplier =
0
; Number multiplicand =
0
; Number product =
0
; Number answer =
0
; String Question =
Wht is
; |
|
//
Simple multiplication game designed to teach |
//
students their time tables up to 10 x 10. |
//
Continue looping until the user enters 1000 |
while ( (
answer
!=
1000 ) ) { |
|
multiplier
.set( value , ( Random.nextDouble()
minimum =
0
maximum = 10
integerOnly
= true ) ); |
multiplicand
.set( value , ( Random.nextDouble()
minimum =
0
maximum = 10
integerOnly
= true ) ); |
product
.set( value , ( (
multiplier *
multiplicand ) ) ); |
Question
.set( value , ( ( ( (
multiplier .toString() ) +
times
) + (
multiplicand .toString() ) ) +
= ? Enter
1000 to quit. ) ); |
answer
.set( value , ( NumberDialog(
question =
Question ) ) ); |
if ( (
answer
==
product ) ) { |
|
doTogether { |
|
cow
.say(
Correct
);
duration = 3 seconds |
cow
.playSound(
cow.moo
(0:01.567) ); |
doInOrder { |
|
cow.neck.head
.turn(
FORWARD
,
0.12 revolutions ); |
cow.neck.head
.turn(
BACKWARD
,
0.25 revolutions ); |
cow.neck.head
.turn(
FORWARD
,
0.12 revolutions ); |
} |
} |
} else { |
|
doTogether { |
|
//
Note the mechanism used to force an integer to be displayed. |
cow
.say( (
Wrong,
the answer is + ( ( Random.nextDouble()
minimum =
product
maximum =
product
integerOnly
= true ) .toString() ) ) );
duration =
3 seconds |
cow
.playSound(
world.chicken (0:08.542) ); |
doInOrder { |
|
cow.neck.head
.turn(
RIGHT
,
0.12 revolutions ); |
cow.neck.head
.turn(
LEFT
,
0.25 revolutions ); |
cow.neck.head
.turn(
RIGHT
,
0.12 revolutions ); |
} |
} |
} |
} |
} | |
Listing 3. Addition game program.
Addition01's CodeCreated by: Dick BaldwinworldEvents
When the world starts |
Do: |
|
Methods
public void main ( ) {
Number firstAddend =
0
; Number secondAddend =
0
; Number sum =
0
; Number answer =
0
; String Question =
Wht is
; |
|
//
Simple addition game designed to teach |
//
students how to add positive numbers up to 10+10 |
//
Continue looping until the user enters 1000 |
while ( (
answer
!=
1000 ) ) { |
|
firstAddend
.set( value , ( Random.nextDouble()
minimum =
0
maximum = 10
integerOnly
= true ) ); |
secondAddend
.set( value , ( Random.nextDouble()
minimum =
0
maximum = 10
integerOnly
= true ) ); |
sum
.set( value , ( (
firstAddend +
secondAddend ) ) ); |
Question
.set( value , ( ( ( (
firstAddend .toString() ) +
+
) + (
secondAddend .toString() ) ) +
= ? Enter
1000 to quit. ) ); |
answer
.set( value , ( NumberDialog(
question =
Question ) ) ); |
if ( (
answer
==
sum ) ) { |
|
doTogether { |
|
cow
.say(
Correct
);
duration = 3 seconds |
cow
.playSound(
cow.moo
(0:01.567) ); |
doInOrder { |
|
cow.neck.head
.turn(
FORWARD
,
0.12 revolutions ); |
cow.neck.head
.turn(
BACKWARD
,
0.25 revolutions ); |
cow.neck.head
.turn(
FORWARD
,
0.12 revolutions ); |
} |
} |
} else { |
|
doTogether { |
|
cow
.say( (
Wrong,
the answer is + ( ( Random.nextDouble()
minimum =
sum
maximum =
sum
integerOnly
= true ) .toString() ) ) );
duration =
3 seconds |
cow
.playSound(
world.chicken (0:08.542) ); |
doInOrder { |
|
cow.neck.head
.turn(
RIGHT
,
0.12 revolutions ); |
cow.neck.head
.turn(
LEFT
,
0.25 revolutions ); |
cow.neck.head
.turn(
RIGHT
,
0.12 revolutions ); |
} |
} |
} |
} |
} | |
Listing 4. Subtraction game program.
Subtraction01's CodeCreated by: Dick BaldwinworldEvents
When the world starts |
Do: |
|
Methods
public void main ( ) {
Number minuend =
0
; Number subtrahend =
0
; Number diference =
0
; Number answer =
0
; String Question =
Wht is
; |
|
//
Simple subtraction game designed to teach |
//
students how to subtract positive numbers up to 10 - 10 |
//
for positive answers only (no signed numbers) |
//
Continue looping until the user enters 1000 |
while ( (
answer
!=
1000 ) ) { |
|
minuend
.set( value , ( Random.nextDouble()
minimum =
1
maximum = 10
integerOnly
= true ) ); |
subtrahend
.set( value , ( (
minuend
+
1 ) ) ); |
while ( (
minuend
<
subtrahend ) ) { |
|
subtrahend
.set( value , ( Random.nextDouble()
minimum =
0
maximum = 10
integerOnly
= true ) ); |
} |
diference
.set( value , ( (
minuend
-
subtrahend ) ) ); |
Question
.set( value , ( ( ( (
minuend
.toString() ) +
-
) + (
subtrahend .toString() ) ) +
= ? Enter
1000 to quit. ) ); |
answer
.set( value , ( NumberDialog(
question =
Question ) ) ); |
if ( (
answer
==
diference ) ) { |
|
doTogether { |
|
cow
.say(
Correct
);
duration = 3 seconds |
cow
.playSound(
cow.moo
(0:01.567) ); |
doInOrder { |
|
cow.neck.head
.turn(
FORWARD
,
0.12 revolutions ); |
cow.neck.head
.turn(
BACKWARD
,
0.25 revolutions ); |
cow.neck.head
.turn(
FORWARD
,
0.12 revolutions ); |
} |
} |
} else { |
|
doTogether { |
|
//
Note the mechanism for forcing the output to be an integer. |
cow
.say( (
Wrong,
the answer is + ( ( Random.nextDouble()
minimum =
diference
maximum =
diference
integerOnly
= true ) .toString() ) ) );
duration =
3 seconds |
cow
.playSound(
world.chicken (0:08.542) ); |
doInOrder { |
|
cow.neck.head
.turn(
RIGHT
,
0.12 revolutions ); |
cow.neck.head
.turn(
LEFT
,
0.25 revolutions ); |
cow.neck.head
.turn(
RIGHT
,
0.12 revolutions ); |
} |
} |
} |
} |
} | |
Listing 5. Drag race game program.
DragRace01's CodeCreated by: Dick BaldwinworldEvents
When is clicked on
redCar |
Do: |
|
When is clicked on
yellowCar |
Do: |
|
When the world starts |
Do: |
|
Methods
public void runTheRace ( ) {
Number trackLength =
0
; Number redDistance =
0
; Number yellowDistance =
0
; Number maxDistance =
0
; Number redIncrement =
0
; Number yellowIncrement =
0
; |
|
trackLength
.set( value , (
subject =
road .getWidth() ) ); |
world.runTheChristmasTree ( ); |
while ( (
maxDistance < ( (
trackLength - (
subject =
redCar .getDepth() ) ) ) ) ) { |
|
//
Update the variables. |
doInOrder { . . . . . } |
|
redIncrement
.set( value , ( Random.nextDouble()
minimum =
0.5
maximum =
1.5 ) );
duration =
0 seconds |
yellowIncrement
.set( value , ( Random.nextDouble()
minimum =
0.5
maximum =
1.5 ) );
duration =
0 seconds |
redDistance
.set( value , ( (
redDistance +
redIncrement ) ) );
duration =
0 seconds |
yellowDistance
.set( value , ( (
yellowDistance +
yellowIncrement ) ) );
duration =
0 seconds |
maxDistance
.set( value , ( Math.max(
redDistance ,
yellowDistance ) ) );
duration =
0 seconds |
} |
//
Move the cars. |
doTogether { . . } |
|
redCar
.move(
FORWARD
,
redIncrement meters );
duration =
.025 seconds
style =
BEGIN_AND_END_ABRUPTLY |
yellowCar
.move(
FORWARD
,
yellowIncrement meters );
duration =
.025 seconds
style =
BEGIN_AND_END_ABRUPTLY |
} |
} |
//
Determine the winner. |
if ( (
redDistance >
yellowDistance ) ) { . . . . } |
|
print(
Red wins
); |
if ( (
world.pickedCar ==
redCar
) ) { . . } |
|
print(
You win
the bet. ); |
} else { |
|
print(
You lose
the bet. ); |
} |
} else { |
|
print(
Yellow
wins. ); |
if ( (
world.pickedCar ==
yellowCar
) ) { |
|
print(
You win
the bet. ); |
} else { |
|
print(
You lose
the bet. ); |
} |
} |
} |
public void red ( ) {
|
|
world.pickedCar
.set( value ,
redCar
); |
print(
----------You picked red. ); |
world.runTheRace ( ); |
} |
public void yellow ( ) {
|
|
world.pickedCar
.set( value ,
yellowCar
); |
print(
----------You picked yellow. ); |
world.runTheRace ( ); |
} |
public void main ( ) {
|
|
print( ); |
print(
Click on
a car to start the race. ); |
} |
public void runTheChristmasTree ( ) {
|
|
doInOrder { |
|
//
Run the lights to start the race. |
circle
.set( isShowing ,
true
);
duration = 0 seconds |
wait(
1
second ); |
circle
.set( color ,
(1, 1, 0) ); |
wait(
1
second ); |
circle
.set( color ,
(0, 1, 0) );
duration =
0 seconds |
} |
} | |
Listing 6. Approximate trajectory program.
JumpingFish01's CodeCreated by: Dick BaldwinworldEvents
When the world starts |
Do: |
|
Methods
public void main ( ) {
|
|
world.setTheStage ( ); |
doTogether { |
|
bug2.fly ( ); |
bug3.fly ( ); |
bug4.fly ( ); |
bug5.fly ( ); |
mainBug.fly ( ); |
} |
} |
public void setTheStage ( ) {
|
|
//
Set size and initial location of the fish and the camera. |
fish
.resize(
4
);
duration = 0 seconds |
water
.setPointOfView(
world
);
duration = 0 seconds |
fish
.setPointOfView(
water
);
duration = 0 seconds |
camera
.setPointOfView(
water
);
duration = 0 seconds |
camera
.turn(
RIGHT
,
0.25 revolutions );
duration =
0 seconds |
camera
.move(
FORWARD
,
20 meters );
duration =
0 seconds |
camera
.move(
UP
,
1 meter );
duration =
0 seconds |
camera
.pointAt(
fish
);
duration = 0 seconds |
camera
.move(
LEFT
,
2 meters );
duration =
0 seconds |
water
.set( isShowing ,
true
); |
} |
fishMethods
public void jump ( ) {
Number azimuthInRadians =
0
; Number length =
0
; |
|
doInOrder { |
|
//
Compute the horizontal azimuth to the bug. |
azimuthInRadians
.set( value , ( Math.atan( ( (
world.zCoor /
world.xCoor ) ) ) ) );
duration =
0 seconds |
if ( (
world.xCoor <
0
) ) { |
|
azimuthInRadians
.set( value , ( (
azimuthInRadians +
3.14
) ) );
duration =
0 seconds |
} else { |
|
Do Nothing |
} |
length
.set( value , ( ( ( (
world.xCoor / ( Math.cos(
azimuthInRadians ) ) ) ) + (
subject =
mainBug .getDepth() ) ) ) );
duration =
0 seconds |
fish
.turn(
LEFT
, ( ( ( (
azimuthInRadians /
2
) ) /
3.14
) ) );
duration =
0 seconds |
} |
doInOrder { |
|
//
Set vertical launch angle and make the fish visible. |
fish
.turn(
BACKWARD
,
.125 revolutions );
duration =
0 seconds |
fish
.set( isShowing ,
true
);
duration = 0 seconds
style =
BEGIN_AND_END_ABRUPTLY |
doTogether { |
|
doTogether { |
|
mainBug.leftWing
.roll(
LEFT
,
0.25 revolutions );
duration =
.25 seconds
style =
BEGIN_AND_END_ABRUPTLY |
mainBug.rightWing
.roll(
RIGHT
,
0.25 revolutions );
duration =
0.25 seconds
style =
BEGIN_AND_END_ABRUPTLY |
} |
doTogether { |
|
mainBug.leftWing
.roll(
RIGHT
,
.375 revolutions );
duration =
0.25 seconds
style =
BEGIN_AND_END_ABRUPTLY |
mainBug.rightWing
.roll(
LEFT
,
0.38 revolutions );
duration =
0.25 seconds
style =
BEGIN_AND_END_ABRUPTLY |
} |
//
This code approximates the trajectory of a projectile. |
fish
.move(
FORWARD
,
length meters );
style =
BEGIN_AND_END_ABRUPTLY
duration =
2.5 seconds |
fish
.turn(
FORWARD
,
0.25 revolutions );
style =
BEGIN_AND_END_ABRUPTLY
duration =
2.5 seconds |
} |
doTogether { |
|
//
Make the bug and the fish disappear under the water. |
mainBug
.set( isShowing ,
false
);
duration = 0 seconds
style =
BEGIN_AND_END_ABRUPTLY |
fish
.move(
FORWARD
,
4 meters );
duration =
0.25 seconds
style =
BEGIN_AND_END_ABRUPTLY |
} |
} |
} |
mainBugMethods
public void fly ( ) {
Number xInc =
0
; Number yInc =
0
; Number zInc =
0
; Number height =
2
; Number turnAngle =
0.25
; |
|
doInOrder { |
|
//
Initialize the bug's size and location |
mainBug
.resize(
20
);
duration = 0 seconds |
mainBug
.setPointOfView(
water
);
duration = 0 seconds |
mainBug
.move(
FORWARD
,
xCoor meters );
duration =
0 seconds |
mainBug
.move(
LEFT
,
zCoor meters );
duration =
0 seconds |
mainBug
.move(
UP
,
2 meters );
duration =
0 seconds |
mainBug
.set( isShowing ,
true
);
duration = 0 seconds |
} |
while ( (
height
>
0.1 ) ) { |
|
//
Make bug move randomly and flap wings. |
//
Bug ends up landing on the water |
doInOrder { |
|
xInc
.set( value , ( Random.nextDouble()
minimum =
-0.5
maximum =
0.5 ) );
duration =
0 seconds |
zInc
.set( value , ( Random.nextDouble()
minimum =
-0.5
maximum =
0.5 ) );
duration =
0 seconds |
yInc
.set( value , ( Random.nextDouble()
minimum =
0.05
maximum =
0.2 ) );
duration =
0 seconds |
turnAngle
.set( value , ( Random.nextDouble()
minimum =
-0.25
maximum =
0.25 ) );
duration =
0 seconds |
world.xCoor
.set( value , ( (
world.xCoor +
xInc
) ) );
duration =
0 seconds |
world.zCoor
.set( value , ( (
world.zCoor +
zInc
) ) );
duration =
0 seconds |
height
.set( value , ( (
height
-
yInc ) ) );
duration =
0 seconds |
} |
doTogether { |
|
doInOrder { |
|
doTogether { |
|
mainBug.leftWing
.roll(
LEFT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
mainBug.rightWing
.roll(
RIGHT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
} |
doTogether { |
|
mainBug.leftWing
.roll(
RIGHT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
mainBug.rightWing
.roll(
LEFT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
} |
} |
doTogether { |
|
mainBug
.move(
FORWARD
,
xInc meters );
style =
BEGIN_AND_END_ABRUPTLY
asSeenBy =
water |
mainBug
.move(
LEFT
,
zInc meters );
asSeenBy =
water
style =
BEGIN_AND_END_ABRUPTLY |
mainBug
.move(
DOWN
,
yInc meters );
asSeenBy =
water
style =
BEGIN_AND_END_ABRUPTLY |
mainBug
.turn(
RIGHT
,
turnAngle revolutions );
duration =
1 second |
} |
} |
} |
fish.jump ( ); |
} |
bug2Methods
public void fly ( ) {
Number xInc =
0
; Number yInc =
0
; Number zInc =
0
; Number height =
2
; Number turnAngle =
0.25
; |
|
doInOrder { |
|
//
Initialize the bug's size and location |
wait( ( Random.nextDouble()
minimum =
0.1
maximum =
2 ) ); |
bug2
.resize(
20
);
duration = 0 seconds |
bug2
.setPointOfView(
water
);
duration = 0 seconds |
bug2
.move(
FORWARD
, ( Random.nextDouble()
minimum =
-1
maximum =
-3 ) );
duration =
0 seconds |
bug2
.move(
LEFT
, ( Random.nextDouble()
minimum =
-12
maximum =
-14 ) );
duration =
0 seconds |
bug2
.move(
UP
, ( Random.nextDouble()
minimum =
0.5
maximum =
2 ) );
duration =
0 seconds |
bug2
.set( isShowing ,
true
);
duration = 0 seconds |
} |
while ( (
height
>
0.1 ) ) { |
|
//
Make bug move randomly and flap wings. |
//
Bug ends up landing on the water |
doInOrder { |
|
xInc
.set( value , ( Random.nextDouble()
minimum =
-0.5
maximum =
0.5 ) );
duration =
0 seconds |
zInc
.set( value , ( Random.nextDouble()
minimum =
-0.5
maximum =
0.5 ) );
duration =
0 seconds |
yInc
.set( value , ( Random.nextDouble()
minimum =
-0.2
maximum =
0.2 ) );
duration =
0 seconds |
turnAngle
.set( value , ( Random.nextDouble()
minimum =
-0.25
maximum =
0.25 ) );
duration =
0 seconds |
world.xCoor
.set( value , ( (
world.xCoor +
xInc
) ) );
duration =
0 seconds |
world.zCoor
.set( value , ( (
world.zCoor +
zInc
) ) );
duration =
0 seconds |
height
.set( value , ( (
height
-
yInc ) ) );
duration =
0 seconds |
} |
doTogether { |
|
doInOrder { |
|
doTogether { |
|
bug2.leftWing
.roll(
LEFT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
bug2.rightWing
.roll(
RIGHT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
} |
doTogether { |
|
bug2.leftWing
.roll(
RIGHT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
bug2.rightWing
.roll(
LEFT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
} |
} |
doTogether { |
|
bug2
.move(
FORWARD
,
xInc meters );
style =
BEGIN_AND_END_ABRUPTLY
asSeenBy =
water |
bug2
.move(
LEFT
,
zInc meters );
asSeenBy =
water
style =
BEGIN_AND_END_ABRUPTLY |
bug2
.move(
DOWN
,
yInc meters );
asSeenBy =
water
style =
BEGIN_AND_END_ABRUPTLY |
bug2
.turn(
RIGHT
,
turnAngle revolutions );
duration =
1 second |
} |
} |
} |
} |
bug3Methods
public void fly ( ) {
Number xInc =
0
; Number yInc =
0
; Number zInc =
0
; Number height =
2
; Number turnAngle =
0.25
; |
|
doInOrder { |
|
//
Initialize the bug's size and location |
wait( ( Random.nextDouble()
minimum =
0.1
maximum =
2 ) ); |
bug3
.resize(
20
);
duration = 0 seconds |
bug3
.setPointOfView(
water
);
duration = 0 seconds |
bug3
.move(
FORWARD
, ( Random.nextDouble()
minimum =
-1
maximum =
-3 ) );
duration =
0 seconds |
bug3
.move(
LEFT
, ( Random.nextDouble()
minimum =
-12
maximum =
-14 ) );
duration =
0 seconds |
bug3
.move(
UP
, ( Random.nextDouble()
minimum =
0.5
maximum =
2 ) );
duration =
0 seconds |
bug3
.set( isShowing ,
true
);
duration = 0 seconds |
} |
while ( (
height
>
0.1 ) ) { |
|
//
Make bug move randomly and flap wings. |
//
Bug ends up landing on the water |
doInOrder { |
|
xInc
.set( value , ( Random.nextDouble()
minimum =
-0.5
maximum =
0.5 ) );
duration =
0 seconds |
zInc
.set( value , ( Random.nextDouble()
minimum =
-0.5
maximum =
0.5 ) );
duration =
0 seconds |
yInc
.set( value , ( Random.nextDouble()
minimum =
-0.2
maximum =
0.2 ) );
duration =
0 seconds |
turnAngle
.set( value , ( Random.nextDouble()
minimum =
-0.25
maximum =
0.25 ) );
duration =
0 seconds |
world.xCoor
.set( value , ( (
world.xCoor +
xInc
) ) );
duration =
0 seconds |
world.zCoor
.set( value , ( (
world.zCoor +
zInc
) ) );
duration =
0 seconds |
height
.set( value , ( (
height
-
yInc ) ) );
duration =
0 seconds |
} |
doTogether { |
|
doInOrder { |
|
doTogether { |
|
bug3.leftWing
.roll(
LEFT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
bug3.rightWing
.roll(
RIGHT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
} |
doTogether { |
|
bug3.leftWing
.roll(
RIGHT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
bug3.rightWing
.roll(
LEFT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
} |
} |
doTogether { |
|
bug3
.move(
FORWARD
,
xInc meters );
style =
BEGIN_AND_END_ABRUPTLY
asSeenBy =
water |
bug3
.move(
LEFT
,
zInc meters );
asSeenBy =
water
style =
BEGIN_AND_END_ABRUPTLY |
bug3
.move(
DOWN
,
yInc meters );
asSeenBy =
water
style =
BEGIN_AND_END_ABRUPTLY |
bug3
.turn(
RIGHT
,
turnAngle revolutions );
duration =
1 second |
} |
} |
} |
} |
bug4Methods
public void fly ( ) {
Number xInc =
0
; Number yInc =
0
; Number zInc =
0
; Number height =
2
; Number turnAngle =
0.25
; |
|
doInOrder { |
|
//
Initialize the bug's size and location |
wait( ( Random.nextDouble()
minimum =
0.1
maximum =
2 ) ); |
bug4
.resize(
20
);
duration = 0 seconds |
bug4
.setPointOfView(
water
);
duration = 0 seconds |
bug4
.move(
FORWARD
, ( Random.nextDouble()
minimum =
-1
maximum =
-3 ) );
duration =
0 seconds |
bug4
.move(
LEFT
, ( Random.nextDouble()
minimum =
-12
maximum =
-14 ) );
duration =
0 seconds |
bug4
.move(
UP
, ( Random.nextDouble()
minimum =
0.5
maximum =
2 ) );
duration =
0 seconds |
bug4
.set( isShowing ,
true
);
duration = 0 seconds |
} |
while ( (
height
>
0.1 ) ) { |
|
//
Make bug move randomly and flap wings. |
//
Bug ends up landing on the water |
doInOrder { |
|
xInc
.set( value , ( Random.nextDouble()
minimum =
-0.5
maximum =
0.5 ) );
duration =
0 seconds |
zInc
.set( value , ( Random.nextDouble()
minimum =
-0.5
maximum =
0.5 ) );
duration =
0 seconds |
yInc
.set( value , ( Random.nextDouble()
minimum =
-0.2
maximum =
0.2 ) );
duration =
0 seconds |
turnAngle
.set( value , ( Random.nextDouble()
minimum =
-0.25
maximum =
0.25 ) );
duration =
0 seconds |
world.xCoor
.set( value , ( (
world.xCoor +
xInc
) ) );
duration =
0 seconds |
world.zCoor
.set( value , ( (
world.zCoor +
zInc
) ) );
duration =
0 seconds |
height
.set( value , ( (
height
-
yInc ) ) );
duration =
0 seconds |
} |
doTogether { |
|
doInOrder { |
|
doTogether { |
|
bug4.leftWing
.roll(
LEFT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
bug4.rightWing
.roll(
RIGHT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
} |
doTogether { |
|
bug4.leftWing
.roll(
RIGHT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
bug4.rightWing
.roll(
LEFT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
} |
} |
doTogether { |
|
bug4
.move(
FORWARD
,
xInc meters );
style =
BEGIN_AND_END_ABRUPTLY
asSeenBy =
water |
bug4
.move(
LEFT
,
zInc meters );
asSeenBy =
water
style =
BEGIN_AND_END_ABRUPTLY |
bug4
.move(
DOWN
,
yInc meters );
asSeenBy =
water
style =
BEGIN_AND_END_ABRUPTLY |
bug4
.turn(
RIGHT
,
turnAngle revolutions );
duration =
1 second |
} |
} |
} |
} |
bug5Methods
public void fly ( ) {
Number xInc =
0
; Number yInc =
0
; Number zInc =
0
; Number height =
2
; Number turnAngle =
0.25
; |
|
doInOrder { |
|
//
Initialize the bug's size and location |
wait( ( Random.nextDouble()
minimum =
0.1
maximum =
2 ) ); |
bug5
.resize(
20
);
duration = 0 seconds |
bug5
.setPointOfView(
water
);
duration = 0 seconds |
bug5
.move(
FORWARD
, ( Random.nextDouble()
minimum =
-1
maximum =
-3 ) );
duration =
0 seconds |
bug5
.move(
LEFT
, ( Random.nextDouble()
minimum =
-12
maximum =
-14 ) );
duration =
0 seconds |
bug5
.move(
UP
, ( Random.nextDouble()
minimum =
0.5
maximum =
2 ) );
duration =
0 seconds |
bug5
.set( isShowing ,
true
);
duration = 0 seconds |
} |
while ( (
height
>
0.1 ) ) { |
|
//
Make bug move randomly and flap wings. |
//
Bug ends up landing on the water |
doInOrder { |
|
xInc
.set( value , ( Random.nextDouble()
minimum =
-0.5
maximum =
0.5 ) );
duration =
0 seconds |
zInc
.set( value , ( Random.nextDouble()
minimum =
-0.5
maximum =
0.5 ) );
duration =
0 seconds |
yInc
.set( value , ( Random.nextDouble()
minimum =
-0.2
maximum =
0.2 ) );
duration =
0 seconds |
turnAngle
.set( value , ( Random.nextDouble()
minimum =
-0.25
maximum =
0.25 ) );
duration =
0 seconds |
world.xCoor
.set( value , ( (
world.xCoor +
xInc
) ) );
duration =
0 seconds |
world.zCoor
.set( value , ( (
world.zCoor +
zInc
) ) );
duration =
0 seconds |
height
.set( value , ( (
height
-
yInc ) ) );
duration =
0 seconds |
} |
doTogether { |
|
doInOrder { |
|
doTogether { |
|
bug5.leftWing
.roll(
LEFT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
bug5.rightWing
.roll(
RIGHT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
} |
doTogether { |
|
bug5.leftWing
.roll(
RIGHT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
bug5.rightWing
.roll(
LEFT
,
0.25 revolutions ); ( Random.nextDouble()
minimum =
0.5
maximum =
0.6 )
style =
BEGIN_AND_END_ABRUPTLY |
} |
} |
doTogether { |
|
bug5
.move(
FORWARD
,
xInc meters );
style =
BEGIN_AND_END_ABRUPTLY
asSeenBy =
water |
bug5
.move(
LEFT
,
zInc meters );
asSeenBy =
water
style =
BEGIN_AND_END_ABRUPTLY |
bug5
.move(
DOWN
,
yInc meters );
asSeenBy =
water
style =
BEGIN_AND_END_ABRUPTLY |
bug5
.turn(
RIGHT
,
turnAngle revolutions );
duration =
1 second |
} |
} |
} |
} | |
Listing 7. An improved approximate trajectory
program.
Trajectory02's CodeCreated by: Dick BaldwinworldEvents
When the world starts |
Do: |
|
Methods
public void main ( ) {
|
|
//
Executes a circular arc trajectory given the launch angle |
//
and the distance to the target as user input values. |
world.setTheStage ( ); |
projectile.fly ( ); |
} |
public void setTheStage ( ) {
|
|
//
Move all objects to the origin in 3D space. |
doInOrder { . . . . . } |
|
ground
.setPointOfView(
world
);
duration = 0 seconds |
camera
.setPointOfView(
ground
);
duration = 0 seconds |
projectile
.setPointOfView(
ground
);
duration = 0 seconds |
target
.setPointOfView(
ground
);
duration = 0 seconds |
startPointMarker
.setPointOfView(
ground
);
duration = 0 seconds |
} |
//
Position the camera. |
doInOrder { . . . } |
|
camera
.turn(
LEFT
,
0.25 revolutions );
duration =
0 seconds |
camera
.moveAwayFrom(
target =
ground ,
amount =
20 meters );
duration =
0 seconds |
camera
.move(
UP
,
.0001 meters );
duration =
0 seconds |
} |
//
Make miscellaneous adjustments to objects. |
doInOrder { |
|
projectile
.resize(
2
);
duration = 0 seconds |
target
.resize(
2
);
duration = 0 seconds |
startPointMarker
.resize(
3
);
duration = 0 seconds |
startPointMarker
.move(
DOWN
, ( ( (
subject =
startPointMarker .getHeight() ) /
2
) ) );
duration =
0 seconds |
} |
} |
projectileMethods
public void fly ( ) {
Number pi =
0
; Number chord =
0
; Number angle =
0
; Number arcLength =
0
; |
|
//
arcLength = (pi*chord*angle)/(180*sin(angle)) |
//
Note, the distance to the target is the chord. |
//
The distance to move is the arcLength. |
//
Get user input. |
doInOrder { . . } |
|
chord
.set( value , ( NumberDialog(
question =
Distance to target: ) ) ); |
angle
.set( value , ( NumberDialog(
question =
Launch angle in degrees: ) ) ); |
} |
//
Put objects into position. |
doInOrder { |
|
//
Stand target upright and move into position. |
target
.turn(
BACKWARD
,
0.25 revolutions );
duration =
0 seconds |
target
.move(
FORWARD
, ( (
chord
/
2 ) ) );
duration =
0 seconds
asSeenBy =
ground |
target
.set( isShowing ,
true
);
duration = 0 seconds |
//
Move projectile into position. |
projectile
.move(
BACKWARD
, ( (
chord
/
2 ) ) );
duration =
0 seconds
asSeenBy =
ground |
projectile
.set( isShowing ,
true
);
duration = 0 seconds |
//
Move startPointMarker into position. |
startPointMarker
.move(
BACKWARD
, ( (
chord
/
2 ) ) );
duration =
0 seconds |
startPointMarker
.set( isShowing ,
true
); |
} |
//
Aim projectile and compute fire control solution. |
doInOrder { |
|
projectile
.turn(
BACKWARD
, ( (
angle
/
360 ) ) );
duration =
2 seconds |
//
Get the value of pi. |
pi
.set( value , ( Math.toRadians(
180
) ) );
duration =
0 seconds |
arcLength
.set( value , ( (
pi
* ( ( ( (
chord
*
angle ) ) / ( (
180
* ( Math.sin( ( Math.toRadians(
angle
) ) ) ) ) ) ) ) ) ) ); |
} |
//
Fire the projectile. |
doTogether { |
|
//
The following two statements produce the projectile motion. |
projectile
.move(
FORWARD
,
arcLength meters );
duration =
2 seconds |
projectile
.turn(
FORWARD
, ( (
2
* ( (
angle
/
360 ) ) ) ) );
duration =
2 seconds |
} |
} | |
Listing 10. The program named ObjectParameters01.
ObjectParameters01's CodeCreated by: Dick BaldwinworldEvents
When the world starts |
Do: |
|
Methods
public void main ( ) {
|
|
//
The purpose of this program is to show how to |
//
call custom methods on method parameters of |
//
type Object. Also shows how to cause Object parameter |
//
to point to a different object than the one passed |
//
as a parameter by the calling method. |
world.setTheStage ( ); |
//
Call the same method once for each of four objects. |
//
This method will call a primitive method and a custom method |
//
on the first three objects passed as a parameter. It will
cause the |
//
received parameter to point to a different object in the last
call. |
world.makeObjectsDance (
obj = cow
); |
world.makeObjectsDance (
obj =
penguin1 ); |
world.makeObjectsDance (
obj =
penguin2 ); |
world.makeObjectsDance (
obj =
ground ); |
} |
public void setTheStage ( ) {
|
|
ground
.setPointOfView(
world
);
duration = 0 seconds |
cow
.setPointOfView(
ground
);
duration = 0 seconds |
cow
.move(
RIGHT
,
1 meter );
duration =
0 seconds |
penguin1
.setPointOfView(
ground
);
duration = 0 seconds |
penguin1
.move(
LEFT
,
1 meter );
duration =
0 seconds |
penguin2
.setPointOfView(
ground
);
duration = 0 seconds |
penguin2
.move(
LEFT
,
2 meters );
duration =
0 seconds |
camera
.setPointOfView(
ground
);
duration = 0 seconds |
camera
.turn(
LEFT
,
0.5 revolutions );
duration =
0 seconds |
camera
.move(
BACKWARD
,
10 meters );
duration =
0 seconds |
camera
.move(
UP
,
3 meters );
duration =
0 seconds |
camera
.move(
RIGHT
,
3 meters );
duration =
0 seconds |
camera
.pointAt(
ground
);
duration = 0 seconds |
} |
public void makeObjectsDance ( Object obj) {
|
|
//
Call a primitive method on the incoming object. |
ground
.say(
Call
primitive turn method on incoming parameter. );
duration =
3 seconds |
obj
.turn(
RIGHT
,
1 revolution ); |
//
Identify the specific object and call a custom |
//
method on that object. |
ground
.say(
Identify
the specific object and call a custom method on that object.
);
duration = 3 seconds |
if ( (
obj
==
cow ) ) { |
|
cow.dance ( ); |
} else { |
|
if ( (
obj
==
penguin1 ) ) { |
|
penguin1.dance ( ); |
} else { |
|
if ( (
obj
==
penguin2 ) ) { |
|
penguin2.dance ( ); |
} else { |
|
//
Cause the
parameter to point to the camera |
//
instead
of the ground. Note that the ground |
//
was
caused to turn at the beginning of this method. |
ground
.say(
No match.
Make the parameter point to camera instead of ground.
);
duration = 3 seconds |
obj
.set( value ,
camera
); |
obj
.turn(
LEFT
,
1 revolution );
asSeenBy =
cow |
} |
} |
} |
} |
cowMethods
public void dance ( ) {
|
|
cow.walk (
times = 3
,
speed = 3 ); |
cow
.turn(
RIGHT
,
0.5 revolutions ); |
cow.tailSwish (
times = 5
,
speed = 3 ); |
cow.walk (
times = 3
,
speed = 3 ); |
cow
.turn(
LEFT
,
0.5 revolutions ); |
} |
penguin1Methods
public void dance ( ) {
|
|
//
Note that the behavior of this dance method |
//
is a little different from the behavior of the |
//
dance method belonging to penguin2. |
penguin1.walking (
x = 2
); |
penguin1.turn_head_left ( ); |
penguin1.turn_head_right ( ); |
penguin1.wing_flap (
times = 2
); |
penguin1
.turn(
LEFT
,
0.5 revolutions ); |
penguin1.walking (
x = 2
); |
penguin1
.turn(
LEFT
,
0.5 revolutions ); |
} |
penguin2Methods
public void dance ( ) {
|
|
//
Note that the behavior of this dance method |
//
is a little different from the dance method |
//
belonging to the penguin1 object. |
penguin2.walking (
x = 2
); |
penguin2.wing_flap (
times = 2
); |
penguin2.jumping (
height =
1 ); |
penguin2
.turn(
LEFT
,
0.5 revolutions ); |
penguin2.walking (
x = 2
); |
penguin2
.turn(
LEFT
,
0.5 revolutions ); |
} | |
Listing 11. The program named ObjectListItem01.
ObjectListItem01's CodeCreated by: Dick BaldwinworldEvents
When the world starts |
Do: |
|
Methods
public void main ( ) {
Object[] myList = ; |
|
//
The purpose of this program is to show how to |
//
call custom methods on objects stored in a list |
//
as type Object. |
world.setTheStage ( ); |
world.iterateTheList (
myList =
myList ); |
} |
public void setTheStage ( ) {
|
|
ground
.setPointOfView(
world
);
duration = 0 seconds |
cow
.setPointOfView(
ground
);
duration = 0 seconds |
cow
.move(
RIGHT
,
1 meter );
duration =
0 seconds |
penguin1
.setPointOfView(
ground
);
duration = 0 seconds |
penguin1
.move(
LEFT
,
1 meter );
duration =
0 seconds |
penguin2
.setPointOfView(
ground
);
duration = 0 seconds |
penguin2
.move(
LEFT
,
2 meters );
duration =
0 seconds |
camera
.setPointOfView(
ground
);
duration = 0 seconds |
camera
.turn(
LEFT
,
0.5 revolutions );
duration =
0 seconds |
camera
.move(
BACKWARD
,
10 meters );
duration =
0 seconds |
camera
.move(
UP
,
3 meters );
duration =
0 seconds |
camera
.move(
RIGHT
,
3 meters );
duration =
0 seconds |
camera
.pointAt(
ground
);
duration = 0 seconds |
} |
public void iterateTheList ( Object[] myList)
{
|
|
doInOrder { |
|
//
Iterate the list, calling the same method during each |
//
iteration, passing the next object in the list to the method |
//
each time the method is called. |
//
This method will call a primitive method and a custom method |
//
on the first three objects in the list. It will cause the |
//
fourth item in the list to point to a different object than
the |
//
object originally stored in the list. |
} |
For all
myList
, one item_from_myList at a time { |
|
//
Call a primitive method on the incoming object. |
ground
.say(
Call
primitive turn method on list item. );
duration =
3 seconds |
item_from_myList
.turn(
RIGHT
,
1 revolution ); |
//
Identify the specific object and call a custom |
//
method on that object. |
ground
.say(
Identify
the specific object and call a custom method on that object.
);
duration = 3 seconds |
if ( (
item_from_myList ==
cow
) ) { |
|
cow.dance ( ); |
} else { |
|
if ( (
item_from_myList ==
penguin1
) ) { |
|
penguin1.dance ( ); |
} else { |
|
if ( (
item_from_myList ==
penguin2
) ) { |
|
penguin2.dance ( ); |
} else { |
|
//
Cause the
parameter to point to the camera |
//
instead
of the ground. Note that the ground |
//
was
caused to turn at the beginning of this method. |
ground
.say(
No match.
Make the list item point to camera instead of ground.
);
duration = 3 seconds |
item_from_myList
.set( value ,
camera
); |
item_from_myList
.turn(
LEFT
,
1 revolution );
asSeenBy =
cow |
} |
} |
} |
} |
} |
cowMethods
public void dance ( ) {
|
|
cow.walk (
times = 3
,
speed = 3 ); |
cow
.turn(
RIGHT
,
0.5 revolutions ); |
cow.tailSwish (
times = 5
,
speed = 3 ); |
cow.walk (
times = 3
,
speed = 3 ); |
cow
.turn(
LEFT
,
0.5 revolutions ); |
} |
penguin1Methods
public void dance ( ) {
|
|
//
Note that the behavior of this dance method |
//
is a little different from the behavior of the |
//
dance method belonging to penguin2. |
penguin1.walking (
x = 2
); |
penguin1.turn_head_left ( ); |
penguin1.turn_head_right ( ); |
penguin1.wing_flap (
times = 2
); |
penguin1
.turn(
LEFT
,
0.5 revolutions ); |
penguin1.walking (
x = 2
); |
penguin1
.turn(
LEFT
,
0.5 revolutions ); |
} |
penguin2Methods
public void dance ( ) {
|
|
//
Note that the behavior of this dance method |
//
is a little different from the dance method |
//
belonging to the penguin1 object. |
penguin2.walking (
x = 2
); |
penguin2.wing_flap (
times = 2
); |
penguin2.jumping (
height =
1 ); |
penguin2
.turn(
LEFT
,
0.5 revolutions ); |
penguin2.walking (
x = 2
); |
penguin2
.turn(
LEFT
,
0.5 revolutions ); |
} | |
Listing 12. Program listing for Vector01.
Vector01's CodeCreated by: Dick BaldwinworldEvents
When the world starts |
Do: |
|
Methods
public void main ( ) {
|
|
//
Illustrates how to program in Alice using vectors. |
//
Moves a penguin in 3D space using both absolute |
//
and relative vectors. |
//
Initialize the penguin to the origin. |
world.setTheStage ( ); |
//
Move penguin away from origin with absolute move. |
world.vectorMoveAbs (
obj =
penguin ,
x = 3
, y =
2 ,
z = -1
,
duration = 1 ,
style = 1
); |
wait(
1
second ); |
//
Move back to origin with absolute move. |
world.vectorMoveAbs (
obj =
penguin ,
x = 0
, y =
0 ,
z = 0
,
duration = 0.25 ,
style = 3
); |
wait(
1
second ); |
//
Move away from origin again with absolute move. |
world.vectorMoveAbs (
obj =
penguin ,
x = -1
, y =
2 ,
z = 3
,
duration = 1 ,
style = 4
); |
wait(
1
second ); |
//
Move back to origin in two relative moves with a pause in
between. |
world.vectorMoveRel (
obj =
penguin ,
x = 0.75
, y =
-1 ,
z = -1
,
duration = 3 ,
style = 1
); |
wait(
1
second ); |
world.vectorMoveRel (
obj =
penguin ,
x = 0.25
, y =
-1 ,
z = -2
,
duration = 1 ,
style = 2
); |
} |
public void vectorMoveAbs ( Object obj, Number
x, Number y, Number z, Number duration,
Number style) {
Posistion theVector =
Vector3(
0, 0, 0 ) ; |
|
theVector
.set( value , ( getVector(
right = x
, up
= y ,
forward =
z ) ) );
duration =
0 seconds |
if ( (
style
==
1 ) ) { . . } |
|
obj
.moveTo(
world
);
position of = theVector
duration =
duration seconds
style =
BEGIN_AND_END_GENTLY |
} else { |
|
if ( (
style
==
2 ) ) { |
|
obj
.moveTo(
world
);
position of = theVector
duration =
duration seconds
style =
BEGIN_GENTLY_AND_END_ABRUPTLY |
} else { |
|
if ( (
style
==
3 ) ) { |
|
obj
.moveTo(
world
);
position of = theVector
duration =
duration seconds
style =
BEGIN_ABRUPTLY_AND_END_GENTLY |
} else { |
|
obj
.moveTo(
world
);
position of = theVector
duration =
duration seconds
style =
BEGIN_AND_END_ABRUPTLY |
} |
} |
} |
} |
public void setTheStage ( ) {
|
|
penguin
.setPointOfView(
world
);
duration = 0 seconds |
penguin
.turn(
LEFT
,
0.5 revolutions );
duration =
0 seconds |
magicianWand
.setPointOfView(
world
);
duration = 0 seconds |
magicianWand
.resize(
2
);
duration = 0 seconds |
camera
.setPointOfView(
world
);
duration = 0 seconds |
camera
.move(
BACKWARD
,
15 meters );
duration =
0 seconds |
camera
.move(
UP
,
.0001 meters ); |
} |
public void vectorMoveRel ( Object obj, Number
x, Number y, Number z, Number duration,
Number style) {
Posistion currentVector =
Vector3(
0, 0, 0 ) ; Number newX =
0
; Number newY =
0
; Number newZ =
0
; |
|
currentVector
.set( value , (
obj
.getPosition()
asSeenBy =
world ) );
duration =
0 seconds |
newX
.set( value , ( ( (
currentVector .getDistanceRight() ) +
x
) ) );
duration =
0 seconds |
newY
.set( value , ( ( (
currentVector .getDistanceUp() ) +
y
) ) );
duration =
0 seconds |
newZ
.set( value , ( ( (
currentVector .getDistanceForward() ) +
z
) ) );
duration =
0 seconds |
world.vectorMoveAbs (
obj = obj
, x =
newX ,
y = newY
, z =
newZ ,
duration =
duration ,
style =
style ); |
} | |
Listing 16. The program named Library01.
Library01's Code
Created by: Dick Baldwin
world
Events
When the world starts |
Do: |
|
Methods
public void main ( ) {
|
|
// Demonstrates use of Library methods, functions, and constants. |
// Chicken was manually placed at the origin of the world. |
// ----------------------------------------------------- |
// Print a constant that is stored in the Library object. |
print( ( Library.pi .toString() ) ); |
// Move chicken away from origin using absolute coordinates. |
Library.vectorMoveAbs ( obj = Chicken , x = -1 , y = 2 , z = 3 , duration = 1 , style = 4 ); |
// Move chicken back to origin in two incremental relative |
// moves with a turn in between. |
Library.vectorMoveRel ( obj = Chicken , x = 0.75 , y = -1 , z = -1 , duration = 3 , style = 1 ); |
// Call a Library function to execute the 90-degree turn. |
Chicken .turn( LEFT , ( Library.degreesToRev ( degrees = 90 ) ) ); |
Library.vectorMoveRel ( obj = Chicken , x = 0.25 , y = -1 , z = -2 , duration = 1 , style = 2 ); |
} |
Library
Methods
public void vectorMoveAbs ( Object obj, Number x, Number y, Number z, Number duration, Number style) {
Posistion theVector = Vector3( 0, 0, 0 ) ; |
|
theVector .set( value , ( getVector( right = x , up = y , forward = z ) ) ); duration = 0 seconds |
if ( ( style == 1 ) ) { . . } |
|
obj .moveTo( world ); position of = theVector duration = duration seconds style = BEGIN_AND_END_GENTLY |
} else { |
|
if ( ( style == 2 ) ) { |
|
obj .moveTo( world ); position of = theVector duration = duration seconds style = BEGIN_GENTLY_AND_END_ABRUPTLY |
} else { |
|
if ( ( style == 3 ) ) { |
|
obj .moveTo( world ); position of = theVector duration = duration seconds style = BEGIN_ABRUPTLY_AND_END_GENTLY |
} else { |
|
obj .moveTo( world ); position of = theVector duration = duration seconds style = BEGIN_AND_END_ABRUPTLY |
} |
} |
} | } |
public void vectorMoveRel ( Object obj, Number x, Number y, Number z, Number duration, Number style) {
Posistion currentVector = Vector3( 0, 0, 0 ) ; Number newX = 1 ; Number newY = 1 ; Number newZ = 1 ; |
|
currentVector .set( value , ( obj .getPosition() asSeenBy = world ) ); duration = 0 seconds |
newX .set( value , ( ( ( currentVector .getDistanceRight() ) + x ) ) ); duration = 0 seconds |
newY .set( value , ( ( ( currentVector .getDistanceUp() ) + y ) ) ); duration = 0 seconds |
newZ .set( value , ( ( ( currentVector .getDistanceForward() ) + z ) ) ); duration = 0 seconds |
Library.vectorMoveAbs ( obj = obj , x = newX , y = newY , z = newZ , duration = duration , style = style ); |
} |
Functions
java.lang.Number public Number degreesToRev ( Number degrees) {
|
|
Do Nothing |
return ( ( degrees / 360 ) ) ; |
} |
|
Copyright 2007, Richard G. Baldwin. Faculty and staff of public and private
non-profit educational institutions are granted a license to reproduce and to
use this material for purposes consistent with the teaching process. This
license does not extend to commercial ventures. Otherwise, reproduction in
whole or in part in any form or medium without express written permission from
Richard Baldwin is prohibited.
Richard Baldwin is a
college professor (at Austin Community College in Austin, TX) and private
consultant whose primary focus is a combination of Java, C#, and XML. In
addition to the many platform and/or language independent benefits of Java and
C# applications, he believes that a combination of Java, C#, and XML will become
the primary driving force in the delivery of structured information on the Web.
Richard has participated in numerous consulting projects and he
frequently provides onsite training at the high-tech companies located in and
around Austin, Texas. He is the author of Baldwin's Programming
Tutorials, which have gained a
worldwide following among experienced and aspiring programmers. He has also
published articles in JavaPro magazine.
In addition to his programming expertise, Richard has many years of
practical experience in Digital Signal Processing (DSP). His first job after he
earned his Bachelor's degree was doing DSP in the Seismic Research Department of
Texas Instruments. (TI is still a world leader in DSP.) In the following
years, he applied his programming and DSP expertise to other interesting areas
including sonar and underwater acoustics.
Richard holds an MSEE degree from Southern Methodist University and has
many years of experience in the application of computer technology to real-world
problems.
Baldwin@DickBaldwin.com
-end-