Aprende a crear juegos en HTML5 Canvas

lunes, 6 de febrero de 2012

Parte 4. Interactuando con otros elementos.

Además de poder interactuar nosotros con el juego, el segundo punto importante de un juego es que los elementos puedan interactuar entre si. Para saber si dos elementos “se están tocando” (Es decir, hay una intersección entre ellos), no solo nos basta saber su posición XY, también necesitamos conocer el alto y ancho de los elementos.

En Javascript, las funciones cumplen una doble función. No solo te permiten crear eventos que pueden ser llamados en cualquier momento (como ya hemos visto), si no que además pueden hacer la función de objetos (Esto en otros lenguajes suele ser conocido como “clases”). Por ejemplo, crearemos nuestra propio objeto “rectángulo”, que contendrá una posición en X, en Y, además de un ancho y alto.

Además, las funciones de tipo objeto pueden contener sus propias funciones. Así pues, agregaremos al rectángulo una función “intersección”, que nos dirá si está en una intersección con un segundo elemento, así como una función que rellene de forma más sencilla el rectángulo. Para conocer más a detalle sobre este tema, lee el Apéndice 3: Programación Orientada a Objetos; por ahora, tan solo copiemos al final de nuestro código, la siguiente función:
function Rectangle(x, y, width, height) {
    this.x = (x == null) ? 0 : x;
    this.y = (y == null) ? 0 : y;
    this.width = (width == null) ? 0 : width;
    this.height = (height == null) ? this.width : height;

    this.intersects = function (rect) {
        if (rect == null) {
            window.console.warn('Missing parameters on function intersects');
        } else {
            return (this.x < rect.x + rect.width &&
                this.x + this.width > rect.x &&
                this.y < rect.y + rect.height &&
                this.y + this.height > rect.y);
        }
    };

    this.fill = function (ctx) {
        if (ctx == null) {
            window.console.warn('Missing parameters on function fill');
        } else {
            ctx.fillRect(this.x, this.y, this.width, this.height);
        }
    };
}
Como podremos ver, la función Rectángulo está diseñada para recibir las cuatro variables en el orden X, Y, Ancho y Alto. Si omitimos enviar alguna, esta automáticamente se volverá “0” para evitar errores, excepto en el caso de la Altura, que automáticamente obtendrá el valor del Ancho (Lo que es práctico para omitir enviar ancho y alto si queremos que ambos tengan el mismo valor).

Ahora haremos unos cambios a nuestro código. Primero eliminaremos las variables “x” y “y” que declaramos en un comienzo, y en su lugar, crearemos una variable “player” nula:
var player = null;
Al cual asignaremos un valor de nuestro nuevo tipo rectángulo dentro de la función “init”:
    // Create player
    player = new Rectangle(40, 40, 10, 10);
Después, cambiaríamos la forma en que se dibuja el rectángulo a la siguiente forma:
    player.fill(ctx);
Por último, y hay que tener mucho cuidado en esto, será que todas nuestras variables “x” y “y” que usamos antes las convertiremos agregando “player.” antes de ellas, de igual forma que hicimos con la forma en que dibujamos el rectángulo.

Ahora, necesitaremos un nuevo elemento con el cual interactuar. Crearemos una nueva variable al que asignaremos un valor tipo rectángulo llamada “food”:
    food = new Rectangle(80, 80, 10, 10);
De igual forma, la dibujaremos, solo que esta la haremos de color rojo:
    // Draw food
    ctx.fillStyle = '#f00';
    food.fill(ctx);
Ahora, analizaremos si ambos están en una intersección. En dado caso, agregaremos un punto a nuestro puntaje, y cambiaremos la posición de la comida a otro lugar al azar. Para ello, primero tendremos que declarar nuestro puntaje:
var score = 0;
También agregaremos esta función que nos será muy útil para facilitar el uso de números enteros al azar:
function random(max) {
    return Math.floor(Math.random() * max);
}
Ahora si, en la función "act", después de mover a nuestro jugador, compararemos si ambos elementos están en una intersección, y de ser así, agregaremos un punto más y cambiaremos de posición la comida:
        // Food Intersects
        if (player.intersects(food)) {
            score += 1;
            food.x = random(canvas.width / 10 - 1) * 10;
            food.y = random(canvas.height / 10 - 1) * 10;
        }
La pequeña ecuación de dividir la pantalla entre 10 dentro del random y multiplicarla al final de nuevo, hace que la comida aparezca en un lugar cada 10 pixeles, de esta forma se ajustará “a la rejilla”. Por último, dibujaremos nuestro puntuaje en pantalla:
    // Draw score
    ctx.fillText('Score: ' + score, 0, 10);
Guardemos y probemos el código. Ahora cada vez que el rectángulo verde toque al rojo, el puntaje subirá.

Código final:

[Canvas not supported by your browser]
var KEY_ENTER = 13,
    KEY_LEFT = 37,
    KEY_UP = 38,
    KEY_RIGHT = 39,
    KEY_DOWN = 40,
    
    canvas = null,
    ctx = null,
    lastPress = null,
    pause = true,
    dir = 0,
    score = 0,
    player = null,
    food = null;

window.requestAnimationFrame = (function () {
    return window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        function (callback) {
            window.setTimeout(callback, 17);
        };
}());

document.addEventListener('keydown', function (evt) {
    lastPress = evt.which;
}, false);

function Rectangle(x, y, width, height) {
    this.x = (x == null) ? 0 : x;
    this.y = (y == null) ? 0 : y;
    this.width = (width == null) ? 0 : width;
    this.height = (height == null) ? this.width : height;

    this.intersects = function (rect) {
        if (rect == null) {
            window.console.warn('Missing parameters on function intersects');
        } else {
            return (this.x < rect.x + rect.width &&
                this.x + this.width > rect.x &&
                this.y < rect.y + rect.height &&
                this.y + this.height > rect.y);
        }
    };

    this.fill = function (ctx) {
        if (ctx == null) {
            window.console.warn('Missing parameters on function fill');
        } else {
            ctx.fillRect(this.x, this.y, this.width, this.height);
        }
    };
}

function random(max) {
    return Math.floor(Math.random() * max);
}

function paint(ctx) {
    // Clean canvas
    ctx.fillStyle = '#000';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    
    // Draw player
    ctx.fillStyle = '#0f0';
    player.fill(ctx);
    
    // Draw food
    ctx.fillStyle = '#f00';
    food.fill(ctx);

    // Debug last key pressed
    ctx.fillStyle = '#fff';
    //ctx.fillText('Last Press: '+lastPress,0,20);
    
    // Draw score
    ctx.fillText('Score: ' + score, 0, 10);
    
    // Draw pause
    if (pause) {
        ctx.textAlign = 'center';
        ctx.fillText('PAUSE', 150, 75);
        ctx.textAlign = 'left';
    }
}

function act() {
    if (!pause) {
        // Change Direction
        if (lastPress == KEY_UP) {
            dir = 0;
        }
        if (lastPress == KEY_RIGHT) {
            dir = 1;
        }
        if (lastPress == KEY_DOWN) {
            dir = 2;
        }
        if (lastPress == KEY_LEFT) {
            dir = 3;
        }

        // Move Rect
        if (dir == 0) {
            player.y -= 10;
        }
        if (dir == 1) {
            player.x += 10;
        }
        if (dir == 2) {
            player.y += 10;
        }
        if (dir == 3) {
            player.x -= 10;
        }

        // Out Screen
        if (player.x > canvas.width) {
            player.x = 0;
        }
        if (player.y > canvas.height) {
            player.y = 0;
        }
        if (player.x < 0) {
            player.x = canvas.width;
        }
        if (player.y < 0) {
            player.y = canvas.height;
        }

        // Food Intersects
        if (player.intersects(food)) {
            score += 1;
            food.x = random(canvas.width / 10 - 1) * 10;
            food.y = random(canvas.height / 10 - 1) * 10;
        }
    }
    
    // Pause/Unpause
    if (lastPress == KEY_ENTER) {
        pause = !pause;
        lastPress = null;
    }
}

function repaint() {
    window.requestAnimationFrame(repaint);
    paint(ctx);
}

function run() {
    setTimeout(run, 50);
    act();
}

function init() {
    // Get canvas and context
    canvas = document.getElementById('canvas');
    ctx = canvas.getContext('2d');
    
    // Create player and food
    player = new Rectangle(40, 40, 10, 10);
    food = new Rectangle(80, 80, 10, 10);
    
    // Start game
    run();
    repaint();
}

window.addEventListener('load', init, false);

60 comentarios:

  1. muy buena explicación, la verdad se entiende :D asique gracias espero q sigan con estos tutoriales!

    ResponderEliminar
  2. la siguiente ecuación para el random: food.x=random(canvas.width/10-1)*10; tiende a poner el food fuera del lienzo mientras que en:
    food.x=random(canvas.width/10)*10; esta perfecto.
    la primera ecuación la pones el código, la segunda la pones en la explicación, gracias por la info.

    ResponderEliminar
    Respuestas
    1. Que extraño, llevo jugando un largo rato, por 150 puntos, y no he podido reproducir el error que me dices... ¿Estás seguro de esto?

      PD: El segundo código no lo he encontrado tampoco en la explicación...

      Eliminar
    2. //--La pequeña ecuación de dividir la pantalla entre 10 dentro del random y multiplicarla al final de nuevo, hace que la comida aparezca en un lugar cada 10 pixeles, de esta forma se ajustará “a la rejilla”. Por último, dibujaremos nuestro puntuaje en pantalla:--//
      o por lo menos te olvidaste de mencionar la resta de 1 :), cuando leí la explicación lo probé inmediatamente sin la resta del 1, antes que se me ocurriera por cuenta propia :)

      Eliminar
    3. ¡Ya veo lo que está ocurriendo! (Y por qué ahora no ocurre el error de hace tiempo).

      Originalmente, este tutorial fue creado para Applets de Java la década pasada, antes de la existencia de HTML5. el ParseInt de Java, redondea el valor de random, dando un valor entre 1 y 30, que al multiplicarse por 10, da de 10 a 300.

      300 es el valor del ancho, por lo que si obtiene este valor la comida, aparecerá ya en el borde externo de la pantalla, por lo que no se vería dentro del juego. Al restar -1, no solo se evitaba este problema, si no que además dando un valor entre 0 y 29, permitía que apareciera desde el mero comienzo de la pantalla (Aunque esto a veces hace que aparezca incluso detrás del contador de puntos).

      Sin embargo, en JavaScript, ParseInt *trunca* el valor en lugar de redondearlo, por lo que aun al obtener 29.999, el máximo valor sigue siendo 29, y por eso no desaparece la comida de la pantalla, aun sin hacer lo antes indicado. Por eso, el incluirlo o no, no afecta demasiado dentro de este código.

      Es cuestión de ver lo que conviene más dependiendo lo que uno desarrolla. Gracias por hacer notar este punto ;)

      Eliminar
    4. dirás un valor entre 1 y 29, porque si coge el 0, lo resta -1, y lo multiplica por 10 y ai si que saldra hasta del lienzo del navegador, jaja, con tu ultimo comentario entendía a la perfección la ecuación

      Eliminar
    5. Obtiene el valor entre 0 y 29 después de restarle -1, ya que originalmente obtiene un valor entre 1 y 30. Lo importante es que ya quedó claro ;)

      Eliminar
  3. como creo un objeto de tipo circulo?

    ResponderEliminar
    Respuestas
    1. Eso se ve en la entrada futura localizada en http://juegoscanvas.blogspot.com/2012/06/distancia-entre-circulos.html

      Eliminar
  4. me gustaría saber que hace esta funcion y que retorna :/ , para que lo aplicas

    this.intersects=function(rect){
    if(rect!=null){
    return(this.xrect.x&&
    this.yrect.y);
    }
    }

    ResponderEliminar
    Respuestas
    1. Puede que no lo haya explicado muy claro. Esta función evaluar si dos rectángulos estan en intersección (Si estan chocando). Se manda a llamar cada vez que se desea saber si dos objetos se estan tocando entre sí.

      Espero ya quede comprendido :)

      Eliminar
  5. podrias explicar mejor la funcion de intersección por favor, no me queda claro para que sumar las dimensiones. No seria suficiente comparando las coordenadas de los 2 objetos??

    PD.
    Gracias por el tutorial me ha servido de mucho

    ResponderEliminar
    Respuestas
    1. En el caso especifico de este juego, comparar solo las coordenadas sería suficiente, ya que los movimientos de nuestros objetos son exactos. Pero en caso de juegos futuros, donde el movimiento no es perfecto (Como se verá en entradas futuras), las coordenadas de ambos objetos no serán exactamente iguales, y por tanto, para saber si hacen intersección, es necesaria esta función. Puedes probarlo por tu cuenta para comprobarlo.

      Eliminar
  6. No etiendo que significa esto


    this.x=(x==null)?0:x;

    podrias explicarme por favor?

    ResponderEliminar
    Respuestas
    1. tampoco entiendo la lógica de esta función :(

      this.intersects=function(rect){
      if(rect!=null){
      return(this.xrect.x&&
      this.yrect.y);
      }

      Eliminar
    2. El primero, se asegura que haya un valor en X, y en caso de no haberlo, lo convierte en un 0 para que no de errores.

      El segundo, compara si los dos elementos están en intersección entre ellos, revisando las áreas de ambos respecto a sus posiciones.

      Eliminar
    3. Antes yo tampoco sabía que hacía el código de Elkin hasta que vi que el problema era mio...no tenía ni idea de POO en JS, vamos sólo con decir que no sabía que existiese la POO también en este lenguaje ajaj xD os podéis reir de mi ^^

      Como varía algo de Java me vi este tuto y resolvió todas mis dudas.

      http://lineadecodigo.com/javascript/clase-javascript-con-metodos-privados/

      Si alguien tiene el mismo problema que tuve yo espero que le sirva.

      Eliminar
    4. ¡Excelente sitio que has compartido! Explica de forma muy sencilla y concisa el concepto de Programación Orientada a Objetos en JavaScript. Esperemos otros usuarios tambien encuentren ayuda en ese enlace. ¡Gracias por compartirlo!

      Eliminar
  7. Hola.

    Primero de todo decir que todo lo leído hasta ahora está muy bien explicado, lo entiendo todo perfectamente a excepción de lo siguiente:
    1. this.width=(width==null)?0:width;
    ¿Qué significan el "?" y el ":"?

    2. return(this.x actúan como operadores lógicos de "menor/mayor que", pero no estoy seguro de si ésa es su función en este caso.

    Muchas gracias.

    ResponderEliminar
    Respuestas
    1. Este comentario ha sido eliminado por el autor.

      Eliminar
    2. Acabo de ver la respuesta al comentario de Elkin Delgado, que era mi pregunta 1. :)

      Sobre la dos, pegué mal el texto:
      return(this.xrect.x&&
      this.yrect.y);
      No entiendo la función de los "<" y ">".

      Gracias de nuevo.

      Eliminar
    3. La segunda, hace la evaluación de lo que está dentro de los paréntesis, y se convierte en un valor verdadero o falso, que es lo que se regresa a través de ese return. Espero eso aclare tu pregunta.

      Eliminar
    4. Aclaradísima! :D

      Muchas gracias!

      Eliminar
  8. hola!! excelente la explicación.. por las dudas no tenes algun juego parecido a ricochet kills utilizando canvas???

    ResponderEliminar
    Respuestas
    1. ¡Gracias! Y lo siento, pero no tengo ningún juego parecido al que mencionas.

      Eliminar
  9. Una pregunta, en esta línea this.intersects=function(rect){
    Veo que haces varias cosas con ese 'rect', para ver cuando chocan los rectángulos...

    Pero no entiendo exactamente qué es rect?, dónde esta definida esa variable?, gracias!

    ResponderEliminar
    Respuestas
    1. Se envía en la función, y es el rectángulo con el que se va a comparar, tal como se muestra en esta línea:

      if(player.intersects(food)){
      //...
      }

      Eliminar
  10. Me está saliendo perfecto, mientras miro tu curso, estoy haciendo una revision del javascript en la página javascriptya que es mucho más didactica. para aprender javascript desde cero.
    Pero en si, javascript es facil de aprender una vez que veas la sintaxis te será muy facil manejar otros lenguajes.

    ResponderEliminar
  11. Buenas estoy intentado crear un rectangulo situado al azar al recagar la pagina. Estoy usando la function
    function random(max){
    return Math.floor(Math.random()*max);
    }
    y luego
    var x_alzar = random(300);
    var y_alzar = random(150);
    var rectangulo_al_azar=new Rectangulo(x_alzar,y_alzar,10,10);
    ......
    me funciona bien pero he puesto manualmente el numero max...que tengo que poner en random() para que automaticamente me coloque la altura y el ancho de la ventana canvas.
    Espero explicarme. Un saludo y gracias por este blog.

    ResponderEliminar
    Respuestas
    1. Creo que lo que buscas es random(canvas.width) y random(canvas.height), ¿Es esto correcto?

      Eliminar
    2. exactamente! asi lo puse en principio pero no me salia. Por eso lo puse manual por si el fallo venia de otro sitio. Pero asi puesto no sale

      Eliminar
    3. Esto lo pones en la función init después de la creación de canvas, ¿verdad?

      Eliminar
    4. este es el codigo completo:
      window.addEventListener('load',init,false);
      var canvas=null,ctx=null;
      var x_alzar = random(canvas.width);
      var y_alzar = random(canvas.height);
      var rectangulo_al_azar=new Rectangulo(x_alzar,y_alzar,10,10);
      function random(max){
      return Math.floor(Math.random()*max);
      }
      function init(){
      canvas=document.getElementById('canvas');
      ctx=canvas.getContext('2d');
      paint(ctx);

      }
      function paint(ctx){
      ctx.fillStyle='#000';
      ctx.fillRect(0,0,canvas.width,canvas.height);

      ctx.fillStyle='#00e5ee';
      rectangulo_al_azar.fill(ctx);
      }
      function Rectangulo(x,y,width,height){
      this.x=x;
      this.y=y;
      this.width=width;
      this.height=height;
      this.fill=function(ctx){
      ctx.fillRect(this.x,this.y,this.width,this.height);
      }
      }

      Eliminar
    5. He ahí el problema! Intentas acceder a su informacion siendo nulo. Tienes que esperar a obtener sus valores, dentro de la función init o después de ejecutarla.

      Eliminar
    6. ok gracias ya lo hice. Pensaba que era metiendolo en el init pero no.... meti las variables dentro de paint y funciono.
      MUCHAS GRACIAS estoy aprendiendo mucho mirando el código poco a poco.

      Eliminar
    7. Para entender mejor la function random...Como podría agregar a la misma un numero min...es decir que ademas de poner un valor como max que ademas tenga un valor como minimo por ejemplo máximo de 300 y min 10.
      function random(max){
      return Math.floor(Math.random()*max);
      }

      Eliminar
    8. Me alegra saber que ya ha quedado resuelta tu duda, pero prosigue con cuidado, pues en caso de hacer un paint de nuevo posteriormente, tu rectángulo se moverá de nuevo.

      Con el random min,max, es un poco mas difícil de lo que parece. Para agregar el mínimo, debes sumarlo al resultado, pero si lo haces solo asi, te daría en este ejemplo un número de 10 al 310, por lo que para respetar el máximo, debes restarle antes el mínimo a la hora de multiplicarlo:

      function random(min,max){
      return Math.floor(Math.random()*(max-min))+min;
      }

      Eliminar
    9. Muchas gracias por resolver mis dudas. Es curioso como hay que pensar para resolver esos problemas. Estoy aprendiendo mucho.GRACIAS!

      Eliminar
  12. ME he dado cuanta que por momentos el rectangulo player se sale del canvas, y puede devolverlo usando las teclas de direccion.

    ResponderEliminar
  13. Tal vez esto suene muy tonto, soy novato, pero que diferencia hay entre declarar:
    var variableUno = 10;
    var variableDos = 11;

    a como lo haces en las primeras lineas:

    var variableUno = 10, variableDos = 11;

    ¿Porque lo haces? es como cuando bajas una libreria js y esta la
    .min(sin espacios) y la normal o comodida.

    Una pregunta mas, di con tu EXCELENTE blog despues de que ya habia visto otros tutoriales de juegos canvas, y ahi actualiza con settimeout, este metodo lo entiendo, dice que funcion se tiene que actualizar y esta en milisegundos, pero trato de usar lo que usas tu el requestAnimationFrame si sale, me hice una animacion de una pelotita que va pegando con el cambas y cambia de direccion, con el settimeout se como modular la velocidad de la animacion (actulizacion de la funcion) pero no le entiendo mucho al requestAnimationFrame como modular

    ResponderEliminar
    Respuestas
    1. La forma en que se declaran las variables funcionan de igual forma en ambos casos, por lo que la forma en que se usen depende de la comodidad del desarrollador.

      En mi caso, prefiero agruparles en lineas que tengan un significado entre sí, aunque eso es enteramente personal.

      Toda la información respecto a RequestAnimationFrame, sus razones de uso, y como regular el tiempo con él, se ve más adelante a detalle en los apéndices 1 y 2. Si aún después de leerlos te quedan dudas, puedes preguntarme dichas dudas y con gusto te ayudaré a comprender mejor.

      Eliminar
  14. puedes hacer un ejemplo con sprites se lo agradeceria mucho

    ResponderEliminar
    Respuestas
    1. Dos temas adelante se muestra el uso de sprites.

      Eliminar
  15. Primero que nada muchas gracias por tomarte la molestia y el tiempo en hacer estos tutoriales, gracias a ellos he aprendido mucho. Pero tengo un problema, en esta cuarta parte de tu tutorial sigo todos los pasos, y al decir que sigo me refiero a que voy tecleando el codigo mientras leo las explicaciones, pero me sigue sin funcionar y no se a que se deba. El codigo lo tengo exactamente igual e incluso ya revise minusiosamente para ver si se me fue algun error de dedo pero nada, no se que pase.

    ResponderEliminar
    Respuestas
    1. Me extraña que, si el código es igual, haya ese conflicto. ¿De casualidad la consola de javascript te reporta algún error? ¿Probaste compilar y pegar el código final tal cual?

      Eliminar
  16. Hola soy daniel (daniel_dgs@hotmail.com)
    primero agradecerte muchísimo por este curso, me está ayudando bastante a entender javascript, y deseo decirte que se me ocurrio cambiar un poco la función Rectangle, que es la clase que dibuja al player a food el código que escribi fue el siguiente

    function Rectangle(x, y, width, height, fillStyle)//fillStyle es el color de relleno del rectangulo
    {
    //Comprobamos que esten todos los valores y si no cambiamos los valores para los null
    this.x = (x == null)?0:x;
    this.y = (y == null)?0:y;
    this.width = (width == null)?0:width;
    this.height = (height == null)?this.width:height;
    this.fillStyle = (fillStyle == null)?'#fff':fillStyle;//Si no se pone se predetermina blanco

    //Función interna de la clase Rectangle, que inica si existe intersección o no
    this.intersects = function(rect)
    {
    if(rect != null)
    {
    return (this.x < rect.x + rect.width && this.x + this.width > rect.x &&
    this.y < rect.y + rect.height && this.y + this.height > rect.y);
    }
    }

    //Función interna de la clase Rectangle, que lo auto dibuja en las posiciones x e y
    this.fill = function(ctx)
    {
    if(ctx != null)
    {
    ctx.fillStyle = this.fillStyle;//Inicamos el que el color de relleno será el de this.fillStyle
    ctx.fillRect(this.x, this.y, this.width, this.height);
    }
    }
    }


    muchas gracias de antemano

    ResponderEliminar
  17. una conulta como se hace para cambiar la tipografia, y tañano de score y pause. gracias

    ResponderEliminar
    Respuestas
    1. ¡Ese es un buen detalle que no he mencionado! Se haría agregando esta línea antes de escribir el texto:

      ctx.font = '32px Arial';

      Tan sólo asegurate de usar una fuente web-safe, o cargar previamente tu fuente mediante @font-face en tu CSS.

      Eliminar
  18. Hola! tengo una duda con respecto a los atributos de los objetos. Es necesario poner "this." en cada uno de ellos(por ejemplo this.x)?
    Gracias por compartir tus conocimientos!

    ResponderEliminar
    Respuestas
    1. En Javascript sí. De lo contrario, intentarías acceder a los valores globales, los cuales posiblemente no existan en este caso, y genere un error.

      Eliminar
  19. Hola gente!!!
    Corrijanme si me equivoco
    es lo mismo excribir esto:

    this.x=(x==null)?0:x;

    y esto

    if (this.x==null){
    x=0;
    }


    ??????????

    ResponderEliminar
  20. Tengo un problema con player.fill(ctx); y if (player.intersects(food)) {
    Las 2 aparecen como undefined y cuando presiono la tecla ENTER desaparece el player y el food.

    ResponderEliminar
    Respuestas
    1. Parece que algo en tu código está eliminando por error tu variable player al iniciar. ¿Me podrías compartir tu código para ver qué lo puede estar causando?

      Eliminar
    2. var KEY_ENTER = 13,
      KEY_LEFT = 37,
      KEY_UP = 38,
      KEY_RIGHT = 39,
      KEY_DOWN = 40,

      canvas = null,
      ctx = null,
      lastPress = null,
      pause = true,
      dir = 0,
      score= 0,
      player = null,
      food = null;

      document.addEventListener('keydown', function (evt) {
      lastPress = evt.which;
      }, false);

      function Rectangle (x, y, width, height){
      this.x = (x == null) ? 0 : x;
      this.y = (y == null) ? 0 : y;
      this.width = (width == null) ? 0 : width;
      this.height = (height == null) ? this.width : height;

      this.intersects = function (rect){
      if (rect == null){
      window.console.warn('Missing parameters on function intersects')
      } else {
      return (this.x < rect.x < rect.width &&
      this.x + this.width > rect.x &&
      this.y < rect.y + rect.height &&
      this.y + this.height > rect.y);
      }
      };

      this.fill = function (ctx){
      if (ctx == null){
      window.console.warn('Missing parameters on function fill');
      } else {
      ctx.fillRect(this.x, this.y, this.width, this.height);
      }
      };
      }

      function random (max){
      return Math.floor(Math.random()* max);
      }

      function paint(ctx){

      ctx.fillStyle = '#000';
      ctx.fillRect(0, 0, canvas.width, canvas.height);

      ctx.fillStyle = '#0f0';
      player.fill(ctx);

      ctx.fillStyle = '#f00';
      food.fill(ctx);

      ctx.fillStyle = '#fff';
      ctx.fillText('Last Press: ' + lastPress, 0, 20);

      ctx.fillText('Score:' + score, 0, 10);

      if (pause){
      ctx.textAlign = 'center';
      ctx.fillText ('PAUSE', 150, 75);
      ctx.textAlign = 'left';
      }
      }

      function act (){
      if (!pause){
      if (lastPress == KEY_UP){
      dir = 0;
      }
      if (lastPress == KEY_RIGHT){
      dir = 1;
      }
      if (lastPress == KEY_DOWN){
      dir = 2;
      }
      if (lastPress == KEY_LEFT){
      dir = 3;
      }

      if (dir == 0){
      player -= 10;
      }
      if (dir == 1){
      player += 10;
      }
      if (dir == 2){
      player += 10;
      }
      if (dir == 3){
      player -= 10;
      }

      if (player.x >= canvas.width){
      player = 0;
      }
      if (player.y >= canvas.height){
      player = 0;
      }
      if (player.x < 0){
      player = canvas.width;
      }
      if (player.y < 0){
      player = canvas.height;
      }

      if (player.intersects(food)) {
      score =+ 1;
      food.x = random(canvas.width/10 - 1)* 10;
      food.y = random(canvas.height/10 - 1)* 10;
      }
      }

      if (lastPress == KEY_ENTER){
      pause = !pause;
      lastPress = null;
      }
      }

      function repaint (){
      window.requestAnimationFrame(repaint);
      paint(ctx);
      }

      function run (){
      setTimeout(run, 50);
      act();
      }

      function init(){
      canvas = document.getElementById('canvas');
      ctx = canvas.getContext('2d');

      player = new Rectangle(40, 40, 10, 10);
      food = new Rectangle(80, 80, 10, 10);

      run();
      repaint();
      }

      window.addEventListener('load', init, false);

      Eliminar
    3. ¡Ya identifiqué el problema! En donde evalúas "dir", están estás modificando "player" directamente, en lugar de "player.x" y "player.y". Prueba arreglando esto, y tú código ya debería estar bien.

      Eliminar
    4. Si, gran error mio :(, no me fije en el código.
      Muchas gracias por al ayuda :), seguiré con esta guía :).

      Eliminar
  21. Tengo un problema: cuando abro index.html, no me aparece ni la "comida" ni el contador de puntaje ni, al pausar, la palabra "PAUSE". Alguna ayuda?

    Pd:mi codigo es



    var KEY_ENTER = 13,
    KEY_LEFT = 37,
    KEY_UP = 38,
    KEY_RIGHT = 39,
    KEY_DOWN = 40,

    canvas = null,
    ctx = null,
    lastPress = null,
    pause = true,
    dir = 0,
    score= 0,
    player = null,
    food = null;

    document.addEventListener('keydown', function (evt) {
    lastPress = evt.which;
    }, false);

    function Rectangle (x, y, width, height){
    this.x = (x == null) ? 0 : x;
    this.y = (y == null) ? 0 : y;
    this.width = (width == null) ? 0 : width;
    this.height = (height == null) ? this.width : height;

    this.intersects = function (rect){
    if (rect == null){
    window.console.warn('Missing parameters on function intersects')
    } else {
    return (this.x < rect.x < rect.width &&
    this.x + this.width > rect.x &&
    this.y < rect.y + rect.height &&
    this.y + this.height > rect.y);
    }
    };

    this.fill = function (ctx){
    if (ctx == null){
    window.console.warn('Missing parameters on function fill');
    } else {
    ctx.fillRect(this.x, this.y, this.width, this.height);
    }
    };
    }

    function random (max){
    return Math.floor(Math.random()* max);
    }

    function paint(ctx){

    ctx.fillStyle = '#000';
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    ctx.fillStyle = '#0f0';
    player.fill(ctx);

    ctx.fillStyle = '#f00';
    food.fill(ctx);

    ctx.fillStyle = '#fff';
    ctx.fillText('Last Press: ' + lastPress, 0, 20);

    ctx.fillText('Score:' + score, 0, 10);

    if (pause){
    ctx.textAlign = 'center';
    ctx.fillText ('PAUSE', 150, 75);
    ctx.textAlign = 'left';
    }
    }

    function act (){
    if (!pause){
    if (lastPress == KEY_UP){
    dir = 0;
    }
    if (lastPress == KEY_RIGHT){
    dir = 1;
    }
    if (lastPress == KEY_DOWN){
    dir = 2;
    }
    if (lastPress == KEY_LEFT){
    dir = 3;
    }

    if (dir == 0){
    player.y -= 10;
    }
    if (dir == 1){
    player.x += 10;
    }
    if (dir == 2){
    player.y += 10;
    }
    if (dir == 3){
    player.x -= 10;
    }

    if (player.x >= canvas.width){
    player = 0;
    }
    if (player.y >= canvas.height){
    player = 0;
    }
    if (player.x < 0){
    player = canvas.width;
    }
    if (player.y < 0){
    player = canvas.height;
    }

    if (player.intersects(food)) {
    score =+ 1;
    food.x = random(canvas.width/10 - 1)* 10;
    food.y = random(canvas.height/10 - 1)* 10;
    }
    }

    if (lastPress == KEY_ENTER){
    pause = !pause;
    lastPress = null;
    }
    }

    function repaint (){
    window.requestAnimationFrame(repaint);
    paint(ctx);
    }

    function run (){
    setTimeout(run, 50);
    act();
    }

    function init(){
    canvas = document.getElementById('canvas');
    ctx = canvas.getContext('2d');

    player = new Rectangle(40, 40, 10, 10);
    food = new Rectangle(80, 80, 10, 10);

    run();
    repaint();
    }

    window.addEventListener('load', init, false);

    ResponderEliminar
  22. Hola cuando abro el juego me aparece el siguiente error en google chrome:Uncaught SyntaxError: Unexpected token {
    la parte del fallo es:
    if (player.intersects(food) {
    score += 1;
    food.x = random(canvas.width / 10 - 1) * 10;
    food.y = random(canvas.height / 10 - 1) * 10;
    }
    }

    ResponderEliminar
    Respuestas
    1. Parece ser que te falta un paréntesis de cierre al final de la condición "if".

      Eliminar