You are on page 1of 9

Como Hacer un Juego de Memoria con FLASH MX 2004

tomado de http://www.BleachEatingFreaks.com/flash/games/memory/
(traducido por dante becerra lagos, softenglish@gmail.com)

Paso 1: ¿Como debemos hacer el juego?

La mejor forma para que nosotros podemos hacer un juego de memoria es establecer el
método aquí. En la vida real ¿Cómo ejecutaríamos un juego de memoria? Pensaremos en
cada carta como una carta de naipe. Queremos modelar nuestras imágenes y hacerlo fácil para
añadir más en otro momento, ¿Correcto? Por lo tanto digamos que las cartas constituyen un
mazo. Esto es lo que recomiendo para empezar el juego.

1) Barajar el mazo.
2) Tomar X cartas del mazo (uno real tendría 52, y resultaría en un juego muy largo. Por
esta razón solo usaremos X cantidad de cartas del total. Esto nos facilitará hacer
niveles en el futuro, aumentando X en forma dinámica)
3) Fotocopiar las cartas extraídas de modo que tengamos un duplicado perfecto) (2 de
cada carta)
4) Baraja los duplicados ( no podemos tenerlos en el mismo orden que nuestro primer
mazo)
5) Por último, reparte las cartas de ambos mazos al mismo tiempo.
¡El Monstruo Lógico! La lógica nos dice que idealmente tu querrías entregarlas al azar porque
de otra forma alguien se puede dar cuenta que si se entregaran de los mazos al mismo tiempo,
no hay forma que N pueda parear N+2K, de esta forma reduciendo las posibilidades a la
MITAD! Pero nos gustaría pensar que nadie se dará cuenta sin saber como fue programado.
No es como si ellos vieran verdaderamente entregar las cartas desde los dos mazos.

En consecuencia ¿Suena como una forma razonable sacar las cartas y entregarlas? ¿Como
podemos hacerlo mas divertido?

Ahora cualquiera puede finalmente descubrir todas las cartas, pero es nuestro trabajo hacer
divertido el juego. En ese caso, registraremos el número de intentos y el tiempo que transcurre
durante el juego. Esto nos permitirá tener un sistema de puntaje, y entonces el juego se hace
mas competidito e interactivo.

Paso 2: Pensemos en el código

Bien, ahora ¿Como podemos hacer todo esto en el computador? Bien usemos un arreglo para
representar el mazo. Tendremos N cantidad de imágenes, y queremos tomar X de ellas y
usarlas como cartas. Esencialmente cada valor en el arreglo será mostrado como su numero
mas 1, de esta forma el índice 0 (cero) del arreglo será la imagen 1, y así sucesivamente.

No te olvides cuando leas el código que “//” y /* algo aquí*/ son comentarios para los lectores,
no el computador. Podemos crear un mazo de cartas con una función createDeck(). Esta
función es muy simple.

/*
* Función createDeck -
* crea un arreglo que sirva como un modelo de cartas de distinto tipo
*/
function createDeck() {
// crea las cartas
startDeck = new Array();
// cada carta = su posicion de arreglo + 1
for (x=0;x<picCount;x++) {
startDeck[x] = x + 1;
}
return startDeck;
}
La función anterior crea el modelo de mazo antes mencionado. Simplemente necesita una
variable global llamada picCount la cual contendrá la cantidad de imágenes que tendremos en
total. Cuando tú añades una imagen, asegúrate de ajustar picCount en forma
correspondiente. Ahora que tenemos un mazo necesitamos una forma de barajarlo.

¿Como lo hacemos? Hemos establecido el mazo como un arreglo. Una opción es elegir dos
elementos en forma aleatoria e intercambiarlos. Pero entonces ¿Cuantas veces tenemos que
hacer esto antes de que estemos seguros de que el mazo esta apropiadamente barajado? Al
menos el doble del tamaño del mazo. ¿Correcto? Tengo una idea.

Tomamos una carta en forma aleatoria y la ponemos en una nueva pila. Aleatoreamente
tomamos una carta en forma seguida hasta que la nueva pila esté llena y la vieja haya
desaparecido. Nuestra nueva pila será un conjunto aleatorio de nuestro mazo original de
cartas.

El monstruo lógico! La lógica nos dice que sacar una carta de un mazo es fácil, la pila se cae
instantáneamente y la ranura faltante se borra, pero si tu tienes un mazo en el computador, de
5 billones de cartas y tu sacas la mitad, entonces 2.5 billones de cartas tienen que moverse
hacia abajo de una vez. En cada ocasión consumiendo mucho tiempo del computador.

El monstruo lógico tiene razón. Las listas son mas difíciles de manejar que en la vida real.
Cuando se saca una carta de un mazo, el mazo completo se queda ahí, y se le debe DECIR a
cada una de las otras cartas que se mueva hacia abajo. Esto esta compuesto por la dificultad
que hemos elegido usar un arreglo, y que ya hemos designado dos mazos completos al
espacio del computador. Se chuparía para mover 3, 4 ,5 o incluso X mazos de espacio solo
para clasificar 1 mazo.

La forma en que yo lo manejaría, no es realmente la mejor forma en el mundo, pero funciona y


es rápida. Básicamente funciona de esta forma:

Elegimos una carta del mazo, la copiamos en nuestro nuevo lugar. Si ya esta en el mazo
nuevo, la desechamos, si es nueva la dejamos. Finalmente, seleccionaremos aleatoreamente
todas las cartas y las copiamos en nuestro nuevo mazo. Cuando esto ha sido realizado,
desechamos el mazo antiguo y hemos barajado.

No necesitamos barajar frecuentemente, de lo contrario se necesitaría un algoritmo mas


avanzado. A continuación puedes ver el código para barajar.

/* función barajar – cuando se da un mazo de N cartas (arreglo) la acción de barajar retornara


un mazo de N cartas con los mismos elementos del mazo dado en orden aleatorio.
*/
function shuffle(deckOld) {
// creación del mazo que retornaremos
deck = new Array();
// llenamos el arreglo de cartas con imagines únicas
do {
//*** NO USARSE EN PAGINAS WEB ***
newRandom = Math.floor(deckOld.length*Math.random());
deck.push(deckOld[newRandom]);

// copia el arreglo
scanData = new Array();
scanData = scanData.concat(deck);

// si el ultimo elemento es un duplicado, entonces quitar.


if (scanData.sort(Array.UNIQUESORT) == 0) {
deck.pop();
}
} while (deck.length<deckOld.length);

// retornar el arreglo finalizado


return deck;
}

Usamos la estructura de datos de pila la cual es muy común en computación. Flash tiene
funciones incorporadas para una pila, lo cual hace el proceso completo más rápido. En efecto,
cuando usas una función Flash MX en vez de una escrita a medida, la ejecución del programa
es usualmente más rápida. Esto tiene que ver con el nivel en que se ejecuta el código.
Actionscript se ejecuta como un script, en cambio las funciones innatas de Flash residen en
ejecutables y DLL’s.

Por lo tanto ahora podemos crear un mazo y barajarlo. ¿Y ahora como vamos a solucionar el
problema de manipular las cartas?

Bien, hagamos una función que manipule. Le decimos cuantas cartas manipular, y le daremos
un mazo vacío. La función manipuladora barajará el mazo, tomará P cartas de la parte superior,
las copiará y las barajará. Y luego las entregamos. Entregar las cartas será simplemente hacer
una copia de una carta, poner sus posiciones X e Y y ya que todas las cartas no son reales,
tenemos que decirle a la carta que imagen mostrar. Aquí esta nuestra función de manipulación
terminada.

/*
* Función manipulación (deal - toma las cartas y las entrega en la
* pantalla. De un número de imágenes único y uPic*2 cartas son
* entregadas en la pantalla.
*
* Parámetros:
* uPic = cantidad de imágenes únicas a manipular
* mazo de cartas desde donde manipularlas (todas las imágenes)
*/
function deal(uPic, cardDeck) {
moveY = 1;
moveX = 1;
// ir a la próxima hilera después de que rowBreak cartas
// son manipuladas
rowBreak = 8;

//inicializa los mazos


deck1 = new Array();
deck2 = new Array();
// barajar el mazo completo
deck1 = shuffle(cardDeck);
// tomar solo el numero de cartas que se necesita
deck1 = deck1.slice(0,uPic);
// mazo2(deck2) son las cartas barajadas del mazo1 (deck1)
deck2 = shuffle(deck1);

// establecer dos veces las únicas para la duplicación.


for (icount=0; icount<(2*uPic); icount++) {
//*** no usarse en paginas web ***
curName = "pc"+icount;
moveX = (icount%rowBreak)+1;
duplicateMovieClip(_root.pCard, curName, icount);
// se necesita eval ya que curName es solo una cadena
// y no un puntero objeto
with (eval(curName)) {
// posicion de la carta
_x = (moveX-1)*110+15;
_y = 150*moveY-125;
// ahora toma una imagen para la carta
// curPicNum es del mazo1 si es impar y del mazo2 si es par
curPicNum = (icount % 2 == 0) ? (deck2.pop()) : (deck1.pop());
theCard.cardFace.gotoAndStop(curPicNum);
}
if ((icount+1)%rowBreak == 0) {
moveY++;
}
}
}
ahora volvamos a examinar esta función:

rowBreak es una variable que nos dice cuantas cartas por hileras (rows). Podemos modificar
esto para subdividir dinámicamente en filas a cualquier ritmo. La variable deck1 esta llena con
la versión barajada de mazo trasladado. Nosotros luego sacamos una porción de las cartas y
barajamos el resto. Esto se hace con la función slice. Básicamente cortamos la parte superior y
la volvemos a poner en el mazo, las otras caen al suelo.

deck1=deck1.slice(0,uPic);

el mazo2 (deck2) esta luego compuesto por una versión barajada del mazo1 y tenemos
nuestras cartas listas para empezar.

Nosotros creamos la carta, luego establecemos 3 cosas. Le decimos que posición X y posición
Y y que imagen mostrar.
-xpos esta controlada principalmente por el numero envolvente moveX, en la medida que las
cartas se incrementan hasta rowBreak.
-ypos esta controlada por moveY, el cual seguirá avanzando en cada rowBreak se hace mas
grande.
-curPicNum es la variable que controla que imagen la carta debe mostrar. Como puedes ver,
hemos hecho una carta movieClip que querrá saber cuales de sus imágenes picCount mostrar.
Sin establecer la detención de la carta movieClip en una imagen particular, simplemente
continuara moviéndose continuamente en círculos a través de las imágenes.

Paso 3: Listo para animar

Ahora podemos hacer que nuestras cartas hagan lo que queramos, es hora de hacer los
movieClips y animaciones necesarias que harán el juego atractivo al ojo.

Primero haz un movieclip que será llamado "cardFront". Este consistirá en todas las imágenes
que serán los frentes de las cartas. También, necesitaremos un mc/graphic llamado "cardBack"
el cual será las partes traseras de las cartas que veremos.

CardFront:

Te sugiero que dimensiones todas las imágenes usando el ancho y alto de la carta. Luego
numerarlas y ponlas en un directorio espacial. Esto haría la importación más simple. Ahora crea
el movieclip apropiado e importa todas las imágenes de ese directorio. Cada imagen debe
importarse a su propio fotograma. La cantidad de fotogramas que tienes debería ser tu variable
picCount. Aquí puedes ver las mías.
La carta de juego: La verdadera carta que usaremos, en realidad pasara varias fases. Lo que
haremos, de modo que podamos nivelar nuestro código para que funcione en un nivel modular,
es que crearemos un movieclip llamado “card”, y haremos toda la animación ahí dentro. Luego
insertaremos el movieclip card dentro del movieclip de la carta de juego, de modo que
tengamos una capa exterior para escribir nuestro código.

El movieclip “carta” (card) debería tener varias animaciones. Deberías animar la parte trasera
de la carta dando se vuelta, y luego el frente abriéndose, como si la carta se hubiese dado
vuelta. También puedes animar la carta volviéndose a voltear, y animar como te gustaría que la
carta se fuese cuando la persona hace el par. Así se ve el mío.

Como puedes ver tengo tres capas. Una para las acciones y títulos de los fotogramas, una para
el frente de la carta y uno para la parte trasera. Aquí hay un análisis mayor de cómo particioné
este mivieclip.

Fotograma1: tiene la acción “stop()” y el nombre de “cardturn”. El stop esta ahí para hacer que
la carta se de vuelta cuando se le pida hacerlo.

Fotograma2-29: animación de las cartas dándose vuelta para mostrar el frente. Como en el
Fotograma2, tengo la acción

// aumentar el numero de cartas en juego


_root.cardsInPlay++;

Este es para decirle a la raíz que esta carta se esta animando. Tenemos que hacer esto porque
solo debemos permitirle a alguien voltear 2 cartas a la vez. Tenemos que controlar cuantas
cartas están en juego cierto? De otra manera podríamos estar volteando varias cartas a la vez.
Fotograma30: Simplemente nombré el fotograma “holdDown” (mantener abajo) para mantener
la carta en esa posición.

Fotograma31-50: En el fotograma 31 tengo el código para comprobar si hay una coincidencia.


Básicamente, hay dos posibilidades. Esta es la primera cata volteada, o la segunda. Si es la
primera, mantén la carta abajo., si es la segunda en el juego hacer el chequeo. Aquí esta el
código.

// si una carta esta en juego luego mantenerla


// si dos cartas están en juego luego chequear si hacen pares
if (_root.cardsInPlay == 1) {
gotoAndPlay("holdDown");
} else if (_root.cardsInPlay == 2) {
// chequeo de la condición carrera de pareo aquí
if (_root.card2Match == cardFace._currentframe) {
gotoAndPlay("correctMatch");
} else {
_root.card2Match = "";
}
}

Aquí, _root.card2Match es la carta que buscamos parear. Cuando sean las mismas, tenemos
lo que queremos. La razón por la cual es una condición carrera, como se establece
anteriormente en los comentarios, es porque cuando se voltea una carta, siempre se pareara
consigo misma, de modo que es importante que esta carta haga toda esta búsqueda antes que
la otra carta la alcance. Estamos seguros con la condición de carrera, porque el código se
evalúa sobre la base de fotograma por fotograma. Este paradigma de código no debería salir
de Actionscript , y no es un paso que los programas normales puedan saltarse.

Fotograma 50-77: animamos la carta volviéndose a dar vuelta.


Fotograma 78 finalizamos la vuelta ahora volvemos al comienzo, también tenemos el código
para sacar esta carta del juego.

// sacar esta carta del juego.


_root.cardsInPlay--;
// volvemos al comienzo.
gotoAndStop("cardTurn");

Fotograma 79-110: el fotograma 79 lo llamamos “correctMatch” de modo que cuando se


produce un par, ira ahí. Hacemos cualquier animación para finalizar. Yo simplemente hice
girarla carta hasta desaparecer. en el fotograma 110 la carta es sacada y ponemos el código
en la capa de acciones.

// Quitar la carta del juego.


_root.cardsInPlay--;
// Un numero menos del total de cartas
_root.totalCardsNum--;

// Chequear si el juego terminó


if (_root.totalCardsNum == 0) {
_root.gotoAndPlay("Ender");
stop();
} else {
stop();
}

Este código saca la carta del juego, tenemos que hacer esto ya que lo podríamos haber
saltado. Y también chequeamos si el juego terminó. Si es así saltamos al final, de lo contrario
nos quedamos donde estamos con la carta faltante. El mivieclip raíz continuará ejecutándose,
el stop solo afectará el movieclip que contiene, de esta forma solo afectando esta carta.
Este código también que inicialicemos la variable totalCardsNum, con el numero total de
cartas que tendremos.

Uy! Se ve como mucho.¿ Cierto? Pero es todo sentido común y simple. Tienes que enseñarle
al computador a hacer cosas que tu quieres que el haga, ese es el juego de la programación.

Paso 4: Unirlo todo

Ahora tenemos nuestro manipulador, y tenemos nuestra carta de juego. Todo lo que tenemos
que hacer es finalizar algo de código en la carta, y comenzar a tirarlas! Oh! y por su puesto,
hacer nuestras escenas de comienzo y fin.

Preparación del volteo de las cartas:

Para las cartas, toma una instancia de la carta de juego (playingCard) y ponla en el escenario
principal. Asegúrate que sea lo suficientemente baja para estar fuera de la pantalla. Nombra la
instancia pCard. Pon el siguiente código.

on (release) {
// solo animar dos cartas en algún momento.
if ((_root.cardsInPlay < 2) && (this.theCard._currentframe <= 1))
{
// si esta es la primara carta guardar el numero de la imagen
//*** no usarse en paginas web ***
if (_root.cardsInPlay == 1) {
_root.card2Match = this.theCard.cardFace._currentframe;
}

// comenzar animacion
this.theCard.play();
// contra intento
_root.attemptCount++;
_root.gameAttempts = "ATTEMPTS:"
+ Math.round(_root.attemptCount / 2);
}
}

Este simple código le dice a la carta, de que cuando es soltada cheque 2 cosas:

1) Si hay ya dos cartas en juego.


2) Si no esta ya animándose.
Si pasa ambas, entonces continua para comprobar si es la primera carta en juego o la
segunda. Dependiendo de la respuesta establece la carta para parearse.

Ahora, anima la carta con la directiva “ play” e incrementamos el conteo de los intentos.
Recuerda? El conteo de los intentos se va a utilizar para calcular el puntaje. Parece que las
carta están listas para girar. Escribamos nuestro código simple de manipulación.

Manipulación de las cartas:

En el escenario principal, en la capa de acciones y probablemente bajo las funciones,


establezcamos las cartas para ser manipuladas:

// crear un mazo
fullDeck = new Array();
fullDeck = createDeck();
// Total de cartas unicas
totalUniqueCards = 16;
// numero total de cartas en juego
totalCardsNum = totalUniqueCards * 2;
// manipula las cartas+ los duplicados del mazo
deal(totalUniqueCards, fullDeck);
// esperar aqui ahora.
stop();

Suficientemente simple? Gracias a las funciones!!!! Ahora podemos jugar con al memoria,
establecer el numero de cartas a manipular, establecer las imágenes fácilmente y manipular las
cartas fácilmente. Este juego puede ser fácilmente amplificado para tener características mas
avanzadas.

Paso 5 – Puntajes y Adiós

Necesitaras crear un fotograma en el escenario principal y llamarlo “Ender”. Se saltara a este


fotograma cuando la última carta sea pareada. Sugiero que lo pongas e una escena
completamente nueva. Tu seguramente quieres calcular tu puntaje. Lo hago multiplicando la
cantidad de intentos por la cantidad de segundos transcurridos. Tú has estado guardando la
cantidad de intentos en una variable global_root.attemptCount, pero ¿ como puedes calcular
cuantos segundos han transcurrido?

Bien, lo que hice fue hacer un cronometro simple en el juego principal. Este consiste en un
mivieclip, con un texto dinámico dentro. Llame al texto “gameTime”

En la capa de acciones del movieclip puse el siguiente código:

onClipEvent(load) {
staticPrefix = "TIMER: ";
startTime = getTimer();
staticSuffix = " seconds";
}
onClipEvent(enterFrame) {
curTime = getTimer();
elapsed = curTime - startTime;
_root.secondsElapsed = Math.floor(elapsed / 1000);
//trace(secondsElapsed);
this.gameTime = staticPrefix +
_root.secondsElapsed + staticSuffix;
}

Este es un código cronometro básico . En la carga del mivieclip, obtiene cuantos milisegundos
el juego ha estado funcionando con el getTimer(). Luego cada fotograma después de ese
(enterFrame), calcula el tiempo el tiempo transcurrido con curTime-startTime. Esto por su
puesto retorna milisegundos, lo que implica que tenemos que dividir por 1k para tener los
segundo normales. Este código establece _root.secondsElapsed para nosotros. De modo
que ahora podemos usar eso para calcular el puntaje  he puesto dos textos dinámicos, uno
llamado summary y el otro finalScore.
También hago un chequeo rápido para asegurarme que alguien no hizo un clic derecho con el
mouse y eligió jugar, para saltarse el juego y obtener un puntaje perfecto. El código en el
fotograma es el siguiente:

// asegurarse que alguien no hizo trampa con el clic derecho del mouse
if (_root.totalCardsNum != 0) {
gotoAndPlay("beginning");
}

// obtener las partes


seconds = _root.secondsElapsed;
numAttempts = Math.floor(_root.attemptCount / 2)

// ahora imprime el puntaje


sumText = "You took " + seconds
+ " seconds to complete this game.\nYou did so in "
+ numAttempts + " attempts.\n\nYour final score is: "
_root.summary = sumText;
_root.finalScore = seconds * numAttempts;
// para la pantalla
stop();

FELICITACIONES! Ahora tienes todas las piezas para hacer un juego de memoria. Puedes ver
la versión finalizada de este juego
en:http://www.BleachEatingFreaks.com/flash/games/memory/

Traducido por dante becerra lagos, softenglish@gmail.com


19 de julio de 2007

You might also like