Talking about Ball: Revamped game, and Flash.
March 14th update: part 5.3 released.
Now we will discuss about code optimization (the boring – but very important – part) and some visual tricks to make the game look better, and some more issues.
Code optimization
In tutorials 1 to 5.1, I always include the ball actionscript in the ball object. This is correct, but it might create confusion when you have to deal with a lot of objects.
You just can’t remember where did you put that piece of code. Imagine to pick up a game coded in this way after some weeks… would you really remember where did you put that collision script?
So, we will learn how to put all the code in a single frame.
Example 1: the ball
This is the same movie seen in the “No walls and speed limit” example covered in tutorial 5.1.
This time, as said, there is no actionscript in the objects, just the main script in frame 1
_root.attachMovie("hero", "hero_on_stage", 10000, {_x:200,_y:20});
power = 0.65;
yspeed = 0;
xspeed = 0;
wind = 0.00;
gravity = 0.1;
upconstant = 0.75;
friction = 0.99;
hero_on_stage.onEnterFrame = function() {
if (Key.isDown(Key.LEFT)) {
xspeed = xspeed-power;
}
if (Key.isDown(Key.RIGHT)) {
xspeed = xspeed+power;
}
if (Key.isDown(Key.UP)) {
yspeed = yspeed-power*upconstant;
}
if (Key.isDown(Key.DOWN)) {
yspeed = yspeed+power*upconstant;
}
xspeed = (xspeed+wind)*friction;
yspeed = yspeed+gravity;
if (xspeed>15) {
xspeed = 15;
}
if (xspeed<-15) {
xspeed = -15;
}
if (this.yspeed>15) {
this.yspeed = 15;
}
if (yspeed<-15) {
yspeed = -15;
}
this._y += yspeed;
this._x += xspeed;
if (this._x<0) {
this._x += 500;
}
if (this._y<0) {
this._y += 350;
}
if (this._y>350) {
this._y -= 350;
}
if (this._x>500) {
this._x -= 500;
}
this._rotation = this._rotation+xspeed;
};
Let’s see the script:
Line 1: I attach the movie “hero” (the ball) on the stage and call it as “hero_on_stage” (this will be the instance name). The 10000 is the depth of the movie. An object with a higher depth than another will appear “in front” of it. Just imagine that an object with an higher depth is on an higher layer. The {_x:200,_y:20}
means that the _x value is set to 200 and the _y to 20. Basically, this line acts like I took the hero from the library panel, instanced as “hero_on_stage” and position it to 200,20
Lines 2-8: Here I put the declarations previously included in the onClipEvent (load)
Line 9: The main loop previously called onClipEvent (enterFrame)
now is called in this way.
The other lines work in the same way as in the previous example. Just notice that all attributes like _x, _y, _rotation are defined using this._x, this._y, this._rotation…
Example 2: The scrolling
To optimize the scrolling example seen in tutorial 5.1 we are using the same technique: one single actionscript in the first frame.
_root.attachMovie("kira", "kira", 8000, {_x:234, _y:159});
_root.attachMovie("wall", "wall", 10000, {_x:240, _y:0, _width:5700, _height:5700});
yspeed = 0;
xspeed = 0;
wind = 0.00;
power = 0.65;
gravity = 0.1;
upconstant = 0.75;
friction = 0.99;
kira.onEnterFrame = function() {
if (Key.isDown(Key.LEFT)) {
xspeed = xspeed-power;
}
if (Key.isDown(Key.RIGHT)) {
xspeed = xspeed+power;
}
if (Key.isDown(Key.UP)) {
yspeed = yspeed-power*upconstant;
}
if (Key.isDown(Key.DOWN)) {
yspeed = yspeed+power*upconstant;
}
xspeed = (xspeed+wind)*friction;
yspeed = yspeed+gravity;
if (xspeed>0) {
this.kira.gotoAndStop(1);
} else {
this.kira.gotoAndStop(2);
}
_root.wall._y -= yspeed;
_root.wall._x -= xspeed;
if (_root.wall.hitTest(this._x, this._y, true)) {
xspeed = 0;
yspeed = 0;
_root.wall._x = 240;
_root.wall._y = 0;
}
};
The theory is the same of the previous example, just notice lines 25-29 that makes the player (kira) face left if she is going to the left, and right if she is going to the right. How can I did it? Simply inserting two frames in the kira object, one with kira facing left, one with kira facing right.
And that’s where the boring part ends.
Now…
Eye Candy 1: Trails
Even if you may think a flash game can be funny even with simple graphics, you should improve the visual appeal as much as you can. Let’s see some tricks.
We will have our ball leaving a trail when it moves. In a very simple way.
First, you need to create a new movieclip with a shape on it, then you have to create another new movieclip with the previously created movieclip in it.
This way:
(never mind if you should find it a bit unclear, you will find all source codes at the end of the tutorial)
1: The shape itself: a red circle with a blue border
2: The movieclip where we draw the shape
3: Another movieclip, with the movieclip seen at 2 inside of it
4: Look how I instanced the movieclips
And now the actionscript:
_root.attachMovie("hero", "hero_on_stage", 10000,{_x:200,_y:20});
_root.attachMovie("trail_sprite", "trail_mc", 8000);
trailbitmap = new flash.display.BitmapData(500, 350, true,0x000000);
_root.createEmptyMovieClip("trail", 1);
trail.attachBitmap(trailbitmap, 0);
trail_mc._visible = false;
power = 0.65;
yspeed = 0;
xspeed = 0;
wind = 0.00;
gravity = 0.1;
upconstant = 0.75;
friction = 0.99;
hero_on_stage.onEnterFrame = function() {
if (Key.isDown(Key.LEFT)) {
xspeed = xspeed-power;
}
if (Key.isDown(Key.RIGHT)) {
xspeed = xspeed+power;
}
if (Key.isDown(Key.UP)) {
yspeed = yspeed-power*upconstant;
}
if (Key.isDown(Key.DOWN)) {
yspeed = yspeed+power*upconstant;
}
xspeed = (xspeed+wind)*friction;
yspeed = yspeed+gravity;
if (xspeed>15) {
xspeed = 15;
}
if (xspeed<-15) {
xspeed = -15;
}
if (yspeed>15) {
yspeed = 15;
}
if (yspeed<-15) {
yspeed = -15;
}
this._y += yspeed;
this._x += xspeed;
if (this._x<0) {
this._x += 500;
}
if (this._y<0) {
this._y += 350;
}
if (this._y>350) {
this._y -= 350;
}
if (this._x>500) {
this._x -= 500;
}
this._rotation = this._rotation+xspeed;
_root.trail_mc.trail_sprite._x = this._x;
_root.trail_mc.trail_sprite._y = this._y;
_root.trailbitmap.draw(_root.trail_mc);
trail_rectangle = new flash.geom.Rectangle(0, 0, 500, 350);
trail_blur = new flash.filters.BlurFilter(2, 2, 3);
_root.trailbitmap.applyFilter(_root.trailbitmap, trail_rectangle, new Point(0, 0), trail_blur);
};
Line 2: I attach the trail_sprite object (the one containing the movieclip with the shape) on a depth lesser than the hero’s one (it will be behind the ball)
Line 3: I create a new BitmapData object called trailbitmap.
What is a BitmapData?
From macromedia livedocs: The BitmapData class lets you create arbitrarily sized transparent or opaque bitmap images and manipulate them in various ways at runtime. This class lets you separate bitmap rendering operations from the Flash Player internal display updating routines. By manipulating a BitmapData object directly, you can create very complex images without incurring the per-frame overhead of constantly redrawing the content from vector data.
Basically, the bitmap data is a “field” where we can apply various visual effects.
It is declared in this way:
BitmapData(width:Number, height:Number, [transparent:Boolean], [fillColor:Number])
So my BitmapData has a width of 500 (same of the movie), height of 350 (same of the movie), and it’s transparent. The fourth parameter, the fillcolor, is not so important since it’s transparent.
Line 4: I create an empty movieclip (a new one) called “trail” at depth 1 (behind the other ones)
Line 5: In this trail movieclip I attach the BitmapData previously created
Line 6: I set the trail_mc clip to invisible. I mean I do not want to see the trail_mc clip, I only want to see its effects on the BitmapData
The other lines are the same seen in previous examples until…
Lines 57-58: I set the _x and _y of the trail_sprite movieclip inside the trail_mc movieclip to the same values of hero’s _x and _y. I mean that I am moving the shape movieclip inside the trail_mc movieclip that is the one containing the movieclip with the shape. Don’t worry if you think you can’t understand it… we will discuss about it in other tutorials.
Line 59: I draw the trail_mc movieclip (with its inner movieclip updated) inside the BitmapData object
Line 60: I define a rectangle starting at (0,0) with width = 500 and height = 350. This is the rectangle where we are going to create the special effect.
Line 61: Defining a blur filter. This filter has 3 parameters: The amount of horizontal blur (set to 2, I noticed it works better with multiples of 2), the amount of vertical blur (same thing) and the number of times to perform the blur (when it’is set to 1, the result is a softly unfocused look, when it’s set to 3, it approximates a Gaussian blur filter)
Line 62: The effect is applyed. It’s easier than it may seem, I am considering about creating a complete tutorial about it anyway.
Eye Candy 2: Explosions
If in the previous example we had a trail, in this one we will consider an explosion.
Let’s apply the same theory to the tunnel game:
_root.attachMovie("kira", "kira", 8000, {_x:234, _y:159});
_root.attachMovie("wall", "wall", 10000, {_x:240, _y:0, _width:5700, _height:5700});
_root.attachMovie("trail_sprite", "trail_mc", 4000);
trailbitmap = new flash.display.BitmapData(500, 350, true, 0x000000);
_root.createEmptyMovieClip("trail", 1);
trail.attachBitmap(trailbitmap, 0);
trail_mc._visible = false;
yspeed = 0;
xspeed = 0;
wind = 0.00;
power = 0.65;
gravity = 0.1;
upconstant = 0.75;
friction = 0.99;
die = 0;
kira.onEnterFrame = function() {
if (Key.isDown(Key.LEFT)) {
xspeed = xspeed-power;
}
if (Key.isDown(Key.RIGHT)) {
xspeed = xspeed+power;
}
if (Key.isDown(Key.UP)) {
yspeed = yspeed-power*upconstant;
}
if (Key.isDown(Key.DOWN)) {
yspeed = yspeed+power*upconstant;
}
if (!die) {
xspeed = (xspeed+wind)*friction;
yspeed = yspeed+gravity;
}
if (xspeed>0) {
this.kira.gotoAndStop(1);
} else {
this.kira.gotoAndStop(2);
}
_root.wall._y -= yspeed;
_root.wall._x -= xspeed;
if (_root.wall.hitTest(this._x, this._y, true)) {
if (die<100) {
xspeed = 0;
yspeed = 0;
_root.trail_mc.explosion._x = this._x;
_root.trail_mc.explosion._y = this._y;
_root.trail_mc.explosion.gotoAndPlay(die*2+1);
this._alpha = 0;
_root.trailbitmap.draw(_root.trail_mc);
trail_rectangle = new flash.geom.Rectangle(0, 0, 500, 350);
trail_blur = new flash.filters.BlurFilter(2, 2, 3);
_root.trailbitmap.applyFilter(_root.trailbitmap, trail_rectangle, new Point(0, 0), trail_blur);
die+=0.5;
} else {
die = 0;
xspeed = 0;
yspeed = 0;
this._alpha = 100;
_root.wall._x = 240;
_root.wall._y = 0;
}
}
};
Line 15: A new variable called die. It will states if the player... died...
Lines 29-32: xspeed and yspeed are updated only if the player didn't die.
Line 41: Things to do if the player hit the wall and die < 100
Line 46: Tell the explosion movieclip (the "trail_sprite" in the previous example) to goto and play frame die*2+1
. Now we have no more a shape but an explosion movieclip to be blurred
Line 47: Set the player alpha to 0 (to "remove" the bubble from stage)
Line 52: Increment die by 0.5. In this way we will remain inside this loop until die < 100... 200 frames or so. Lines 52-60: Things to do if die >=100 (the player died some frames ago): set the _alpha to 100 (return visible), set die to 0 again and reset wall _x and _y and speed.
Now that some visual effects are done, it's time to...
Adding sound
In this example we want to add a sound effect to our explosion.
First, you must import the sound in the library and instance it. When you have a linkage, the actionscript is very simple:
_root.attachMovie("kira", "kira", 8000, {_x:234, _y:159});
_root.attachMovie("wall", "wall", 10000, {_x:240, _y:0, _width:5700, _height:5700});
_root.attachMovie("trail_sprite", "trail_mc", 4000);
trailbitmap = new flash.display.BitmapData(500, 350, true, 0x000000);
_root.createEmptyMovieClip("trail", 1);
trail.attachBitmap(trailbitmap, 0);
trail_mc._visible = false;
exp_sound = new Sound();
exp_sound.attachSound("boom");
yspeed = 0;
xspeed = 0;
wind = 0.00;
power = 0.65;
gravity = 0.1;
upconstant = 0.75;
friction = 0.99;
die = 0;
kira.onEnterFrame = function() {
if (Key.isDown(Key.LEFT)) {
xspeed = xspeed-power;
}
if (Key.isDown(Key.RIGHT)) {
xspeed = xspeed+power;
}
if (Key.isDown(Key.UP)) {
yspeed = yspeed-power*upconstant;
}
if (Key.isDown(Key.DOWN)) {
yspeed = yspeed+power*upconstant;
}
if (!die) {
xspeed = (xspeed+wind)*friction;
yspeed = yspeed+gravity;
}
if (xspeed>0) {
this.kira.gotoAndStop(1);
} else {
this.kira.gotoAndStop(2);
}
_root.wall._y -= yspeed;
_root.wall._x -= xspeed;
if (_root.wall.hitTest(this._x, this._y, true)) {
if (!die) {
exp_sound.start();
}
if (die<100) {
xspeed = 0;
yspeed = 0;
_root.trail_mc.explosion._x = this._x;
_root.trail_mc.explosion._y = this._y;
_root.trail_mc.explosion.gotoAndPlay(die*2+1);
this._alpha = 0;
_root.trailbitmap.draw(_root.trail_mc);
trail_rectangle = new flash.geom.Rectangle(0, 0, 500, 350);
trail_blur = new flash.filters.BlurFilter(2, 2, 3);
_root.trailbitmap.applyFilter(_root.trailbitmap, trail_rectangle, new Point(0, 0), trail_blur);
die += 0.5;
} else {
die = 0;
xspeed = 0;
yspeed = 0;
this._alpha = 100;
_root.wall._x = 240;
_root.wall._y = 0;
}
}
};
Line 8: Defines a variable (exp_sound) for a new sound
Line 9: Attaching the sound. In this case, boom is the linkage name of the sound
Lines 43-45: When the collision is checked and die is equal to 0 (it's the first frame where we are checking the collision), play the sound.
Now download the source code of all examples, give me feedback and proceed to part 5.3
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.