Tutorial: Cannonball using ActionScript 3.0. Open Flash and ...

anthropologistbarrenΛογισμικό & κατασκευή λογ/κού

4 Ιουλ 2012 (πριν από 4 χρόνια και 11 μήνες)

394 εμφανίσεις

Tutorial: Cannonball using ActionScript 3.0.

Open Flash and under Create New select Flash file (ActionScript 3.0).

Here is how you create a cannonball Flash application, specifically, a [very] basic
simulation of ballistic/projectile motion. This could be the start for a more elaborate
game. Here is the opening screen for the game.




The player can change the angle of the cannon and/or the speed of the ball leaving the
mouth of the cannon. The physics of the situation means that the program must first
calculate the horizontal and vertical components of the speed at the time of the firing of
the cannon. The ball is moved at periodic intervals. At each interval, the vertical speed is
modified just as it would be in the physical world when gravity acts on bodies in motion.
The next screen shot shows the end of a flight: the ball is partially hidden on the ground.


The next image shows the screen after the ball has hit the target and it has fallen apart.
The falling apart is itself an animation.



The requirements for this application are
• A way to produce animation, that is, to produce a sequence of pictures showing
the ball in different positions
• Detection of the ball hitting the target and reaching the ground
• Calculation of the horizontal and changing vertical velocities
• A way to allow the player to enter in new angle and speed
• A way to show the target falling apart.

There is a description of each step and then the implementation of this particular
application.

ActionScript 3.0 has a new class called Timer. This means that you (your code) can
create Timer objects. Timer objects can be used to set up timing events and handlers for
those events.

var mytimer:Timer = new Timer(50);

The var statement sets up mytimer as a Timer object, with the timing event occurring
every 50 milliseconds. This is pretty fast / often. It is possible to add a second parameter
to the Timer constructor to indicate how many events you want to happen. For
example,

var timer2: Timer: new Timer(2000, 5);

will set up timing intervals every 2 seconds (1000 milliseconds in a second) and do this 5
times. NOT having a second parameter means that the timing events will keep happening
until the code stops them. Actually, the var statement does not start the timing. This will
come later. The next statement to explain is the set up of the event handling:

mytimer.addEventListener(TimerEvent.TIMER, moveball);

This statement "says": when the TimerEvent called TIMER happens, invoke the
function moveball. This is similar to what is done for buttons.

In this application, the timing interval event statement will be in the fire function:

mytimer.start();

To stop timing (assuming it has been set up in the var statement to last indefinitely), use

mytime.stop();

The timing event will be stopped if the ball hits the target or the ground as determined by
code in the moveball function.

So now I have explained how to invoke a function every so often. The main task of the
event handler, designated as moveball in the code shown, will be to re-position the
ball. This is done by modifying the horizontal (x) and the vertical (y) positions. Very
briefly, in Flash, and many other computer applications, positions on the screen are
indicated by 2 numbers (coordinates). The horizontal position is from the left and often
designated as x and the vertical position is from the top and designated as y. This is NOT
the standard analytical geometry you may remember.

In Flash, the horizontal position of movie clip instances is designated using dot notation:
the name of the instance followed by a dot followed by x. Similarly, the vertical position
of movie clip instances is designated by the name of the instance followed by a dot
followed by a y. The code will re-position the ball by assigning values to ball.x and
ball.y.

Before going into the specifics of ActionScript 3.0, please appreciate the fact that the
animation is produced by generating snapshots in rapid succession. This is discrete
simulation of motion. The ball is here and then it is there. It does not move smoothly
from place to place. The collision detection is done at each interval of time. This means
that the ball may appear to penetrate a wall because the collision calculation is done a
quantum amount of time later. One way to see this is to experiment with the numbers.
You can make the ball jump out of the box.

Flash provides a method to detect when a point, indicated by an x,y pair, designates a
pixel that is occupied by material in a movie clip instance. Two if tests shown next
checks of the x and y values designating the registration point of the ball movie clip
instance overlaps any occupied pixel of the movie clip instance named target and then
checks for the ball hitting the ground by comparing the y coordinate of the ball and the
y coordinate of the ground.

if (target.hitTestPoint(ball.x,ball.y, true)) {
mytimer.stop();

ball.visible = false;
target.gotoAndPlay(2);
}
if (ball.y > ground.y) {
ball.y = ground.y;
mytimer.stop();
}

The true parameter specifies that the check is against actual material. If this parameter
was false or omitted, the test would be against the bounding rectangle of target.
Now, for my simple target, this does not make a difference, but you may want to make
your target a more complex shape.You can guess that the bounding rectangle test can be
done quicker than the actual material test. It could be that you could develop a game in
which you may be concerned with performance and may need to think about this.

Flash has 3 types of text fields: static, dynamic, and input. Static fields are used to label
parts of the screen. Once they are set during development time, they do not change.
Dynamic text fields can be set at development time and also by code. They were used in
the coin toss and in rock paper scissors. Input text fields can be set at development time
and by code AND by the player (user). These fields contain text, so the code must
convert the text to number data type before using as the speed in bouncing ball.

The cannonball application makes use of the following variables (in addition to
mytimer):

var sp:Number = parseInt(speed.text);
var xd:Number;
var yd1:Number;
var yd2:Number;
var g:Number=2;

The text field speed holds text! The parseInt function converts that text to an integer
and assigns that value to sp. A similar calculation will take place for the angle in the
fire function. The xd variable is for the horizontal component of the velocity. I gave it
this name thinking of the term displacement. The yd1 and yd2 variables hold the initial
and the final vertical velocities for each interval. This is how discrete as opposed to
continuous calculations are done for situations such as projectile motion. The g variable
holds the factor representing gravity. Don't take this number too seriously: I
experimented and it made the arcs look nice.

The application uses two functions: the fire function that starts the motion and the
moveball function invoked by the workings of mytimer.

Here is the physics and mathematics!

Recall from the introduction that the characteristic arc of projectile motion occurs
because the horizontal velocity remains the same but the vertical velocity changes. The
ball is shot from the cannon at an angle. Your code will determine the initial horizontal
and vertical components of the ball's motion using trigonometry.

speed
speed * cos(angle)
speed * si n(angle)

The variable xd is set to the horizontal component in the fire function. You need two
variables for the vertical component because it is changing due to the effects of gravity.
The variable g holds the value of gravity per 1/12th of a second. (This appears more
serious than it really is. The units could be whatever we want, but this value seems to
work out well.) The yd1 variable will hold the value at the start of the increment of time
and the yd2 will hold the modified value. Your code will average the two to determine
the distance moved during that unit of time.

Next, here is some mathematics (actually computer technology, too): How do you
measure angles? What units are used? For the computation done, we need the angle to be
in radians as opposed to degrees. (Haven't you ever wondered why there are 360 degrees
to a circle? The answer is that there is no reason. The radians method is an intrinsic
measure: there are 2 times pi radians in a circle.) The Flash program is a hybrid: some
commands expect angles in degrees and some expect radians. (Most computer languages
are either a hybrid like Flash or ‘expect’ radians in all situations.). To convert from
degrees to radians, the code multiplies the number by pi/180.

Here is the complete fire function.

function fire(ev:MouseEvent):void {
ball.visible = true;
var degrees = parseInt(angledeg.text);
cannon.rotation = -degrees;
var angler = degrees * Math.PI / 180;
var sp = parseInt(speed.text);
xd = Math.cos(angler)*sp;
yd2 = -Math.sin(angler)*sp;
target.gotoAndStop(1);
ball.x = cannon.x+ cannon.width;
ball.y = cannon.y-cannon.height;
mytimer.start();
}

The fire function is invoked based on connecting it to the firebtn using the statement:

firebtn.addEventListener(MouseEvent.CLICK, fire);

You have seen some of the moveball function already. Here is the whole thing:

function moveball(ev:TimerEvent):void {
ball.x += xd;
yd1=yd2;
yd2 = yd2+g;
ball.y +=.5*(yd1+yd2);

if (target.hitTestPoint(ball.x,ball.y, true)) {
mytimer.stop();

ball.visible = false;
target.gotoAndPlay(2);
}
if (ball.y > ground.y) {
ball.y = ground.y;
mytimer.stop();
}
}

This is the code for the application. Now you need to create the material. Before doing
so, name the default layer board and add two more layers: actions and interface. Layers
serve both to organize and to make sure some graphical material is displayed on top of
others. You also can get some graphics to lie on others because of the order in which they
were brought to the Stage, but using layers is more reliable.

Select the board layer. Using Insert/New Symbol, create in the Library: target, ball,
cannon and ground. Start simple and then make things more complex later. Here is a
screen shot for my (simple) cannon:



Notice that the cross indicating the registration point (aka origin) of the cannon is at the
left end. The rotation will be around this point, which is what I wanted.

Here is a screen shot showing the ground symbol. Again, note where the registration
point is.



Here is a screen shot for the ball. If you look at the code, if the registration point for the
ground is way above the actual ground, the ball will stop prematurely.


The test for the ball hitting the target compares the ball.x and ball.y point with the
material of the target, so the registration point is not as critical for the target. What I did
for the target is create several frames.

For the target movie clip symbol, start off with the first frame something simple, like
a rectangle.

Open up the Actions panel and put

stop();

in the first frame. Use Insert/Timeline/Keyframe to get a second keyframe. Erase part of
the rectangle. Open up the Actions panel for this frame and put in

stop();

Here is the first frame (after I created several frames):





Bring everything to the Stage and make sure each has a name. The code uses the same
names target, ball, cannon and ground.

Now select the interface layer and add a button. I used one from the Common Library. I
did not think it needed a label. You do need to give the button a name: firebtn. Use
the T tool to create 4 text fields: 2 static holding the words angle and speed and two
input. The input text fields should be named angledeg and speed. The screen shot
shows the angledeg selected:



You should write something in each of the two fields. I chose to type in 45 and 10. The
player can change these but it makes sense to put in values just in case the player clicks
on the button.

Click on the actions layer and put in the code. The code consists of the var statements
(do not forget mytimer), the two addEventListener statements, and the two
function definitions. Here is an outline (the inside of the functions are omitted. Go back
in the tutorial to see this.)

var mytimer:Timer = new Timer(100);
var sp:Number = parseInt(speed.text);
var xd:Number;
var yd1:Number;
var yd2:Number;
var g:Number=2;

mytimer.addEventListener(TimerEvent.TIMER, moveball);

function moveball(ev:TimerEvent):void {

}


firebtn.addEventListener(MouseEvent.CLICK, fire);

function fire(ev:MouseEvent):void {


}


The program is ready to test. When it is working, go back to target and expand the
animation to make it more dramatic. The screen shot shows a keyframe in the middle of
the cel animation (also called frame by frame animation) created.




The cannonball application is a good demonstration of using the facilities of Flash for
interactions, computed animation and cel animation all together.