Creación de un juego de coches de carreras en Flash

Introducción

Buenas, primero de todo gracias por interesarte por este tutorial, que tanto tiempo cuesta hacerlo, por no hablar del juego en sí, que me llevó dos jornadas de trabajo bastante intensas.

Primero de todo comentarte que para realizar este tutorial y comprenderlo del todo, es probable que necesites un nivel medio/alto de ActionScript, ya que si no, es posible que te quedes bastante pez en algunos momentos. Yo intentaré explicar todo lo mejor que pueda, ;-) .

Saludos, y gracias!!!

Capítulo 1: Creación del coche y movimiento

Empezaremos por ver como se realiza el coche y el movimiento del mismo, para ello dibuja un coche como más te guste, yo he dibujado este que veis aquí debajo:



Es importante que lo dibujéis de lado, luego mediante una función podremos elegir hacia donde queráis que mire.

Bien, una vez lo tenéis dibujado tenéis que convertirlo en un clip de película y llamarlo en la biblioteca AccionesCocheAzul. En este clip meteremos las acciones para girar, colisionar, etcetera. De momento para no liar al personal pondremos solo las acciones de girar, mas adelante veremos como hacerlo para que colisione con las paredes y el otro coche. Las acciones que teneis que poner ahora son estas:


onClipEvent (load) {
_root.speeda = 0;
_root.angle1 = 270;
}
onClipEvent (enterFrame) {
radian = Math.PI/180*_root.angle1;
_parent._x += (_root.speeda*Math.cos(radian));
_parent._y += (_root.speeda*Math.sin(radian));
_parent._rotation = _root.angle1;
}


Expliación del códigorimero inicializamos las variables speeda y angle1. Speeda, es la velocidad (empieza parado), y angle1 es la dirección en la que empieza mirando. Si angle1 está en 0 empezará mirando hacia la derecha, yo lo he puesto en 270, por lo tanto empezará mirando 270º mas hacia la derecha, es decir, mirando hacia arriba. Ver imagen:



Con la variable radian, calculamos el angulo de giro, fijaos que _root.angle1 aparece aquí, y tened en cuenta que aunque cambiemos el valor para posicionarlo mirando hacia donde querais, esta formula funcionara igual. Luego con el coseno y el seno (math.cos y math.sin) calculamos cuanto tiene que moverse en x e y segun la velocidad que lleva (_root.speeda) en ese momento nuestro vehiculo. Por ultimo, aplicamos la rotacion que hemos guardado en _root.angle1. Esta variable la modificaremos con nuestro teclado, lo veremos en el siguiente paso.

Bueno, ahora tenemos nuestro coche pero aún no se mueve, sólo hemos aplicado las fórmulas, tenemos que darle valores a esas fórmulas a través de nuestro teclado, vamos a ver como.

Este clip de película, conviértelo de nuevo en clip de película, puedes llamarlo Jugador1, Coche1 o como mas te apetezca, pero en el nombre de variable ponle \"cocheazul\". Luego, ponle las siguientes acciones:

onClipEvent (enterFrame) {
// Primer IF, incremento y decremento del giro
if (_root.speeda>0 || _root.speeda<0) {
if (Key.isDown(Key.RIGHT)) {
_root.angleclav1 = 4;
} else if (Key.isDown(Key.LEFT)) {
_root.angleclav1 = -4;
}
}
//Segundo IF,velocidad y desaceleracion en la marcha adelante
if (Key.isDown(Key.UP)) {
if (_root.speeda<8) {
_root.speeda += 0.5;
}
} else {
if (_root.speeda>0) {
_root.speeda -= 0.1;
if (_root.speeda<0.5) {
_root.speeda = 0;
}
}
}
//Tercer IF,velocidad y desaceleracion en la marcha atras
if (Key.isDown(Key.DOWN)) {
if (_root.speeda>-4) {
_root.speeda--;
}
} else {
if (_root.speeda<0) {
_root.speeda = _root.speeda+0.1;
if (_root.speeda>0.5) {
_root.speed1 = 0;
}
}
}
_root.angle1 += _root.angleclav1;
_root.angleclav1 = 0;
}


Expliación del código: En el primer \"if\" vemos que cuando el coche se mueve (_root.speeda>0 || _root.speeda<0), este puede girar a través del Key.isDown, aumentando o reduciendo el angulo (angleclav) en saltos de 4. Podéis modificar este valor para hacer que vaya más rápido o más lento.

El segundo \"if\", sirve para calcular la velocidad que tendrá el coche hacia delante, su aceleración y su deceleración. La velocidad la podemos ver en _root.speeda<8, el coche no podrá de ir más rápido de 7 \"puntos\". Acelerará a un ritmo de 0.5 \"puntos\" (_root.speeda+=0.5), y decelerará cuando no toquemos la flecha arriba (_root.speeda-=0.1). Si os fijais también veréis que cuando llega a 0.5 de velocidad, esta cambia directamente a 0, esto lo he hecho para evitar bucles infinitos y así hacer que cuando llegue a 0 deje de restar, sino estaría restando siempre.

El tercer \"if\", calcula la velocidad en marcha atrás. Básicamente funciona igual que el anterior, sólo que los valores estan ajustados para que vaya mas lento, dado que la marcha atras del coche es mas lenta, que la marcha adelante.

Por último, añadimos a _root.angle1, la cantidad de giro deseada e inicializamos _root.angleclav.



Bien, pues por ahora ya está, ya teneis vuestro coche que se mueve felizmente por la pantalla. Ajustar los fps, yo lo he puesto a 36 y se mueve bastante suave.

Capítulo 2: Colisiones con los bordes

Vamos a hacer que nuestro coche colisione contra las paredes, en el siguiente capítulo crearemos otro coche, y veremos como colisionan entre sí, de una manera un poco rudimentaria, pero efectiva a la hora de hacerlo jugable.

Primero de todo ajusta las medidas de la película, yo como trabajo a 1280x1024 mi lienzo media 900x900, pero tu puedes hacerlo todo lo grande o pequeño que quieras, aunque preferiblemente hazlo grande para luego trabajar mejor cuando hagamos el circuito.

Una vez tengas las medidas adecuadas entra dentro del coche haciendo doble click, ahora tienes que estar en el clip de película interno, que contenía las fórmulas. Añade a lo que había en el evento onClipEvent (enterframe) lo siguiente al final del todo:

//Colisión contra las paredes
//Derecha
if (_parent._x>880) {
_parent._x = 880;
if (_root.speeda>0) {
_root.speeda--;
} else {
_root.speeda++;
}
}
//Izquierda
if (_parent._x<20) {
_parent._x = 20;
if (_root.speeda>0) {
_root.speeda--;
} else {
_root.speeda++;
}
}
//Abajo
if (_parent._y>880) {
_parent._y = 880;
if (_root.speeda>0) {
_root.speeda--;
} else {
_root.speeda++;
}
}
//Arriba
if (_parent._y<20) {
_parent._y = 20;
if (_root.speeda>0) {
_root.speeda--;
} else {
_root.speeda++;
}
}


Explicación del código: Si os fijais cada segmento está claramente marcado con una etiqueta que pone qué detecta cada uno, nosotros tomaremos como ejemplo el de Izquierda.

Básicamente la colisión se produce cuando el coche se acerca a una de las paredes, en este caso izquieda, por lo tanto si mi coche mide 40 pixels de largo, cuando esté a 20 significará que estará tocando la pared (if (_parent._y<20)), entonces lo que hago es que no avance (_parent._y=20). Como penalización, lo que hago es que si toca la pared pierde velocidad, por eso si va hacia adelante (if (_root.speeda>0)) hacemos _root.speeda--, de lo contrario (else) sumamos _root.speeda++.

Capítulo 3: Colisiones con otro vehículo

Para empezar este capítulo necesitamos otro coche igual que el anterior puedes duplicar los clips si lo prefieres, yo por mi parte he hecho uno nuevo y luego he vuelto a hacer los pasos que ya he explicado en los capítulos 1 y 2, pero lo que SÍ ES INDISPENSABLE, es que los nombres de las variables de los vehículos sean diferentes. Si os fijáis todas las variables del coche azul acaban con la letra \"a\" y las del coche rojo no, por ejemplo a _root.speeda yo la he llamado _root.speed en el otro coche.

Una vez hayáis acabado vuestro flamante coche, yo he hecho este flamante deportivo..



..tendréis que hacer los pasos anteriores de los capítulos 1 y 2 y a este nuevo coche llamarlo \"cocherojo\", como nombre de variable.

Ahora dentro del clip del coche rojo, poned debajo del control de colisiones contra los muros, las siguientes acciones:

//Colisión contra el otro coche
with (_root.cocherojo) {
if (_root.cocheazul.hitTest(getBounds(_root).xMax)) {
_root.speed = 0;
}
if (_root.cocheazul.hitTest(getBounds(_root).xMin)) {
_root.speed = 0;
}
if (_root.cocheazul.hitTest(_x, getBounds(_root).yMax)) {
_root.speed = 0;
}
if (_root.cocheazul.hitTest(_x, getBounds(_root).yMin)) {
_root.speed = 0;
}
}


Explicación del código: Con la función with (_root.cocherojo) lo que hacemos es que todo lo que hay entre sus llaves solo afecte al clip cocherojo, no es una función que se utilize en exceso pero va bastante bien. Luego utilizamos el hitTest con getBounds, que sirve para calcular los límites que tiene nuestro clip de película y hacer que si \"cocherojo\" da con uno de los bordes de \"cocheazul\", la velocidad se ponga a 0.

Esto produce un ralentecimiento cuando impactan ambos vehículos, y lo hace de una manera muy sencilla bastante jugable, en un futuro, quizá substituamos este código por uno en el que el coche que vaya más veloz produzca un golpe de una fuerza directamente proporcional a su velocidad a la velocidad del otro, provocando inercias y \"derrapes\"... pero, tranquilos, que de momento no he llegado a ese estado, y tengo que estudiarlo cuidadosamente antes de añadirlo.

Capítulo 4: Seguimiento del circuito

Llegados a este punto tenemos que construir nuestro circuito. He de aclarar que tal y como he hecho el control de seguimiento no me ha acabado de convencer, ya que estuve dándole vueltas y no di con una solución mejor que la que hay en este juego.

Lo que vamos a hacer es lo siguiente. Primero dibujaremos los diferentes tramos de la pista, es decir, primero la recta, luego un giro a la derecha, otro a la izquierda, etc... cada uno de estos tramos los convertiremos en un clip de película que contendrá las siguientes acciones:

onClipEvent (load) {
fina = 0;
finr = 0;
}
onClipEvent (enterFrame) {
if (this, hitTest(_root.cocheazul)) {
if (fina == 0) {
_root.ok1 += 1;
fina = 1;
}
}
if (this, hitTest(_root.cocherojo)) {
if (finr == 0) {
_root.ok2 += 1;
finr = 1;
}
}
}


También tendremos que dibujar una meta que contendrá estas acciones:

onClipEvent (load) {
nv1 = 0;
nv2 = 0;
_root.ok1 = 0;
_root.ok2 = 0;
}
onClipEvent (enterFrame) {
// Llegada a meta del coche azul
if (this, hitTest(_root.cocheazul) && _root.ok1 == 11) {
nv1 = nv1+1;
_root.jugador1 = nv1+\"ª Vuelta\";
_root.ok1 = 0;
_root.t1.fina = 0;
_root.t2.fina = 0;
_root.t3.fina = 0;
_root.t4.fina = 0;
_root.t5.fina = 0;
_root.t6.fina = 0;
_root.t7.fina = 0;
_root.t8.fina = 0;
_root.t9.fina = 0;
_root.t10.fina = 0;
_root.t11.fina = 0;
}
// Llegada a meta del coche rojo
if (this, hitTest(_root.cocherojo) && _root.ok2 == 11) {
nv2 = nv2+1;
_root.jugador2 = nv2+\"ª Vuelta\";
_root.ok2 = 0;
_root.t1.finr = 0;
_root.t2.finr = 0;
_root.t3.finr = 0;
_root.t4.finr = 0;
_root.t5.finr = 0;
_root.t6.finr = 0;
_root.t7.finr = 0;
_root.t8.finr = 0;
_root.t9.finr = 0;
_root.t10.finr = 0;
_root.t11.finr = 0;
}
}


Explicación del código: Primero podemos ver que en cada tramo comprobamos mediante una variable interna llamada \"fina\". Cuando el coche toca el tramo, si fina es igual a 0 aumentamos en 1 ok1. En el momento que toquemos la meta si has pasado por todos los tramos ok1 tiene que valer 11, por lo tanto, sumaremos una nueva vuelta y pondremos a 0 otra vez las variables \"fina\" de todos los tramos. En el código del tramo fijaos que es muy importante poner \"fina\" en 1 para que no haya un \"tramposo\" que se dedique a pasar 11 veces por un mismo tramo y no siga el circuito.

Es una manera poco ortodoxa de hacer que sigan el circuito, aunque es igual de efectivo.