En este tutorial vamos aprender como hacer el clásico juego Snake, donde una Serpiente crecía cada vez que se comía una manzana (un punto negro), supongo que este juego lo conocemos todos.
Se utilizara el elemento Canvas que nos brinda HTML5 y jQuery.
Crear la estructura HTML y el Canvas
Esta será la estructura de nuestro HTML.
<!doctype html>
<html>
<head>
<title>Juego Snake</title>
<script src="jquery.js" type="text/javascript"></script>
<script src="snake.js" type="text/javascript"></script>
</head>
<body>
<canvas id="pantalla"></canvas>
</body>
</html>
Y para comenzar con el JavaScript quien será que hará toda la programación solo almacenaremos en una variable el canvas, su contexto que es donde dibujaremos y haremos toda la magia, y le daremos un tamaño de 400px de ancho, 400px de alto.
$(document).ready(function(){
//elemento jQuery del canvas
var $canvas = $('#pantalla');
//elemento DOM del canvas
var canvas = $canvas[0];
//dar dimensiones al canvas
$canvas.width(400);
$canvas.height(400);
//obtener el contexto
var ctx = canvas.getContext('2d');
});
Probemos si el canvas funciona dandole un color de fondo, agregamos la siguientes lineas después de
var ctx = canvas.getContext('2d'); solo para ver si hasta el momento vamos bien.ctx.fillStyle = '#fea';
ctx.fillRect(0, 0, 400, 400);
Esto nos debería mostrar un fondo amarillento en un recuadro 400×400 pixeles.
Vamos hacer que se mueva la serpiente
ya que sabemos que funciona el canvas, vamos a crear el juego en si, comenzando por hace que se mueva.
Pero antes vamos a acomodar el código desde ahora en un objeto de esta manera.
var Snake = (function(){
//propiedades
var $canvas = undefined;
var canvas = undefined;
var ctx = undefined;
function iniciar_canvas()
{
//elemento jQuery del canvas
$canvas = $('#pantalla');
//elemento DOM del canvas
canvas = $canvas[0];
//dar dimensiones al canvas
$canvas.prop('width', '400');
$canvas.prop('height', '400');
//obtener el contexto
ctx = canvas.getContext('2d');
}
})();
Así tenemos el canvas y el contexto en nuestro propio objeto o área donde no interferirá con otras variables que podrían existir y la podemos acceder de cualquier otra funciona que pertenezca a Snakeque es el encargado de todo el juego.
Definamos 4 propiedades mas para saber la posición, el color y el ancho de nuestra serpiente.
var posicion_x = 0;
var posicion_y = 0;
var color_serpiente = '#000000'; //color negro en hexadecimal
var ancho_serpiente = 10;
Definamos un nuevo método llamado iniciar_juego, el que se encargara de iniciar todo para el juego, por el momento solo iniciara las propiedades de posición,
Snake.posicion_x y Snake.posicion_y y finalmente llamara el método principal ciclo, este se ejecutara dentro de iniciar_canvas.function iniciar_juego()
{
//iniciar propiedades
posicion_x = 50;
posicion_x = 30;
//iniciar juego
ciclo();
}
Definamos otro método llamado ciclo será nuestro método principal el cual se encargara de dibujar y hacer que todo se mueva, aunque tal vez no directamente todo pase en este método.
function ciclo()
{
ctx.fillStyle = color_serpiente;
ctx.fillRect(posicion_x, posicion_y, ancho_serpiente, ancho_serpiente);
posicion_y += ancho_serpiente;
posicion_x += ancho_serpiente;
setTimeout(ciclo, 200);
}
Cada 0.2segundos (200 milisegundos) (5 frame por segundos) volvemos a ejecutar la función ciclo y esto creara el movimiento de nuestra serpiente. Pero si hasta ahora han estado intentando solo nos mostrara una linea como la imagen que esta debajo en vez de moverse parece que esta dibujando una linea en diagonal.
Hasta ahora para poder obtener lo que vemos en la imagen de arriba, tenemos el código siguiente
var Snake = (function(){
//propiedades
var $canvas = undefined;
var canvas = undefined;
var ctx = undefined;
var posicion_x = 0;
var posicion_y = 0;
var color_serpiente = '#000000'; //color negro en hexadecimal
var ancho_serpiente = 10;
function iniciar_canvas()
{
//elemento jQuery del canvas
$canvas = $('#pantalla');
//elemento DOM del canvas
canvas = $canvas[0];
//dar dimensiones al canvas
$canvas.prop('width', '400');
$canvas.prop('height', '400');
//obtener el contexto
ctx = canvas.getContext('2d');
iniciar_juego();
}
function ciclo()
{
ctx.fillStyle = color_serpiente;
ctx.fillRect(posicion_x, posicion_y, ancho_serpiente, ancho_serpiente);
posicion_y += ancho_serpiente;
posicion_x += ancho_serpiente;
setTimeout(ciclo, 200);
}
return {
iniciar_canvas: iniciar_canvas
};
})();
//cuando el documento este cargado completamente ejecutar iniciar_canvas
$(document).ready(Snake.iniciar_canvas);
Para esto debemos “limpiar” nuestro canvas antes de volver a dibujar y re-posicionar nuestra serpiente, pero definamos nuestro color de fondo en este momento, con el clásico verde o algo parecido a ese verde como propiedad.
var color_fondo = '#A9CB95';
function ciclo()
{
//limpiar pantalla
ctx.fillStyle = color_fondo;
ctx.fillRect(0, 0, 400, 400);
//dibujar serpiente
ctx.fillStyle = color_serpiente;
ctx.fillRect(posicion_x, posicion_y, ancho_serpiente, ancho_serpiente);
posicion_y += ancho_serpiente;
posicion_x += ancho_serpiente;
setTimeout(ciclo, 200);
}
La Serpiente
Ahora si vemos ver nuestra serpiente moverse diagonalmente en vez de dibujar una linea diagonal.
Vamos a mantener todo relacionado a la serpiente en si en un objeto, asi que si a volver a modificar un poco el codigo.
Crearemos otra propiedad llamada
serpientevar serpiente;
serpiente = function()
{
}
Y movemos todas las propiedades y metodo relacionado a este “objeto”.
function serpiente()
{
var posicion_x = 0;
var posicion_y = 0;
var color_serpiente = '#000000'; //color negro en hexadecimal
}
No movemos
ancho_serpiente porque le vamos a cambiar el nombre a ancho_cuadro ya que sera el mismo ancho de la comida y para mantener el valor en una sola variable se cambiara el nombre.
Agregaremos dos propiedades mas a la serpiente:
var cuerpo = [];
var tamano_inicial = 3;
Este tamaño inicial indica el tamaño inicial de la serpiente, digamos fuera de codigo que es el largo de la serpiente, la cantidad de cuadrito que tendra y el cuerpo sera un array conteniendo cada “cuadrito” de su cuerpo.
Entonces ya que estamos moviendo todo lo relacionado a la serpiente, moveremos el codigo que dibujaba la serpiente y lo llamaremos dibujar.
this.dibujar = function()
{
//dibujar serpiente
ctx.fillStyle = color_serpiente;
ctx.fillRect(posicion_x, posicion_y, ancho_cuadrado, ancho_cuadrado);
}
Crear otro metodo para colocar su posición
this.colocar = function(x, y)
{
posicion_x = x;
posicion_y = y;
}
Ahora crearemos nuestro objeto serpiente y asignarle la posición en el metodo iniciar_juego, ahora este metodo se vera de esta forma:
function iniciar_juego()
{
serpiente = new serpiente();
//iniciar posicion
serpiente.colocar(50, 50);
//iniciar juego
ciclo();
}
Entonces para mover la serpiente debemos sumarle el ancho del cuadro como soliamos hacer anteriormente, pero ahora no podemos acceder a la propiedad posicion_y ni posicion_x, pero total olvidemos eso ya que debemos crear un método que se llame mover el cual se encargara de mover la serpiente, ya que de por si ella comienza a moverse sola y solo el jugador elige en que dirección, pero siempre se estará moviendo.
function ciclo()
{
//limpiar pantalla
ctx.fillStyle = color_fondo;
ctx.fillRect(0, 0, 400, 400);
serpiente.dibujar();
serpiente.mover();
setTimeout(ciclo, 200);
}
En la serpiente tendremos el método mover que por ahora simplemente tendremos una linea de código
this.mover = function()
{
posicion_x += ancho_cuadro;
}
Ahora tenemos la serpiente moviendose sola hacia la derecha, así comenzara el juego con la serpiente moviendose a la derecha, pero el es un simple cuadrito y este juego empieza con una serpiente de 3 cuadritos de largo lo cual hemos definido 2 variables anteriormente la cual manejara esto.
var cuerpo = [];
var tamano_inicial = 3;
Modificaremos 3 método las cuales son
mover, colocar y dibujar, a continuación lo veremos como van a quedar cada uno.this.colocar = function(x, y)
{
posicion_x = x;
posicion_y = y;
for(i=0;i<tamano_inicial;i++)
cuerpo.push([x - ( i * ancho_cuadro ), y]);
}
this.dibujar = function()
{
//dibujar serpiente
ctx.fillStyle = color_serpiente;
for(i=0;i<cuerpo.length;i++)
ctx.fillRect(cuerpo[i][0], cuerpo[i][1], ancho_cuadro, ancho_cuadro);
}
this.mover = function()
{
for(i=0;i<cuerpo.length;i++)
cuerpo[i][0] += ancho_cuadro;
}
Cuando coloquemos por primera vez nuestra serpiente vamos agregarle a su cuerpo 3 cuadritos, en la variable cuerpo que es un array que almacenara las posiciones de cada “cuadritos” que forman el cuerpo, al dibujar pues recorremos el array cuerpo para dibujar uno por uno cada parte del cuerpo, al igual en mover movemos cada parte una por una.
Con jQuery vamos a manejar los eventos de cuanto se teclea arriba, abajo, delante o atrás de esta manera.
var Direccion = {
'38': 'arriba',
'39': 'derecha',
'40': 'abajo',
'37': 'izquierda'
};
$(document).keydown(function(e){
var tecla = e.which;
if ( Direccion[ tecla ] )
serpiente.cambiarDireccion( Direccion[ tecla ] );
});
Direcciones contiene los valores en numero de la tecla arriba=38, derecha=39, etc. Cuando se presione una tecla, y esa tecla pertenece a una Dirección (si es una tecla permitida) pues la cambiamos con el método
cambiarDireccion (aun no creado todavía).
El método
cambiarDireccion se encargada como si nombre le dice de cambiar la dirección de la serpiente.
Al objeto serpiente le crearemos otra propiedad llamada
direccion que almacenara en que dirección actualmente se dirige, por defecto será hacia la derecha.var direccion = 'derecha';
this.cambiarDireccion = function(nuevaDireccion)
{
direccion = nuevaDireccion;
}
Cambiaremos nuestro método
mover ya que ahora no solo se moverá a la derecha si no que dependerá a que dirección lo este haciendo, por lo tanto nuestro método mover quedara de esta manera.this.mover = function()
{
var nuevaPosicion = cuerpo[0].slice();
switch( direccion )
{
case 'arriba':
nuevaPosicion[1] -= ancho_cuadro;
break;
case 'derecha':
nuevaPosicion[0] += ancho_cuadro;
break;
case 'abajo':
nuevaPosicion[1] += ancho_cuadro;
break;
case 'izquierda':
nuevaPosicion[0] -= ancho_cuadro;
break;
}
cuerpo.unshift(nuevaPosicion);
cuerpo.pop();
}
La nueva posición la determinaremos dependiendo de la posición actual de la cabeza y según su la dirección es donde se moverá, si aumentaremos su posición en x o y, o la aumentaremos. Después la nueva posición la entramos en el cuerpo como cabeza y la cola la sacamos.
Ah! pero no nos olvidemos de prohibir cambiar la dirección de nuestra serpiente a la contraria a la que se dirige, por lo tanto cambiemos el método
cambiarDireccion:this.cambiarDireccion = function(nuevaDireccion)
{
if ( direccion == 'arriba' || direccion == 'abajo' )
var direccionesPermitidas = ['derecha', 'izquierda']
else
var direccionesPermitidas = ['arriba', 'abajo'];
if ( direccionesPermitidas.indexOf(nuevaDireccion) >= 0 )
direccion = nuevaDireccion;
}
Colocar Comida
Tenemos que crear un objeto que maneje la comida que la serpiente tiene que comerse, el objeto será como esta continuación.
//objeto para la comida
function comida()
{
var posicion_x = 0;
var posicion_y = 0;
var color_comida = '#000000';
var posicionAleatoria = [];
this.colocar = function()
{
posicionAleatoria = this.conseguirPosicion();
while ( serpiente.obtenerCuerpo().some(this.posicionOcupada) )
{
posicionAleatoria = this.conseguirPosicion();
}
posicion_x = posicionAleatoria[0];
posicion_y = posicionAleatoria[1];
}
this.mostrar = function()
{
ctx.fillStyle = color_comida;
ctx.fillRect(posicion_x, posicion_y, ancho_cuadro, ancho_cuadro);
}
this.conseguirPosicion = function()
{
var X = Math.floor(Math.random()*($canvas.width()/ancho_cuadro))*ancho_cuadro;
var Y = Math.floor(Math.random()*($canvas.height()/ancho_cuadro))*ancho_cuadro;
return [X, Y];
}
this.posicionOcupada = function(element)
{
return ( element[0] == posicionAleatoria[0] && element[1] == posicionAleatoria[1] );
}
}
Recuerda que también tenemos que crear una variable llamada comida que almacenara este objeto.
posicionAleatoria almacenaremos la posición que generaremos aleatoriamente, y el métodoconseguirPosicion es la encargada de generar esta posiciones aleatoria, mientras queposicionOcupada verifica si esta posición no colisiona con una posición de la serpiente.
Ya que tenemos el método para posicionar la comida debemos crear un método que verifique si la serpiente se ha comido (colisiona) con la comida.
this.colision = function(x, y)
{
if ( posicion_x == x && posicion_y == y )
return true;
}
Modificaremos el método mover de la serpiente para verificar esta colisión.
this.mover = function()
{
var nuevaPosicion = cuerpo[0].slice();
switch( direccion )
{
case 'arriba':
nuevaPosicion[1] -= ancho_cuadro;
break;
case 'derecha':
nuevaPosicion[0] += ancho_cuadro;
break;
case 'abajo':
nuevaPosicion[1] += ancho_cuadro;
break;
case 'izquierda':
nuevaPosicion[0] -= ancho_cuadro;
break;
}
cuerpo.unshift(nuevaPosicion);
cuerpo.pop();
if ( comida.colision(nuevaPosicion[0], nuevaPosicion[1]) )
{
comida.colocar();
var cola = cuerpo[cuerpo.length-1].slice();
cuerpo.push(cola);
}
}
No podemos permitir que se coma así misma ni tampoco choque con los bordes, esto significa que el juego ha terminado. Estos métodos se utilizara para comprobar la colisión.
this.comprobarColisiones = function()
{
var cabeza = cuerpo[0].slice();
var resto = cuerpo.slice(1);
var fueraHorizontalmente = cabeza[0] > ($canvas.width() - ancho_cuadro) || cabeza[0] < 0;
var fueraVerticalmente = cabeza[1] > ($canvas.height() - ancho_cuadro) || cabeza[1] < 0;
if ( fueraHorizontalmente || fueraVerticalmente )
return true;
if ( resto.some(this.seComioAsiMisma) )
return true;
return false;
}
this.seComioAsiMisma = function(element, index, array)
{
var cabeza = cuerpo[0].slice();
if ( element[0] == cabeza[0] && element[1] == cabeza[1] )
return true;
return false;
}
Y Ya tenemos todo, un juego básico y simple, le di unos últimos toques y el código final es el siguiente.
Código Final
HTML
<!doctype html>
<html>
<head>
<title>Juego Snake</title>
<script src="jquery.js" type="text/javascript"></script>
<script src="snake.js" type="text/javascript"></script>
</head>
<body>
<canvas id="pantalla"></canvas>
</body>
</html>
JavaScript
var Snake = (function(){
//propiedades
var $canvas = undefined;
var canvas = undefined;
var ctx = undefined;
var fps = 5;
var intervalo;
var es_fin_juego = false;
var color_fondo = '#A9CB95';
var ancho_cuadro = 10;
var serpiente;
var comida;
//objeto para la comida
function comida()
{
var posicion_x = 0;
var posicion_y = 0;
var color_comida = '#000000';
var posicionAleatoria = [];
this.colocar = function()
{
posicionAleatoria = this.conseguirPosicion();
while ( serpiente.obtenerCuerpo().some(this.posicionOcupada) )
{
posicionAleatoria = this.conseguirPosicion();
}
posicion_x = posicionAleatoria[0];
posicion_y = posicionAleatoria[1];
}
this.colision = function(x, y)
{
if ( posicion_x == x && posicion_y == y )
return true;
}
this.mostrar = function()
{
ctx.fillStyle = color_comida;
ctx.fillRect(posicion_x, posicion_y, ancho_cuadro, ancho_cuadro);
}
this.conseguirPosicion = function()
{
var X = Math.floor(Math.random()*($canvas.width()/ancho_cuadro))*ancho_cuadro;
var Y = Math.floor(Math.random()*($canvas.height()/ancho_cuadro))*ancho_cuadro;
return [X, Y];
}
this.posicionOcupada = function(element)
{
return ( element[0] == posicionAleatoria[0] && element[1] == posicionAleatoria[1] );
}
}
//objeto para la serpiente
function serpiente()
{
var posicion_x = 0;
var posicion_y = 0;
var color_serpiente = '#000000'; //color negro en hexadecimal
var direccion = 'derecha';
var cuerpo = [];
var tamano_inicial = 3;
this.obtenerCuerpo = function()
{
return cuerpo;
}
this.dibujar = function()
{
//dibujar serpiente
ctx.fillStyle = color_serpiente;
for(i=0;i<cuerpo.length;i++)
ctx.fillRect(cuerpo[i][0], cuerpo[i][1], ancho_cuadro, ancho_cuadro);
}
this.colocar = function(x, y)
{
posicion_x = x;
posicion_y = y;
cuerpo = [];
direccion = 'derecha';
for(i=0;i<tamano_inicial;i++)
cuerpo.push([x - ( i * ancho_cuadro ), y]);
}
this.mover = function()
{
if ( this.comprobarColisiones() )
fin_juego();
var nuevaPosicion = cuerpo[0].slice();
switch( direccion )
{
case 'arriba':
nuevaPosicion[1] -= ancho_cuadro;
break;
case 'derecha':
nuevaPosicion[0] += ancho_cuadro;
break;
case 'abajo':
nuevaPosicion[1] += ancho_cuadro;
break;
case 'izquierda':
nuevaPosicion[0] -= ancho_cuadro;
break;
}
cuerpo.unshift(nuevaPosicion);
cuerpo.pop();
if ( comida.colision(nuevaPosicion[0], nuevaPosicion[1]) )
{
comida.colocar();
var cola = cuerpo[cuerpo.length-1].slice();
cuerpo.push(cola);
}
}
this.comprobarColisiones = function()
{
var cabeza = cuerpo[0].slice();
var resto = cuerpo.slice(1);
var fueraHorizontalmente = cabeza[0] > ($canvas.width() - ancho_cuadro) || cabeza[0] < 0;
var fueraVerticalmente = cabeza[1] > ($canvas.height() - ancho_cuadro) || cabeza[1] < 0;
if ( fueraHorizontalmente || fueraVerticalmente )
return true;
if ( resto.some(this.seComioAsiMisma) )
return true;
return false;
}
this.seComioAsiMisma = function(element, index, array)
{
var cabeza = cuerpo[0].slice();
if ( element[0] == cabeza[0] && element[1] == cabeza[1] )
return true;
return false;
}
this.cambiarDireccion = function(nuevaDireccion)
{
if ( direccion == 'arriba' || direccion == 'abajo' )
var direccionesPermitidas = ['derecha', 'izquierda']
else
var direccionesPermitidas = ['arriba', 'abajo'];
if ( direccionesPermitidas.indexOf(nuevaDireccion) >= 0 )
direccion = nuevaDireccion;
}
}
function iniciar_canvas()
{
//elemento jQuery del canvas
$canvas = $('#pantalla');
//elemento DOM del canvas
canvas = $canvas[0];
//dar dimensiones al canvas
$canvas.prop('width', '400');
$canvas.prop('height', '400');
//obtener el contexto
ctx = canvas.getContext('2d');
ctx.fillStyle = '#fea';
ctx.fillRect(0, 0, 400, 400);
iniciar_juego();
}
function iniciar_juego()
{
es_fin_juego = false;
serpiente = new serpiente();
comida = new comida();
//iniciar propiedades
serpiente.colocar(50, 50);
comida.colocar();
//iniciar juego
ciclo();
}
function fin_juego()
{
es_fin_juego = true;
clearTimeout(intervalo);
var x = $canvas.width() / 2;
var y = $canvas.height() / 2;
ctx.font = '30px Georgia';
ctx.textAlign = 'center';
ctx.fillStyle = '#F00';
ctx.fillText('Fin del Juego!', x, y);
ctx.font = '20px Georgia';
ctx.fillStyle = '#000';
ctx.fillText('Presiona R, para reiniciar', x, y + 30);
}
function ciclo()
{
//limpiar pantalla
ctx.fillStyle = color_fondo;
ctx.fillRect(0, 0, 400, 400);
serpiente.dibujar();
comida.mostrar();
serpiente.mover();
if ( ! es_fin_juego )
intervalo = setTimeout(ciclo, 1000 / fps);
}
function reiniciar()
{
es_fin_juego = false;
//iniciar propiedades
serpiente.colocar(50, 50);
comida.colocar();
//iniciar juego
ciclo();
}
var Direccion = {
'38': 'arriba',
'39': 'derecha',
'40': 'abajo',
'37': 'izquierda'
};
$(document).keydown(function(e){
var tecla = e.which;
if ( Direccion[ tecla ] )
serpiente.cambiarDireccion( Direccion[ tecla ] );
if ( tecla == 82 )// tecla R
reiniciar();
});
return {
iniciar_canvas: iniciar_canvas
};
})();
$(document).ready(Snake.iniciar_canvas);
Espero que le haya servido de ayuda es algo sencillo, pero de apoco se aprende.
El código lo pueden encontrar en Github también Snake-Classico-Tutorial-Fuente

0 comentarios:
Publicar un comentario