Aprende a crear juegos en HTML5 Canvas

lunes, 9 de abril de 2012

Mover mientras se presiona tecla.

Aprendimos a cambiar la dirección de un objeto dependiendo de la última tecla presionada. ¿Pero cómo se haría si deseamos mover el objeto solo cuando una tecla está siendo presionada? Es, después de todo, el comportamiento más común en la mayoría de los juegos.

Para aprender a hacer esto, ocuparemos el código que utilizamos en la Parte 3. Usando el teclado y lo modificaremos como se indica a continuación:

Primero, crearemos una variable de tipo arreglo, donde guardaremos todas las teclas que están siendo presionadas:
    var pressing=[];
Ahora, modificaremos el EventListener al final, guardando en nuestro arreglo, en la posición equivalente a la tecla presionada, el valor booleano true, indicando así que esta tecla está siendo presionada.
    document.addEventListener('keydown',function(evt){
        lastPress=evt.keyCode;
        pressing[evt.keyCode]=true;
    },false);
De igual forma, agregaremos un EventListener para cuando la tecla es liberada, cambiando entonces su valor a falso.
    document.addEventListener('keyup',function(evt){
        pressing[evt.keyCode]=false;
    },false);
Así podremos saber cuando una tecla está siendo presionada. Ahora modificaremos de la función "act" la sección donde cambiamos la dirección y movemos el rectángulo por este código:
            // Move Rect
            if(pressing[KEY_UP])
                y-=10;
            if(pressing[KEY_RIGHT])
                x+=10;
            if(pressing[KEY_DOWN])
                y+=10;
            if(pressing[KEY_LEFT])
                x-=10;
Noten que ya no debemos cambiar primero la dirección y luego mover el objeto, ya que el movimiento está dictado de forma directa por la tecla presionada. Así, podremos interactuar en tiempo real con los objetos en pantalla dependiendo la tecla presionada.

Código final:

[Canvas not supported by your browser]
(function(){
    'use strict';
    window.addEventListener('load',init,false);
    var KEY_ENTER=13;
    var KEY_LEFT=37;
    var KEY_UP=38;
    var KEY_RIGHT=39;
    var KEY_DOWN=40;

    var canvas=null,ctx=null;
    var x=50,y=50;
    var lastPress=null;
    var pressing=[];
    var pause=true;

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

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

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

    function act(){
        if(!pause){
            // Move Rect
            if(pressing[KEY_UP])
                y-=10;
            if(pressing[KEY_RIGHT])
                x+=10;
            if(pressing[KEY_DOWN])
                y+=10;
            if(pressing[KEY_LEFT])
                x-=10;

            // Out Screen
            if(x>canvas.width)
                x=0;
            if(y>canvas.height)
                y=0;
            if(x<0)
                x=canvas.width;
            if(y<0)
                y=canvas.height;
        }
        // Pause/Unpause
        if(lastPress==KEY_ENTER){
            pause=!pause;
            lastPress=null;
        }
    }

    function paint(ctx){
        ctx.fillStyle='#000';
        ctx.fillRect(0,0,canvas.width,canvas.height);
        ctx.fillStyle='#0f0';
        ctx.fillRect(x,y,10,10);
        ctx.fillStyle='#fff';
        //ctx.fillText('Last Press: '+lastPress,0,20);
        if(pause){
            ctx.textAlign='center';
            ctx.fillText('PAUSE',150,75);
            ctx.textAlign='left';
        }
    }

    document.addEventListener('keydown',function(evt){
        lastPress=evt.keyCode;
        pressing[evt.keyCode]=true;
    },false);

    document.addEventListener('keyup',function(evt){
        pressing[evt.keyCode]=false;
    },false);

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

19 comentarios:

  1. Hola que tal me podrias ayudar?, no logro hacer que se mueva con el teclado e incluir las paredes al mismo tiempo

    ResponderEliminar
    Respuestas
    1. Creo que intentas crear un objeto sólido. Para lograr esto, debes mover en X el objeto, y si colisiona con un objeto de tipo pared, le restas el movimiento en X. Después, haces lo mismo en Y. Con esto crearás un objeto sólido. Posteriormente, puedes llenar de objetos sólidos un área, para crear paredes y laberintos. ¡Suerte!

      Eliminar
  2. Hey me gustaria q no se pueda salir del cuadro negro el objeto verde...!!! me podrias ayudar con el codigo..!!

    ResponderEliminar
    Respuestas
    1. Habrá que modificar Out Screen de la siguiente forma:

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

      Cambia 10 por el ancho del personaje, en caso que sea distinto. ¡Suerte!

      Eliminar
    2. Hey me funciono super bien..!! Pero para la parte de abajo desaparece, no se xq.. Si podes ayudarme seria bueno y perdon por la molestia..!! y graxias..!!

      Eliminar
    3. Parece que fue un error mio! Si lees, en y, le puse canvas.width en lugar de canvas.height. Solo corrige eso y deberá funcionar sin problemas. Suerte :)

      Eliminar
  3. Oye me podrias proporcionar el codigo para hacer un laberinto...lohe intentado todo y no he podido, te agradezco mucho

    ResponderEliminar
    Respuestas
    1. Lo que le preguntas aquí, es un poco complejo de realizar. Se está planeado incluir un curso paso a paso en tres partes para ello, pero puedes intentarlo. En resumen, cada pared es un rectángulo sólido. El primer comentario de esta entrada incluye como crear objetos sólidos.

      Solo debes crear uno por cada bloque o pared, y eso te permitirá crear el laberinto. Si aun necesitas ayuda, avísame. Suerte.

      Eliminar
  4. El array de JavaScript me desconcierta mucho...

    1- Es de tipo booleano, me es extraño pero vale
    2- Indicas que en el indice 37 se guarde un true

    var PRESSING = [];
    PRESSING[evt.keyCode] = true; // P.E, si pulsa 37(izq)

    ??...en ningun lado le has dicho al array que tenga 37 o más índices.

    ¿se supone que funciona sin indicarle el tamaño como el ArrayList?¿entonces porque cuando metemos un nuevo valor lo mete en la siguiente posicion?

    array.push(valor);

    Además también podemos meterle ese valor en la posición indicada.

    array[indice] = valor;

    Mi posible conclusión
    ---------------------
    Parece que el array de JS es una mezcla de array normal y de ArrayList teniendo las ventajas de ambos.

    1. No es necesario indicarle las posiciones que tendrá.
    2. Puede meter valores unos detrás de otro(ArrayList)
    3. Puede meter valores por índice(Array asecas).

    ResponderEliminar
    Respuestas
    1. Haz llegado a una conclusión muy certera. Los Array en JavaScript son un híbrido de ambos, lo que permite una flexibilidad mucho mayor a la hora de desarrollar.

      Por supuesto, nunca he probado que pasaría en caso de usarse ambos a la vez. Posiblemente un efecto no deseado. Es mejor práctica mantenerlos separados.

      ¡Gracias por tu comentario!

      Eliminar
  5. amigo te tengo una pregunta, lo que pasa es que haciendo una animacioncita con un sprite usando este curso logro mover a mi personaje(hacer que camine) mientras oprimo la tecla derecha, sin embargo quiero que al darle barra espaciadora con el sprite se genere la animacion del golpe, pero sin que se repite infinitamente solo que de el golpe y ya a si mismo un salto que de el salto y ya, y no he podido hacerlo, puesto que si oprimo la tecla la animacion se genera pero se sigue repitiendo, si me puedieras ayudar quedaria muy agradecido.

    ResponderEliminar
    Respuestas
    1. !Todo un reto! Prueba este código si, por ejemplo, tu animación es de 6 cuadros:

      if(currentAnimation<6){
      ctx.drawImage(animation, currentAnimation*10,10,10, x,y,10,10);
      currentAnimation++;
      }

      ¡No olvides asignar de nuevo currentAnimation a 0 cuando la acción empieza de nuevo!

      Eliminar
  6. me sirvio mucho tu consejo ahora el problema es que cada vez que se ejecuta la animacion una vez termina el objeto desaparece, si muevo el personaje vuelve a aparecer y se mueve sin ningun problema, si ejecuto el golpe de nuevo hace la animacion pero una vez que termina se desaparece, no se si tendras algun consejo para esto, y otra pregunta que surge es la de como saltar y mover a mi personaje al mismo tiempo no se si tienes algun consejo para esto, quedo muy agradecido con tu curso y tus consejos realmente es de mucha ayuda, quedo atento a tu respuesta y una vez mas gracias.

    ResponderEliminar
    Respuestas
    1. Si desaparece al final, debe de ser que currentAnimation tiene un valor de más. Prueba con 5 en lugar de 6... Creo que ese puede ser el problema.

      Sobre el movimiento, ¿Que conflicto tienes con moverlo y saltar al mismo tiempo? ¿De que manera lo haces?

      Eliminar
  7. bueno amigo aqui esta el codigo par que entiendas mas mi problema
    window.addEventListener('load',init,false);
    var aTimer=0;
    var lastKey=null;
    var player=new Rectangle(70,300,70,70);
    var spritesheet=new Image();
    var PRESSING=[];
    spritesheet.src='MUA_Spidey.png';

    function init(){
    canvas=document.getElementById('canvas');
    canvas.style.background='#000';
    ctx=canvas.getContext('2d');
    //aqui hago una primera dibujada del personaje ctx.drawImage(spritesheet,0,130,70,70,player.x,player.y,player.width,player.height);
    run();
    }
    function run(){
    setTimeout(run,100);
    if(PRESSING[39]){

    //aqui borro la imagen del personaje puesto que sin esto al //ejecutarse la animacion del spritese sobreponen las //imagenes es decir queda un rastro de las imagenes que se //van dibujando
    ctx.clearRect(player.x,player.y,player.width,player.height);
    player.x+=5;
    }
    if(lastKey == 32){
    //de igual forma al ejecutar esta animacion borro la //imagen para evitar que se sobrepongan
    ctx.clearRect(player.x,player.y,player.width,player.height);
    }
    paint(ctx);
    }

    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;
    }

    function paint(ctx){

    if(PRESSING[39]){
    ctx.drawImage(spritesheet,(aTimer%6)*78,130,70,70,player.x,player.y,player.width,player.height);
    aTimer++;
    if(aTimer>60)
    aTimer-=60;
    if(player.x>canvas.width){
    player.x=-70;
    }
    }

    if(lastKey == 32){
    if(aTimer<5){
    ctx.drawImage(spritesheet,aTimer*90,440,70,70,player.x,player.y,player.width,player.height);
    aTimer++;
    }else if(aTimer>5){
    aTimer = 0;
    }
    }
    }
    document.addEventListener('keydown',function(evt){
    lastKey=evt.keyCode;
    PRESSING[evt.keyCode]=true;
    },false);
    document.addEventListener('keyup',function(evt){
    PRESSING[evt.keyCode]=false;
    },false);

    no se si tendre algun problema con el codigo que es lo mas seguro, asi que te agradeceria si me dijeras cual es el error en mi codigo, y respecto a el salto conjunto con el movimiento, lo que pasa es que al realizar la animacion del salto este es vertical, y al darle la tecla derecha, se ejecuta la animacion de caminar entonces no se como hacer que se haga un salto que vaya hacia adelante.

    ResponderEliminar
    Respuestas
    1. Para empezar, te recomiendo que todos los dibujados y borrados los hagas en la función paint(cx), eso te evitará muchos problemas en el futuro también.

      Segundo: ¿El salto es al momento de presionar el espacio, o mientras se está presionando el espacio? Tengo el presentimiento que ahí puede haber un error.

      Tercero:
      if(aTimer<5){
      //
      aTimer++;
      }else if(aTimer>5){
      aTimer = 0;
      }
      Si aTimer es menor de 5, se suma. Si es mayor, se vuelve cero. ¿Que pasa cuando es igual a 5? No veo que se sume, así que nunca puede llegar a ser mayor a 5. ¿Estoy en lo correcto?

      Sobre salto y movimiento, ya veo el problema. Ya que primero lo dibujas si se mueve, y luego lo vuelves a dibujar si salta. Estos dos deberían ser un "else if" para que solo se dibuje uno u otro. Yo creo que debes darle más prioridad al salto, pero eso ya depende de ti.

      Si tienes más dudas, o quedó algo sin resolver, me avisas ;)

      Eliminar
  8. Hola que tal el código que presentas ya lo tengo, es parecido quisiera saber como puedo hacer para crear flechas que contengan este código y poder mover al personaje en mi juego. Espero puedas ayudarme o darme una idea de como hacerlo gracias

    ResponderEliminar
  9. Hola que tal el código que presentas ya lo tengo, es parecido quisiera saber como puedo hacer para crear flechas que contengan este código y poder mover al personaje en mi juego. Espero puedas ayudarme o darme una idea de como hacerlo gracias

    ResponderEliminar
    Respuestas
    1. ¿Te refieres a botones en la pantalla? De ser así, puedes revisar este tema:

      http://juegos.canvas.ninja/2013/09/botones-en-pantalla.html

      Eliminar