See you again friends :-)
Well, this time we’re going to create a little memory game... it can be a stand-alone game but also, especially thanks to his small dimensions ( < 2 kb!) it can also be an "interactive preloader" for bigger movies... and I think that this is his ideal use :-)
But stop talk... let’s go!
1 – The concept
First of all, we must establish what the player can or can’t do during the game, and how the game will interact with the user and his input. The general lines, and the general concept of this game, is simple
1) user click on a card
2) the card turn and show her symbol
3) user click on the 2nd card
4) the 2nd card show her symbol
Here the game must execute a control... if the cards are equals, the score must increase and the two cards must disappear, otherwise the score doesn’t increase and the cards turn again on their "backface". We must also put some "limitations" to the player... more specifically
- Once selected the 1st card, user can’t click again on it before next turn
- User can’t click on the 2nd card before the 1st has turned
- User must wait the control between 2 cards, he can’t discover more then 2 cards for turn
Those limitations are useful especially for avoid some "mistakes" or problems during the game... we’ll see more in detail during this tutorial.
Well, now we’ve established the general points, inputs and limitations of the game... it’s time to create it, come on!
2 – Setting up, what we need
All we need is love... ops... no, not for this game... now we need
- Flash
- A graphic program (for the "cards contents" creation)
Before start coding (yes, just coding... I haven’t yet told you that the game will be entirely in actionscript ? ;-)), we must create the cards images. In this example, I used 80x90 cards, with 70x80 images... you can make cards at every size you want... in fact the game will be entirely dynamic! It will automatic establish how many cards can be placed on the playground following the size we’ll specify in the code... it’s nice, isn’t it? :-)
Now place the images in "imgs" folder, at the same level of the .fla... the image can explain better the directory and file structure.

Well, in the "imgs" folder we have all the necessary images... now we can pass to flash... we’ve a lot to do with actionscript this time, are you ready? ;-)
3 – Flash and actionscript, the hearth of our game
As I said before, this game will be entirely made in actionscript... all the game will be composed of 130-140 lines of code. In fact, it’s a simple game with a simple structure and interaction... I’ll try to explain why we must put some limitations in this game (seen at point 1), also if the code tells almost all :-)
Let’s start!
First of all, set the canvas size at 550x400, and the framerate at 25 fps.
NOTE: When you test this movie, DON’T show the bandwidth profiler, because when it’s shown in the "test movie" window, the Stage.height action return wrong values (eg 300 instead 400, in our situation).
On the first (and only!) frame, start with this code
// Gamefield settings // EDIT HERE card_w = 70; card_h = 90; card_wdistance = 10; card_hdistance = 10; // STOP EDIT card_dw = card_w + card_wdistance; card_dh = card_h + card_hdistance; row_card = Math.floor((Stage.width - card_wdistance) / card_dw); col_card = Math.floor((Stage.height - card_hdistance) / card_dh); n_card = col_card * row_card % 2 == 0 ? col_card * row_card : (col_card - 1) * row_card; imgs_n = n_card / 2;
What it do? Well, see in detail.
With the "editable lines" we establish
Card width (card_w)
Card height (card_h)
Distance between cols (card_wdistance)
Distance between rows (card_hdistance)
Other lines simply calculate the number of cards that can be placed on the gamefield... with the calculation that you can see in the code
N_card and imgs_n are the final count of cards on the gamefield and the number of images that we need (obviously images are n_card/2 before we must have couple of cards with the same image ;-))
Now, the code that will generate the gamefield... it’s not too hard to comprise, also if it has some points that will require some explanations.
Here ALL the code for the gamefield generation... and also a little more
// CENTER THE IMAGE IN THE CARD
centerImage = function(target){
target.front._visible = false
target.front.container._x -= target.front.container._width / 2
target.front.container._y -= target.front.container._height / 2
}
// ---------------------------------------------
empty = new Array();
function createCard(n, w, h) {
empty.push(n);
this.createEmptyMovieClip('card' + n, n);
with (this['card' + n]) {
lineStyle(0, 0x000000, 100);
beginFill(0x000099, 60);
moveTo(w / 2, 0);
lineTo(w / 2, h / 2);
lineTo(-w / 2, h / 2);
lineTo(-w / 2, -h / 2);
lineTo(w / 2, -h / 2);
lineTo(w / 2, 0);
endFill();
createEmptyMovieClip('front', 1);
front.createEmptyMovieClip('container', 2);
front.createEmptyMovieClip('backgr', 1);
with (front.backgr) {
moveTo(-w / 2, -h / 2);
lineStyle(0, 0x000000, 0);
beginFill(0x333333, 100);
lineTo(w / 2, -h / 2);
lineTo(w / 2, h / 2);
lineTo(-w / 2, h / 2);
lineTo(-w / 2, -h / 2);
endFill();
}
front.onEnterFrame = function() {
tot = this.container.getBytesTotal();
car = this.container.getBytesLoaded();
if (tot == car && tot > 200) {
this._parent._parent.centerImage(this._parent);
delete this.onEnterFrame;
}
};
}
}
selcards = [];
function placeImages() {
act_img = 0;
for (img = 0; img < imgs_n; img++) {
for (k = 0; k < 2; k++) {
act = Math.round(Math.random() * (empty.length - 1));
this['card' + empty[act]].img_id = act_img;
this['card' + empty[act]].front.container.loadMovie("imgs/img" + act_img + ".jpg");
this['card' + empty[act]].onRelease = function() {
this.rotateCard(0, 0);
this.enabled = false;
MovieClip.prototype.enabled = false;
};
empty.splice(act, 1);
}
act_img++;
}
}
MovieClip.prototype.rotateCard = function(v, i) {
this.operazione = v == 0 ? +0.1 : -0.1;
this.incremento = i == 0 ? 100 : 10;
selcards.push(this._name);
this.onEnterFrame = function() {
this.incremento += this.operazione;
this._xscale = (100 * Math.sin(this.incremento));
if (this._xscale > 0) {
this.front._visible = v == 1 ? false : true;
}
if (this._xscale >= 99) {
delete this.onEnterFrame;
selcards.length < 2 ? MovieClip.prototype.enabled = true : null;
v == 0 && selcards.length > 1 ? checkCards() : null;
}
};
};
function generateGameField(cards, distance, hdistance) {
Ypos = card_h / 2 + hdistance;
Xpos = card_w / 2 + distance;
for (c = 0; c < cards; c++) {
createCard(c, card_w, card_h);
if (c % row_card == 0 && c > 0) {
Ypos += this['card' + c]._height + hdistance;
Xpos = card_w / 2 + distance;
}
this['card' + c]._x = Xpos;
this['card' + c]._y = Ypos;
Xpos += this['card' + c]._width + distance;
}
placeImages();
}
generateGameField(n_card, card_wdistance, card_hdistance);
Ok ok don’t escape... now we’ll see it in smart pieces
First of all, the generateGameField() function also if it’s the last function in the code, it’s the "main function", in fact is the only that we call directly... here the same code with some comment for explain better...
function generateGameField(cards, distance, hdistance) {
Ypos = card_h / 2 + hdistance;
Xpos = card_w / 2 + distance;
for (c = 0; c < cards; c++) {
createCard(c, card_w, card_h);
if (c % row_card == 0 && c > 0) {
Ypos += this['card' + c]._height + hdistance;
Xpos = card_w / 2 + distance;
}
this['card' + c]._x = Xpos;
this['card' + c]._y = Ypos;
Xpos += this['card' + c]._width + distance;
}
placeImages();
}
It establish the X and Y position of the first card. Then, using a for() loop, it create (calling the createCard() function) all the cards, and place it on the gamefield... then it call the placeImages() function. Not hard, really?
Now the real core of the gamefield generation... createCard() and placeImages() functions.
function createCard(n, w, h) {
empty.push(n);
this.createEmptyMovieClip('card' + n, n);
with (this['card' + n]) {
lineStyle(0, 0x000000, 100);
beginFill(0x000099, 60);
moveTo(w / 2, 0);
lineTo(w / 2, h / 2);
lineTo(-w / 2, h / 2);
lineTo(-w / 2, -h / 2);
lineTo(w / 2, -h / 2);
lineTo(w / 2, 0);
endFill();
createEmptyMovieClip('front', 1);
front.createEmptyMovieClip('container', 2);
front.createEmptyMovieClip('backgr', 1);
with (front.backgr) {
moveTo(-w / 2, -h / 2);
lineStyle(0, 0x000000, 0);
beginFill(0x333333, 100);
lineTo(w / 2, -h / 2);
lineTo(w / 2, h / 2);
lineTo(-w / 2, h / 2);
lineTo(-w / 2, -h / 2);
endFill();
}
}
}
Excluding the first line ( empty.push(n) ), that simply add at the "empty" array a number, that will be useful for the images placing (see it later), this function draw a card... We can’t use 4 simple lineTo(), because in that mode the movieclip center will be at the corner high left... while we want the center... at the center! (it’s necessary for the right card rotation). Than we use a more complex structure. Every card is structured as
- card
- front
- background
- container
Card is the movieclip the user will click and see at the start of the game... the "backface", we can call. While front has background (the "frontface" background) and container (where we’ll load the image). At the end, this function is simple... it create the backface and the frontface of every card... I think it isn’t useful explain every line, because they’re simply lineTo()... you can imagine what they do :-)
As we seen, the generateGameField() function calls the placeImages() function once the cards are created... as the name says, this function put the images into cards....we use the empty array (into we inserted data in the createCard() function, as said before) for know how cards are yet "empty".
The "mechanism" is simple. We take a number (eg 1), and put the image in "imgs/img"+n+".jpg" (eg imgs/img1.jpg) into two cards... and remove that cards from the "empty" array.
function placeImages() {
act_img = 0;
for (img = 0; img < imgs_n; img++) {
for (k = 0; k < 2; k++) {
act = Math.round(Math.random() * (empty.length - 1));
this['card' + empty[act]].img_id = act_img;
this['card' + empty[act]].front.container.loadMovie("imgs/img" + act_img + ".jpg");
this['card' + empty[act]].front.onEnterFrame = function() {
tot = this.container.getBytesTotal();
car = this.container.getBytesLoaded();
if (tot == car && tot > 200) {
this._parent._parent.centerImage(this._parent);
delete this.onEnterFrame;
}
};
this['card' + empty[act]].onRelease = function() {
this.rotateCard(0, 0);
this.enabled = false;
MovieClip.prototype.enabled = false;
};
empty.splice(act, 1);
}
act_img++;
}
}
Act_img establish what image load... we start from img0.jpg, and go on since img is < imgs_n (that, as saw, it’s the number of images we need).
The loop for(k=0;k<2;k++) is useful for full TWO cards...it select a random card from that avaibles (empty.length tell us how many cards are avaible yet), than assign img_id at the card, and load the image into cardN.front.container.
The enterFrame() tell us when the image is loaded, then we can center the image in the card (see centerImage() function)
The onRelease() function is what the card will do once clicked by the user... and what it do? It call another function... rotateCard, then disable all the movieclips on the scene (in fact we said that once a card is clicked, the user must wait that it’s turned on her frontface before he can click the 2nd card). Well... see the rotateCard function...
MovieClip.prototype.rotateCard = function(v, i) {
this.operazione = v == 0 ? +0.1 : -0.1;
this.incremento = i == 0 ? 100 : 10;
selcards.push(this._name);
this.onEnterFrame = function() {
this.incremento += this.operazione;
this._xscale = (100 * Math.sin(this.incremento));
if (this._xscale > 0) {
this.front._visible = v == 1 ? false : true;
}
if (this._xscale >= 99) {
delete this.onEnterFrame;
selcards.length < 2 ? MovieClip.prototype.enabled = true : null;
v == 0 && selcards.length > 1 ? checkCards() : null;
}
};
};
How to explain it... hmm... simpy tell that when we pass 0,0 at the function, the image turn on her frontface, while we pass 1,1 she turn on her backface (we’ll pass 1,1 if the 2 cards selected by the user won’t be equals). At the end of the turning, all movieclips on the stage are enabled (then the user can select other cards).
Last (but absolutely not least!) function is checkCards(), that we call when selected.length > 1 (it simply means that the checkCards() function is called when the user has selected the 2 cards).
NOTE : this function wasn’t in the "big amount" of code before, you must paste it after the generateGameField() function :-)
score = 0;
this.createTextField('punti', -1, 500, 380, 0, 0);
punti.autoSize = true;
punti.text = 0;
function checkCards() {
if (this[selcards[0]].img_id == this[selcards[1]].img_id) {
score++;
punti.text = score;
eval(selcards[0])._visible = eval(selcards[1])._visible = false;
} else {
eval(selcards[0]).rotateCard(1, 1);
eval(selcards[1]).rotateCard(1, 1);
eval(selcards[0]).enabled = eval(selcards[1]).enabled = true;
}
selcards = [];
MovieClip.prototype.enabled = true;
}
It’s very simple... it check if the img_id of the 2 selected cards are equal. If true, it means that two cards contain the same image, then they can became invisible and the score must increase. Otherwise, the cards are different, then they must turn on their backface (in fact we call rotateCard with 1,1) and come back enabled.
In both situations, the array "selcards" (that must always contains only the names of the 2 selected card) is re-setted and all movieclips are enabled.
"punti" is a text field (created using createTextField()) that show the user score.
Well... we created a simple small game... .I haven’t inserted a "game over "events or similar, because I think that this game, for his weight less than 2 kb, is useful as "interactive preloader"... also because if we’d like to create a "standalone" memory we’d use a more beautiful graphics and other elements that will make the movie less light but more attractive
I hope you liked this small tutorial... and I also hope you understood the explanation, even if the code wasn’t too complex
See you next time... byez ;-)
[edit: Changed to UTF-8 and reformatted - pioSko]
This post has been edited by pioSko: 07 December 2006 - 02:44 AM





MultiQuote




|