Construir un llamativo juego de ruptura en Flash
Spanish (Español) translation by Esther (you can also view the original English article)
En este Tutorial Premium, vamos a construir un juego Breakout; "Brick Breaker" desde cero usando Flash y AS3.
Paso 1: Breve resumen
Utilizando las herramientas de dibujo de Flash crearemos una interfaz gráfica de buen aspecto que será impulsada por varias clases de ActionScript 3.
El usuario podrá jugar a través de una serie de niveles, ¡puedes añadir fácilmente tantos niveles como quieras!
Paso 2: Configuración del documento Flash
Abre Flash y crea un documento de 320 píxeles de ancho y 480 de alto. Ajusta la velocidad de fotogramas a 24fps.



Paso 3: Interfaz



Se mostrará una interfaz colorida y de aspecto agradable. Contendrá múltiples formas, botones, mapas de bits y mucho más.
Vamos a saltar directamente a la creación de esta interfaz gráfica de usuario.
Paso 4: Pantalla principal

Esta es la pantalla o vista principal, será el primer gráfico que aparezca en nuestro juego.
Paso 5: Antecedentes
Crea un rectángulo de 320x480 y rellénalo con este degradado radial: #3A9826, #102A07.

Vamos a darle un poco más de detalle añadiendo un filtro de Photoshop, si no tienes Photoshop puedes intentar añadir un bonito efecto usando las herramientas de Flash.
Abre la imagen en Photoshop y ve a Filtros > Textura > Patchwork, utiliza la siguiente configuración:

Terminarás con algo así:

Este fondo estará en el escenario al igual que los indicadores de paleta, pelota y texto. Convierte el fondo en un MovieClip y llámalo bg.
Paso 6: Título
Selecciona la herramienta de texto (T), elige una fuente adecuada y escribe el título de tu juego. Yo he utilizado este formato: Akashi, 55pt, #FFCC33.

Selecciona el campo de texto y utiliza el panel de filtros para añadir una sombra:

Duplica el texto (Cmd + D) y muévelo 3px hacia arriba para darle algo de relieve.

Convierte los gráficos en un MovieClip y nómbralo MenuScreen, recuerda marcar la casilla Export for ActionScript. Puedes eliminar esto del escenario cuando termines, ya que será llamado usando AS3.
Paso 7: Paleta
Usa la herramienta Rectángulo Primitivo (R) para crear un rectángulo redondo de 57x11.5px, cambia el radio de la esquina a 10 y aplica este gradiente: #4E4E4E, #BABABA, #B0B3BA.

Añade algunas líneas de detalle con la herramienta Rectángulo, ¡utiliza tu propio estilo!

También puedes añadir algo de color a tu paleta, aquí está el resultado final de la mía, el color utilizado es: #CC0000.

Convierta los gráficos en un MovieClip y nómbrelo como paleta.
Paso 8: Pelota
Para crear la pelota, selecciona la herramienta Oval (O) y utilízala para crear un círculo de 12x12px, #CCCCCC.

Duplica el círculo (Cmd + D) cambia su tamaño a 10x10px y rellénalo con este degradado radial: #95D4FF, #0099FF.

Por último, corta el segundo círculo por la mitad y utiliza la herramienta de selección (V) para hacer una curva en la parte inferior. Cambia su color a un degradado lineal blanco con alfa 60, 10.

Convierte los gráficos en un MovieClip y llámalo pelota.
Paso 9: Ladrillo
Nuestro ladrillo será muy sencillo.
Usa la herramienta Rectángulo para crear un rectángulo de 38x18px y aplica el siguiente degradado: #CC0000, #8E0000, #FF5656.

Convierte el rectángulo en un MovieClip y aplica el filtro de sombra utilizado en el texto del título para darle un aspecto más agradable.
Convierte de nuevo el gráfico en un MovieClip y nómbralo Brick, recuerda marcar la casilla Exportar para ActionScript.
Paso 10: Pantalla "About" (Acerca de)
La pantalla "About" mostrará los créditos, el año y el copyright del juego.
Será bastante sencillo de crear, pues ya tenemos todos los elementos que se utilizan en él.

Convierte los gráficos en un MovieClip y nómbralo AboutScreen, recuerda marcar la casilla Export for ActionScript.
Paso 11: Pantalla de juego
Esta es la pantalla del juego, estará en el escenario desde el principio y contendrá la pala, la pelota, el fondo y los indicadores de texto. (Añadiremos los ladrillos mediante código).

Los nombres de las instancias son bastante fáciles y se explican por sí mismos: paddle, ball, bg, scoreTF, livesTF y levelTF.
Paso 12: Insertar fuentes
Para poder utilizar la fuente personalizada de forma dinámica tendremos que incrustarla en la aplicación.
Selecciona un campo de texto dinámico y haz clic en el botón Incrustar... del Panel de Propiedades.

Selecciona/añade todos los caracteres necesarios y haz clic en OK.
Paso 13: Pantalla de alerta
Esta pantalla aparecerá cuando se haya decidido la partida; o bien se gana, se pierde o se llega al game over (ganar todos los niveles o perder todas las vidas).
En esta vista se utilizan dos TextFields dinámicos, que mostrarán el estado actual del juego más un breve mensaje. Los TextFields se llaman titleTF y msgTF.

Convierte los gráficos en un MovieClip y marca la casilla Exportar para ActionScript, establece el nombre de la clase como AlertScreen.
Con esto termina la parte gráfica, ¡que empiece el ActionScript!
Paso 14: Tween Nano

Utilizaremos un motor de interpolación diferente al incluido por defecto en flash, esto aumentará el rendimiento y será más fácil de usar.
Puedes descargar Tween Nano desde su página web oficial.
Paso 15: Nueva clase de ActionScript
Crea una nueva (Cmd + N) Clase de ActionScript 3.0 y guárdala como Main.as en tu carpeta de clases.

Paso 16: Estructura de la clase
Crea tu estructura de clases básica para empezar a escribir tu código.
package { import flash.display.Sprite; public class Main extends Sprite { public function Main():void { // constructor code } } }
Paso 17: Clases requeridas
Estas son las clases que necesitaremos importar para que nuestra clase funcione, la directiva import hace que las clases y paquetes definidos externamente estén disponibles para tu código.
import flash.display.Sprite; import flash.ui.Mouse; import flash.events.MouseEvent; import flash.events.KeyboardEvent; import flash.events.Event; import com.greensock.TweenNano; import com.greensock.easing.Circ;
Paso 18: Variables y constantes
Estas son las variables y constantes que utilizaremos, lee los comentarios en el código para descubrir más sobre ellas.
private const BRICK_W:int = 39; //brick's width private const BRICK_H:int = 19; //brick's height private const OFFSET:int = 6; //An offset used to center the bricks private const W_LEN:int = 8; //the length of the levels, only 8 horizontal bricks should be created on stage private const SCORE_CONST:int = 100; //the amount to add to the score when a brick is hit private var bricks:Vector.<Sprite> = new Vector.<Sprite>(); //stores all the bricks private var xSpeed:int = 5; private var ySpeed:int = -5; private var xDir:int = 1; //x direction private var yDir:int = 1; private var gameEvent:String = ''; //stores events like win, lose, gameover private var currentLevel:int = 0; private var menuScreen:MenuScreen; //an instance of the menu screen private var aboutScreen:AboutScreen; private var alertScreen:AlertScreen; private var lives:int = 3; private var levels:Array = []; //stores the levels
Paso 19: Niveles
Todos nuestros niveles se almacenarán en arrays multidimensionales.
Se trata de arrays que contienen arrays; puedes escribirlos en una sola línea, pero si los alineas puedes ver realmente la forma que tendrá el nivel.
private const LEVEL_1:Array = [[0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0], [0,0,0,1,1,0,0,0], [0,0,0,1,1,0,0,0], [0,1,1,1,1,1,1,0], [0,1,1,1,1,1,1,0], [0,0,0,1,1,0,0,0], [0,0,0,1,1,0,0,0], [0,0,0,0,0,0,0,0],]; //this forms a + sign! private const LEVEL_2:Array = [[0,0,0,0,0,0,0,0], [0,0,0,1,1,0,0,0], [0,0,1,0,0,1,0,0], [0,0,0,0,0,1,0,0], [0,0,0,0,1,0,0,0], [0,0,0,1,0,0,0,0], [0,0,1,0,0,0,0,0], [0,0,1,1,1,1,0,0],]; //this forms a number 2!
En estos niveles los 1s representan el espacio del escenario donde se colocará un ladrillo, y los 0s son simplemente espacio vacío. Estos niveles serán leídos posteriormente por una función que colocará los ladrillos en el escenario. ¡Puedes añadir tantos niveles como quieras usando esta clase!
Paso 20: Código del constructor
El constructor es una función que se ejecuta cuando se crea un objeto a partir de una clase; este código es el primero que se ejecuta cuando se crea una instancia de un objeto (o se ejecuta cuando se carga el juego, en el caso de una clase documento).
Llama a las funciones necesarias para iniciar el juego. Comprueba estas funciones en los siguientes pasos.
public final function Main():void { /* Add Levels */ levels.push(LEVEL_1, LEVEL_2); //we add the levels to the array in order to know how many they are /* Menu Screen, Buttons Listeners */ menuScreen = new MenuScreen(); addChild(menuScreen); menuScreen.startB.addEventListener(MouseEvent.MOUSE_UP, tweenMS); menuScreen.aboutB.addEventListener(MouseEvent.MOUSE_UP, tweenMS); }
Paso 21: Pantalla de menús y animación de la vista "About".
Las siguientes líneas manejan los botones de la pantalla de menús y alternan la vista de menús o de "Acerca de" dependiendo del botón presionado.
private final function tweenMS(e:MouseEvent):void { if(e.target.name == 'startB') //if start button is clicked { TweenNano.to(menuScreen, 0.3, {y: -menuScreen.height, ease: Circ, onComplete: init}); //tween menu screen } else //if about button is clicked { aboutScreen = new AboutScreen();//add about screen addChild(aboutScreen); TweenNano.from(aboutScreen, 0.3, {x: stage.stageWidth, ease: Circ}); //tween about screen aboutScreen.addEventListener(MouseEvent.MOUSE_UP, hideAbout); //add a mouse listener to remove it } } /* Removes About view */ private final function hideAbout(e:MouseEvent):void { TweenNano.to(aboutScreen, 0.3, {x:stage.stageWidth, ease:Circ, onComplete:function rmv():void{ aboutScreen.removeEventListener(MouseEvent.MOUSE_UP, hideAbout); removeChild(aboutScreen); }}); }
Paso 22: Función Init
Esta función realiza las operaciones necesarias para iniciar el juego, lee los comentarios en el código para saber más sobre ella.
private final function init():void { /* Destroy Menu Screen */ menuScreen.startB.removeEventListener(MouseEvent.MOUSE_UP, tweenMS); menuScreen.aboutB.removeEventListener(MouseEvent.MOUSE_UP, tweenMS); removeChild(menuScreen); menuScreen = null; /* Hide Cursor */ Mouse.hide(); /* Build Level Bricks */ buildLevel(LEVEL_1); /* Start Listener */ bg.addEventListener(MouseEvent.MOUSE_UP, startGame); }
Paso 23: Mover la paleta
La paleta será controlada por el ratón, seguirá la posición x del ratón.
private final function movePaddle(e:MouseEvent):void { /* Follow Mouse */ paddle.x = mouseX; }
Paso 24: Colisión del borde de la paleta
Para evitar que la paleta salga del escenario, creamos límites invisibles a los lados de la pantalla.
{ /* Follow Mouse */ paddle.x = mouseX; /* Borders */ if((paddle.x - paddle.width / 2) < 0) { paddle.x = paddle.width / 2; } else if((paddle.x + paddle.width / 2) > stage.stageWidth) { paddle.x = stage.stageWidth - paddle.width / 2; } }
Paso 25: Construir función de nivel
Los niveles serán construidos por esta función.
Utiliza un parámetro para obtener el nivel a construir, calcula su tamaño y ejecuta un bucle for anidado, con un bucle para la altura y otro para la anchura. A continuación, crea una nueva instancia de Brick que se coloca según su anchura, altura y el número correspondiente a i y j.
Por último, el bricks
se añade al vector de bloques para acceder a él fuera de esta función.
private final function buildLevel(level:Array):void { /* Level length, height */ var len:int = level.length; for(var i:int = 0; i < len; i++) { for(var j:int = 0; j < W_LEN; j++) { if(level[i][j] == 1) { var brick:Brick = new Brick(); brick.x = OFFSET + (BRICK_W * j); brick.y = BRICK_H * i; addChild(brick); bricks.push(brick); } } } }
Paso 26: Escuchar el juego
Esta función añade o elimina los escuchadores del ratón y del marco de entrada. Utiliza un parámetro para determinar si los oyentes deben ser añadidos o eliminados: por defecto es añadir.
private final function gameListeners(action:String = 'add'):void { if(action == 'add') { stage.addEventListener(MouseEvent.MOUSE_MOVE, movePaddle); stage.addEventListener(Event.ENTER_FRAME, update); } else { stage.removeEventListener(MouseEvent.MOUSE_MOVE, movePaddle); stage.removeEventListener(Event.ENTER_FRAME, update); } }
Paso 27: Iniciar la función de juego
El siguiente código llama a la función gameListeners()
para iniciar el juego.
private final function startGame(e:KeyboardEvent):void { bg.removeEventListener(MouseEvent.MOUSE_UP, startGame); gameListeners(); }
Paso 28: Movimiento de la pelota
La velocidad de la pelota está determinada por las variables xSpeed y ySpeed, cuando se ejecuta la función update, la pelota comienza a moverse usando estos valores en cada cuadro.
private final function update(e:Event):void { /* Ball Movement */ ball.x += xSpeed; ball.y += ySpeed;
Paso 29: Colisión con la pared
Este código comprueba las colisiones entre la pelota y las paredes.
/* Wall Collision */ if(ball.x < 0){ball.x = ball.x + 3;xSpeed = -xSpeed;};//Left if((ball.x + ball.width) > stage.stageWidth){ball.x = ball.x - 3;xSpeed = -xSpeed;};//Right if(ball.y < 0){ySpeed = -ySpeed;};//Up
Paso 30: Perder el evento del juego
Se utiliza una sentencia if para comprobar si la paleta pierde la pelota. Si es así, el jugador pierde una vida.
if(ball.y + ball.height > paddle.y + paddle.height){alert('You Lose', 'Play Again ›');gameEvent = 'lose';lives--;livesTF.text = String(lives);};//down/lose
Paso 31: Colisiones de pelotas de paleta
Cuando la pelota golpea la paleta, la ySpeed se pone en negativo para que la pelota suba. También comprobamos en qué lado de la paleta ha golpeado la pelota para elegir el lado al que se moverá a continuación.
/* Paddle Collision, check the which side of the paddle the ball hits*/ if(paddle.hitTestObject(ball) && (ball.x + ball.width / 2) < paddle.x) { ySpeed = -5; xSpeed = -5; //left } else if(paddle.hitTestObject(ball) && (ball.x + ball.width / 2) >= paddle.x) { ySpeed = -5; xSpeed = 5; //right }
Paso 32: Colisiones de bloques
Usamos un for y hitTest para comprobar las colisiones con los bloques, cuando la pelota choca con un bloque se usa la misma técnica utilizada en la paleta para determinar el lado que seguirá la pelota.
/* Bricks Collision */ for(var i:int = 0; i < bricks.length; i++) { if(ball.hitTestObject(bricks[i])) { /* Check the which side of the brick the ball hits, left, right */ if((ball.x + ball.width / 2) < (bricks[i].x + bricks[i].width / 2)) { xSpeed = -5; } else if((ball.x + ball.width / 2) >= (bricks[i].x + bricks[i].width / 2)) { xSpeed = 5; }
Paso 33: Cambiar la dirección de la pelota y retirar el bloque
El siguiente código cambia la dirección Y de la pelota y elimina el bloque del escenario y el vector.
/* Change ball y direction */ ySpeed = -ySpeed; removeChild(bricks[i]); bricks.splice(i, 1);
Si quieres, puedes cambiar esta lógica para que la velocidad y de la bola sólo se invierta si golpea la parte superior o inferior de un ladrillo, y no cuando golpea los lados. Pruébalo y verás qué te parece.
Paso 34: Añadir puntuación y comprobar la victoria
Cada golpe de bloque añadirá 100 a la puntuación, la puntuación se tomará de la constante de puntuación y se añadirá a la puntuación actual utilizando las funciones int y String. Este código también comprueba si no hay más bloques en el vector y muestra una alerta si es así.
/* Score++ */ scoreTF.text = String(int(scoreTF.text) + SCORE_CONST); } } /* Check if all bricks are destroyed */ if(bricks.length < 1) { alert('You Win!', 'Next Level ›'); gameEvent = 'win'; } }
Paso 35: Pantalla de alerta
La pantalla de alerta muestra al jugador información sobre el estado del juego, se muestra cuando se alcanza un evento del juego, como perder una vida o completar un nivel.
En esta función se utilizan dos parámetros:
- t: El título de la alerta
- m: Un mensaje corto
private final function alert(t:String, m:String):void { gameListeners('remove'); Mouse.show(); alertScreen = new AlertScreen(); addChild(alertScreen); TweenNano.from(alertScreen.box, 0.3, {scaleX: 0.5, scaleY:0.5, ease:Circ}); alertScreen.box.titleTF.text = t; alertScreen.box.msgTF.text = m; alertScreen.box.boxB.addEventListener(MouseEvent.MOUSE_UP, restart); }
Paso 36: Función de reinicio
La siguiente función comprueba el estado de la partida (ganada, perdida, terminada) y realiza una acción en función de ello.
private final function restart(e:MouseEvent):void { if(gameEvent == 'win' && levels.length > currentLevel+1) //if level is clear but more levels are left { currentLevel++; changeLevel(levels[currentLevel]);//next level levelTF.text = 'Level ' + String(currentLevel + 1); } else if(gameEvent == 'win' && levels.length <= currentLevel+1) //if level is clear and no more levels are available { alertScreen.box.boxB.removeEventListener(MouseEvent.MOUSE_UP, restart); removeChild(alertScreen); alertScreen = null; alert('Game Over', 'Congratulations!'); gameEvent = 'finished'; } else if(gameEvent == 'lose' && lives > 0) //if level failed but lives > 0 { changeLevel(levels[currentLevel]);//same level } else if(gameEvent == 'lose' && lives <= 0) //if level failed and no more lives left { alertScreen.box.boxB.removeEventListener(MouseEvent.MOUSE_UP, restart); removeChild(alertScreen); alertScreen = null; alert('Game Over', 'Try Again!'); gameEvent = 'finished'; } else if(gameEvent == 'finished') //reached when no more lives or levels are available { /* Add menu screen */ menuScreen = new MenuScreen(); addChild(menuScreen); menuScreen.startB.addEventListener(MouseEvent.MOUSE_UP, tweenMS); menuScreen.aboutB.addEventListener(MouseEvent.MOUSE_UP, tweenMS); TweenNano.from(menuScreen, 0.3, {y: -menuScreen.height, ease: Circ}); /* Reset vars */ currentLevel = 0; lives = 3; livesTF.text = String(lives); scoreTF.text = '0'; levelTF.text = 'Level ' + String(currentLevel + 1); xSpeed = 5; ySpeed = -5; clearLevel(); } }
Paso 38: Cambiar de nivel
Esta función cambia al nivel escrito en el parámetro.
private final function changeLevel(level:Array):void { /* Clear */ clearLevel(); /* Redraw Bricks */ buildLevel(level); /* Start */ Mouse.hide(); bg.addEventListener(MouseEvent.MOUSE_UP, startGame); }
Paso 39: Borrar el nivel
Una función para eliminar los bloques restantes y las alertas del escenario. También restablecerá la posición de la paleta y la pelota.
private final function clearLevel():void { /* Remove Alert Screen */ alertScreen.box.boxB.removeEventListener(MouseEvent.MOUSE_UP, restart); removeChild(alertScreen); alertScreen = null; /* Clear Level Bricks */ var bricksLen:int = bricks.length; for(var i:int = 0; i < bricksLen; i++) { removeChild(bricks[i]); } bricks.length = 0; /* Reset Ball and Paddle position */ ball.x = (stage.stageWidth / 2) - (ball.width / 2); ball.y = (paddle.y - paddle.height) - (ball.height / 2) -2; paddle.x = stage.stageWidth / 2; }
Paso 40: Establecer la clase principal

En este tutorial haremos uso de la Clase Documento, si no sabes cómo usarla o estás un poco confundido por favor lee este QuickTip.
Conclusión
El resultado final es un juego personalizable y entretenido, ¡prueba a añadir tus gráficos y niveles personalizados!
Espero que te haya gustado este tutorial de Active Premium, ¡gracias por leer!