Spanish (Español) translation by Viviana Brito (you can also view the original English article)
Este tutorial le dará una alternativa para juegos de carrera 3D en ActionScript 3. Ningún framework externo es requerido para este ejemplo con estilo de la vieja escuela.
Avance del Resultado Final
Vamos a echar una mirada al resultado final hacia el que estaremos trabajando.
Paso 1: Crea el Documento FLA.
Crea un nuevo documento Flash configurado para ActionScript 3.0. Estaré usando dimensiones de 480x320px, una velocidad de fotogramas de 30 FPS, y un fondo de color celeste. Guarde el archivo con un nombre a su elección.



Paso 2: Crear una Clase Documento
Aparte de el FLA, también necesitamos crear una clase documento. Crea un nuevo archivo Actionscript y añada este código.
package { import flash.display.MovieClip; public class Main extends MovieClip { public function Main() { } } }
Guarde este archivo en el mismo directorio que su FLA. Nombrela como Main.as.
Paso 3: Asociar la Clase Main con el FLA
Para poder compilar el código desde la clase Main, necesitamos asociarlo con el FLA. En el panel de Propiedades del FLA, seguido de Class, ingrese el nombre de la clase documento, en este caso, Main.

Luego, guarde los cambios en el FLA.
Paso 4: Dibujar una Línea de Carretera.
Necesitamos comenzar con una línea para representar un segmento del camino. Presione R para seleccionar la herramienta de Rectángulo. En este ejemplo voy a hacer un rectángulo gris para el camino, dos pequeños rectángulos rojos para cada borde del gris, y rectángulos verdes para rellenar el resto de la línea. Los verdes deben ser más anchos que el escenario, voy a hacerlos de 1500 pixeles de ancho. La amplitud del camino puede variar según sus necesidades, voy a usar uno de 245px de ancho. No es necesario que sean muy altos, ya que estaremos usando varias instancias para dibujar todo el camino en la pantalla. Voy a hacerlos de 10px de alto.



Paso 5: Crear un MovieClip para las Lineas de la Carretera.
Una vez que haya dibujado todos los rectángulos, selecciónelos a todos (Ctrl + A) y presione F8 para crear un Clip de Película de esos rectángulos que acaba de hacer. Nombrelo "Road", asegúrese de que el Registration Point está en el centro y seleccione el checkbox "Export for ActionScript".



Terminará con un MovieClip Road en la Librería.



Depende de usted si quiere dibujar cada rectángulo en diferentes capas. Yo sólo voy a poner el gris en una segunda capa. Si tiene una instancia Road en el Stage, elimínela. Vamos a añadir más tarde el MovieClip Road mediante código.
Paso 6: Establecer el Área de Juego.
Vamos a volver a la clase Main. Usaremos ese MovieClip Road para generar la ilusión de una pista de carreras.
Determinaremos la profundidad del camino visible, así como también las dimensiones del área de juego. También, en nuestra clase, todas las instancias Road que añadimos al stage serán accedidas desde un Array. Usaremos otro Array (zMap) para determinar la profundidad de cada línea.
En este ejemplo, voy a establecer una profundidad de 150 líneas de carretera en un área de juego de 480x320 (no es necesario que sea del mismo tamaño que el stage, pero ya que es todo lo que se mostrará, voy a usar esos números).
//Depth of the visible road private const roadLines:int = 150; //Dimensions of the play area. private const resX:int = 480; private const resY:int = 320; //Line of the player's car. private const noScaleLine:int = 8; //All the road lines will be accessed from an Array. private var zMap:Array = []; private var lines:Array = []; private var halfWidth:Number; private var lineDepth:int; private const widthStep:Number = 1;
Paso 7: Visualizar el Camino por Código
Vamos a emplear todas las variables y constantes previas dentro de la función Main. Vamos a escalar cada línea de acuerdo a su correspondiente profundidad.
public function Main() { //Populate the zMap with the depth of the road lines for (var i:int = 0; i < roadLines; i++) { zMap.push(1 / (i - resY / 2)); } //We want the line at the bottom to be in front of the rest, //so we'll add every line at the same position, bottom first. lineDepth = numChildren; for (i = 0; i < roadLines; i++) { var line = new Road(); lines.push(line); addChildAt(line, lineDepth); line.x = resX / 2; line.y = resY - i; } //Scaling the road lines according to their position halfWidth = resX / 2; for (i = 0; i < roadLines; i++) { lines[i].scaleX = halfWidth / 60 - 1.2; halfWidth -= widthStep; } }
Si usted Publica (Ctrl + Enter) el documento en este punto, va a obtener una vista de un camino recto.



Puede juguetear un poco con los cálculos de escala para obtener resultados diferentes. Puede desear un camino más amplio o una vista a la distancia, más larga.
Paso 8: Hacer un Segundo Gráfico de Camino.
En este momento el camino parece tan plano que usted no es capaz de decir si nos estamos moviendo hacia adelante. Necesitamos al menos dos estilos diferentes de segmentos para distinguir cuan rápido o lento nos estamos moviendo.
Vaya al panel de la Librería y haga doble clic sobre el Clip de Película Road para que obtenga los rectángulos que dibujó. Ahora presione F6 para insertar un nuevo Keyframe (Si tiene más de un cuadro va a necesitar insertar un nuevo Keyframe en cada capa). Ahora, basado en el primer cuadro, puede cambiar los colores de los rectángulos o modificar su diseño de alguna manera. Voy a cambiar su color y añadir algunas líneas de carril para el segundo cuadro.



Paso 9: Mantener la Línea del Jugador sin Escalar.
Definiremos una nueva variable en la clase Main para sostener la consistencia en la línea del jugador (asumiendo que va a haber un auto en el juego, vamos a mantener la escala a 1 en esa línea).
private var playerZ:Number;
A continuación, modificaremos la función Main.
Paso 10: Añadir Líneas Alternativas al Camino.
Esta variable será usada en la función Main. Ahora las líneas Road serán segmentadas, algunas se visualizarán en el segundo cuadro y el resto en el primero, aumentando la ilusión de una pista de carrera.
public function Main() { for (var i:int = 0; i < roadLines; i++) { zMap.push(1 / (i - resY / 2)); } playerZ = 100 / zMap[noScaleLine]; for (i = 0; i < roadLines; i++) { zMap[i] *= playerZ; } lineDepth = numChildren; for (i = 0; i < roadLines; i++) { var line = new Road(); lines.push(line); addChildAt(line, lineDepth); line.x = resX / 2; line.y = resY - i; } halfWidth = resX / 2; for (i = 0; i < roadLines; i++) { if (zMap[i] % 100 > 50) lines[i].gotoAndStop(1); else lines[i].gotoAndStop(2); lines[i].scaleX = halfWidth / 60 - 1.2; halfWidth -= widthStep; } }
Puede no ser necesario multiplicar por 100 para obtener los segmentos de manera correcta, pero estos son números que voy a usar en este ejemplo, siéntase libre de modificar los números a su gusto (y si arruina algo, tiene esto como referencia).



Paso 11: Configurar una Velocidad y un Offset
Vamos a comenzar a hacer que las cosas se muevan. Estableceremos una variable para la velocidad. Esto indicará la profundidad que avanzaremos por cuadro. Voy a comenzar a una velocidad de 20, puede usar cualquier número que usted quiera.
También necesitaremos un indicador para los segmentos del camino, que cambiará de acuerdo a la velocidad.
private var speed:int = 20; private var texOffset:int = 100;
Paso 12: Comenzar a Avanzar
Antes de que podamos hacer algo con esas variables, vamos a necesitar importar un nuevo Evento en esta clase. Podemos usar un Timer o un EnterFrame. En este ejemplo voy a usar un Evento EnterFrame.
import flash.events.Event;
Ahora, vamos a cortar el último condicional en la función Main()
y moverlo a una nueva función que estamos creando. Esta nueva función va a ser disparada por el Evento EnterFrame, de manera tal que vamos a tener movimiento continuo en el camino. Vamos a nombrarlo race()
.
public function Main() { for (var i:int = 0; i < roadLines; i++) { zMap.push(1 / (i - resY / 2)); } playerZ = 100 / zMap[noScaleLine]; for (i = 0; i < roadLines; i++) { zMap[i] *= playerZ; } lineDepth = numChildren; for (i = 0; i < roadLines; i++) { var line = new Road(); lines.push(line); addChildAt(line, lineDepth); line.x = resX / 2; line.y = resY - i; } halfWidth = resX / 2; for (i = 0; i < roadLines; i++) { lines[i].scaleX = halfWidth / 60 - 1.2; halfWidth -= widthStep; } addEventListener(Event.ENTER_FRAME, race); }
Paso 13: Definir una Función Race.
A continuación, vamos a traer de vuelta el condicional que fue cortado a la nueva función para obtener movimiento. El texOffset apuntará la posición del camino para mantener una ilusión precisa de movimiento.
private function race(event:Event):void { for (var i:int = 0; i < roadLines; i++) { if ((zMap[i] + texOffset) % 100 > 50) lines[i].gotoAndStop(1); else lines[i].gotoAndStop(2); } texOffset = texOffset + speed; while (texOffset >= 100) { texOffset -= 100; } }
Si Publica esto ahora, debería obtener un camino animado.
Paso 14: Direccionamiento
Caminos perpetuamente derechos son aburridos y hay miles de maneras de hacer una perspectiva que vaya sólo para adelante. Ahora vamos a añadir algunas nuevas variables que se encarguen de las curvas en movimiento.
En este ejemplo voy a alternar curvas hacia la derecha con secciones rectas. El camino por delante va a ser almacenado en la variable nextStretch. También, vamos a mover la posición x en las curvas.
private var rx:Number; //Each line's x position private var dx:Number; //Curve amount per segment private var ddx:Number = 0.02; //Curve amount per line private var segmentY:int = roadLines; private var nextStretch = "Straight";
Paso 15: Añadir Curvas al Camino
La variable rx va a almacenar la posición x de cada línea, así que vamos a querer que comience en el centro y tomar las curvas a partir de allí. Otra cosa, ddx
controla la agudeza de las curvas. En este ejemplo lo tengo a 0.02; usted puede variar su valor entre curvas. Así es como la nueva función race()
se verá.
private function race(event:Event):void { rx = resX / 2; dx = 0; for (var i:int = 0; i < roadLines; i++) { if ((zMap[i] + texOffset) % 100 > 50) lines[i].gotoAndStop(1); else lines[i].gotoAndStop(2); lines[i].x = rx; if (nextStretch == "Straight") { if (i >= segmentY) dx += ddx; else dx -= ddx / 64; //Reverts smoothly from a curve to a straight part. } else if (nextStretch == "Curved") { if (i <= segmentY) dx += ddx; else dx -= ddx / 64; } rx += dx; } texOffset = texOffset + speed; while (texOffset >= 100) { texOffset -= 100; } segmentY -= 1; while (segmentY < 0) { segmentY += roadLines; if (nextStretch == "Curved") nextStretch = "Straight"; else nextStretch = "Curved"; } }
Esta vez no vamos a tocar la función Main. Si lo Publica ahora, va a ver algo como esto.



Puede querer cambiar el valor de la Curva para Izquierda y Derecha y los valores de la dirección. En este punto debería ser capaz de añadir un auto a la escena y controlar manualmente la velocidad.
Paso 16: Colinas, Pendientes
¿Recuerda que los rectángulos para el camino son de más de 1 pixel de alto? Eso puede ayudarle a ampliar la vista del camino en caso de querer colinas en nuestro juego.
Hay un método para hacer colinas que es muy similar al de las curvas. Puede haber muchos métodos diferentes, pero este es uno de los que estaré usando aquí. Para simplificarlo, voy a reciclar la mayor cantidad del código que ya tenemos y sólo añadir unas pocas líneas para este nuevo efecto. Como siempre, si no le gustan los resultados puede modificar los valores a su voluntad.
Acabamos de hacer unas variables para las posiciones x de las líneas del camino, ahora vamos a hacer las de las posiciones y, también.
private var ry:Number; private var dy:Number; private var ddy:Number = 0.01; //A little less steep than the curves.
Paso 17: Cuesta abajo, Cuesta arriba
Para hacerlo más sencillo, en este ejemplo voy a usar los mismos segmentos rectos para un efecto cuesta arriba recto y las curvas para una curva y un efecto de descenso.
private function race(event:Event):void { rx = resX / 2; ry = resY; dx = 0; dy = 0; for (var i:int = 0; i < roadLines; i++) { if ((zMap[i] + texOffset) % 100 > 50) lines[i].gotoAndStop(1); else lines[i].gotoAndStop(2); lines[i].x = rx; lines[i].y = ry; if (nextStretch == "Straight") { if (i >= segmentY) { dx += ddx; dy -= ddy; } else { dx -= ddx / 64; dy += ddy; } } else if (nextStretch == "Curved") { if (i <= segmentY) { dx += ddx; dy -= ddy; } else { dx -= ddx / 64; dy += ddy; } } rx += dx; ry += dy - 1; } texOffset = texOffset + speed; while (texOffset >= 100) { texOffset -= 100; } segmentY -= 1; while (segmentY < 0) { segmentY += roadLines; if (nextStretch == "Curved") nextStretch = "Straight"; else nextStretch = "Curved"; } }
En su juego puede separar las curvas de las colinas y hacer dos algoritmos diferentes, pero este ejemplo muestra los similares que pueden ser.



Paso 18: Mejorar la Estética del Camino
Los juegos de antes no tenían la ventaja del Flash, pero nosotros si. Algo tan simple como añadir un gradiente a las líneas del camino hará una buena diferencia. Si lo desea, puede usar cualquier filtro y texturas que guste, pero en este ejemplo estaré añadiendo algunos gradientes simples, así que volvamos al MovieClip Road.
En el cuadro 1, seleccione el rectángulo gris, luego vaya al panel de Color y seleccione Linear Gradient del menú desplegable, luego elija Reflect color como Flow, de manera que el gradiente se aplicará desde el primero hasta el último color. No le estoy diciendo que elija el mismo color que yo, pero aquí voy a usar el #666666 y el #999999. Si necesita rotar el gradiente, presione F para cambiar a Gradient Transform Tool, que le permitirá mover, rotar y redimensionar su gradiente. En este caso voy a mover el gradiente a un cuarto del rectángulo y redimensionarlo a la mitad del tamaño de modo tal que el centro sea más claro y los bordes más oscuros. Yo uso un tamaño similar para la parte verde, así que cambiaré de verde oscuro (#006600) a un verde claro (#009900) continuamente.



Ahora vaya al cuadro 2 y haga nuevos gradientes con diferentes colores. Para el rectángulo gris, mantuve el color claro y sólo cambié el color oscuro a #777777. En la parte verde, cambié el tamaño del gradiente para evitar un aspecto de tablero de damas y el cambio de colores fue muy sutil (#007700 y #008800).



Tal vez ahora va a querer agregar un lindo fondo en el horizonte, o algunos gráficos para el cielo.
Conclusión.
Ya sea que cuente con pocos recursos para frameworks 3D o simplemente quiera un aspecto de vieja escuela, ahora tiene un ejemplo simple sobre cómo hacer una ilusión de profundidad para un juego de carreras. Ahora depende de usted si será un Gran Prix o una carrera callejera en una autopista llena de tráfico o quizás algo que no tenga relación con las carreras.
Espero que este tutorial le haya sido de ayuda. ¡Gracias por leer!