Aprende a Crear Juegos en HTML5 Canvas

March 18, 2018 | Author: Brook Blair | Category: Animation, Image, Computer Graphics, Image Processing, Imaging


Comments



Description

Aprende a crear juegos en HTML5 Canvaslunes, 14 de mayo de 2012 Hojas de sprites y animaciones Ahora que tenemos la base para un juego completo, pasaremos a agregar las imágenes. Anteriormente ya vimos como agregar imágenes a un juego, así que aprovecharé este momento para enseñarles una técnica más avanzada: Las hojas de sprites. Una hoja de sprites (Spritesheet) es una imagen grande que contiene varios sprites en la misma, tal como la imagen siguiente que es la que usaremos en el presente ejemplo: La ventaja de las hojas de sprites sobre las imágenes individuales, es que la hoja de sprites pesa menos que la suma de todas las imágenes individuales, lo que es una ventaja especial en un juego web, por que significa menos ancho de banda consumido, menos llamadas al servidor y una descarga más rápida. HTML5 Canvas ya viene con un método avanzado para dibujar secciones de una imagen sobre nuestro canvas, lo que nos permite usar hojas de sprites con mucha facilidad. Para usar esta forma, hay que pasar nueve parámetros a la función drawImage: ctx.drawImage(image, sX,sY,sWidth,sHeight, dX,dY,dWidth,dHeight); El primer parámetro, es la imagen que tiene nuestra hoja de sprites. Los siguientes cuatro, indican el área de nuestra imagen que será dibujada (source), y los últimos cuatro, indican el área en nuestro canvas donde será dibujada esta imagen (destiny). Prosigamos al ejemplo práctico. Comencemos declarando una variable imagen y asignando su fuente a nuestra imagen: var spritesheet=new Image(); spritesheet.src='spritesheet.png'; Después, vayamos a donde dibujamos nuestro jugador, y sustituyamos el rectángulo verde, dibujando la primer imagen de nuestra hoja de sprites. La nave está en en la posición 0,0 de nuestra hoja de sprites, y tiene una altura y anchura de 10x10 pixeles, por tanto, la función quedaría así: ctx.drawImage(spritesheet,0,0,10,10,player.x,player.y,player.width,player.height ); Prosigamos con las naves enemigas. La cuarta imagen de nuestra hoja de sprites es la nave enemiga, y la quinta, la nave totalmente en blanco, que representa la nave enemiga cuando está siendo dañada. Ambas tienen 10x10 pixeles de alto y ancho, y se encuentran en las posiciones fillStyle='#fff'.i++) //ctx.drawImage(spritesheet.shots[i].drawImage(spritesheet.0.powerups[i].wid th.50.width.l=shots.i<l. //ctx.length.fillStyle='#cc6'.i<l.timer%2==0) // ctx.y.fillRect(shots[i].height).40. } Ahora que ya comprendemos la forma en que funciona esta función. //ctx. ctx.60.y.height).enemies[i].height).width.30.powerups[i]. ctx.enemies[i].5.y.enemies[i].x. Nota que los disparos tienen una altura y anchura de 5x5 pixeles. enemies[i].0 respectivamente.y.i<l.x. ctx.powerups[i]. a diferencia de los demás elementos que hemos dibujado antes. hagamos lo mismo para las mejoras: for(i=0.height).enemies[i].height).10.powerups[i].shots[i].width.70.0.width.0.powerups[i]. he bajado la velocidad de las naves enemigas y de las mejoras .fillStyle='#00f'.shots[i].10.0 y 40.type==1) // ctx. else // ctx.wid th.fillStyle='#f90'.10.10.powerups[i].length.x.length.x. enemies[i].shots[i]. } Y para los disparos: for(i=0.i++){ if(powerups[i].5).l=powerups. else // ctx.l=enemies.y.fillRect(enemies[i]. ctx.height) .x.30.height).width.enemies[i].powerups[i].i++){ if(enemies[i].y. Por tanto.10.fillRect(powerups[i].5.x.10.x.powerups[i].y.powerups[i].enemies[i].10.5.drawImage(spritesheet.enemies[i]. las funciones para dibujarlas quedarían de esta forma: for(i=0. Para hacer el juego más jugable.x.enemies[i].powerups[i].powerups[i].y.enemies[i].0.drawImage(spritesheet. ctx.10.drawImage(spritesheet.0.shots[i]. En el caso de los disparos. podemos eliminar esta parte.0+(aTimer%3)*10. son tres imágenes dispuestas de forma horizontal con 10 pixeles de ancho.height). Si tienes una animación de más de 10 imágenes. Primero.0+(aTimer%2)*5. Posteriormente. aplicaremos la animación a nuestros objetos por medio de este contador.player. se agrega el contador al ciclo.de 10 a 5 pixeles por turno. ten cuidado con este ciclo. Declaramos una variable para ello al comienzo de nuestro juego: var aTimer=0.70.width . Si deseas hacer más lenta una animación. restaremos ese valor sobre el mismo: aTimer++.y. Para crear animaciones.0. Para obtener la imagen de la animación a dibujar. la formula quedaría de esta forma: (aTimer%6)*10 Y este valor se suma al origen en X o Y. así como agregado 4 naves al comienzo del juego.player. Probemos el juego. la formula es ligeramente más complicada.x. En los dos casos previos. así que deberás sumar su origen correspondiente.shots[i].5. Pero nota que la mayoría de las veces. sacamos el módulo de nuestro contador entre la cantidad de imágenes que tiene nuestra animación.player.10. Ahora. es que permiten crear animaciones de forma sencilla. hay que dividir el valor de nuestro contador entre la cantidad de pausas que se desea hacer. según corresponda si están dispuestos de forma horizontal o vertical. se dibujarían de esta forma: ctx. if(aTimer>360) aTimer-=360. por tanto. y como sumar un valor a 0 es redundante.player.x. y veremos como cada imagen de nuestra hoja de estilos es dibujada correctamente en su posición del juego.10. después . dio la casualidad que el origen al que se sume el valor es 0. son dos imágenes dispuestas de forma vertical con 5 pixeles de alto. y este valor lo multiplicamos por el alto o el ancho de cada imagen.5. por que es el mínimo número divisible entre los números del 1 al 10. la animación no comenzará en 0.y. En el caso de nuestra nave.5). pero alerta a estos detalles sobre los objetos animados. por tanto el dibujado quedaría de esta forma: ctx.drawImage(spritesheet. pues puede crear saltos inesperados en dicha animación.drawImage(spritesheet. primero se necesita un contador que nos indique la animación por dibujar. Para mantener mayor control sobre este contador. Este valor puedes cambiarlo por el que creas conveniente. Otra ventaja que presentan las hojas de sprites. Por ejemplo si tuvieramos una animación de 6 imágenes. cuando supere el valor de 360. Como podrás ver en nuestra hoja de sprites. sumándose en uno cada turno mientras el juego no esté en pausa. esta ya está preparada para agregar animaciones a nuestro jugador y a los disparos.5.shots[i]. Se elige 360. ya sea que estén dispuestos de forma vertical u horizontal dentro de nuestra hoja de estilos. init. Por ejemplo. spritesheet.0. según su disposición. var spritesheet=new Image(). var multishot=1. var PAUSE=true. y como siempre. window. Así concluimos con el conocimiento para hacer un segundo juego. ¡Felices códigos! Codigo Final 'use strict'. var enemies=[].280.ctx=null. var shots=[]. } . pero considero que es una buena herramienta saber como hacer dicho efecto para cuando lo necesiten en sus futuros juegos.este valor se convierte a un entero con la función "parseInt". Posteriormente ya se saca el módulo de la cantidad de imágenes que tiene nuestra animación. y la base ha quedado completa. var score=0. function random(max){ return ~~(Math. var aTimer=0. Con esto. estoy en mi mejor disposición para ayudarles. var GAMEOVER=true. la formula quedaría de esta forma: (parseInt(aTimer/4)%6)*10 No aplicaré esta formula a ninguna animación del presente juego.addEventListener('load'. y con este. var PRESSING=[]. var messages=[].src='spritesheet. y este valor lo multiplicamos por el alto o ancho de cada imagen. Gracias por acompañarme una vez más en este sitio.10.3). hemos aplicado una hoja de estilos a nuestro juego. puedes seguir aplicando lo que has aprendido para mejorar y personalizar tu propio juego de naves. var canvas=null. var powerups=[].10. si quisieramos hacer que nuestra animación de 6 imágenes fuera 4 veces más lenta.random()*max).false).png'. var lastPress=null. var player=new Rectangle(70. cualquier duda o comentario. 10.0. multishot=1. } function game(){ if(!PAUSE){ // GameOver Reset if(GAMEOVER) reset(). messages.x=70.10.0.0.2)).length=0. enemies.0.getContext('2d').2)).0.50).10.10.0. } function reset(){ score=0. player. player.function init(){ canvas=document. enemies. player. GAMEOVER=false.2)). canvas. enemies.timer=0.getElementById('canvas'). enemies. game().10.10.10. ctx=canvas. .10. shots. enemies.push(new Rectangle(90.style.2)).push(new Rectangle(50.y=280. player.0.health=3. powerups.length=0.0.length=0. } function run(){ setTimeout(run.push(new Rectangle(10.length=0. run().background='#000'. paint(ctx).push(new Rectangle(130. y+2.push(new Rectangle(player. } // Move Shots for(var i=0.y<0){ shots.4)).4)). shots.push(new Rectangle(player.x.player.y. if(PRESSING[37]) //LEFT player.1).4.y.push(new Rectangle(player.player.y-=10.y.player. // Out Screen if(player.width-player. .i<l.y-=10. shots.player.4.push(new Rectangle(player.4.x=canvas.x=0.y+=10. lastPress=null. // New Shot if(lastPress==32){ if(multishot==3){ shots. if(shots[i].x+4.splice(i--.4)).width-player.player.l=shots.x+10. if(player.player.x<0) player.x-2.y+2.4)). shots.push(new Rectangle(player.4)).x+=10.width) player.width.// Move Player //if(PRESSING[38]) //UP // player.x+3. //if(PRESSING[40]) //DOWN // player.length.i++){ shots[i]. } else shots. } else if(multishot==2){ shots.4. if(PRESSING[39]) //RIGHT player.y.push(new Rectangle(player.x+6.x-=10.x>canvas.4)).4.4. i<l.y+=2.push(new message('+5'. l--. .y)).y+=5.player.push(new message('MULTI'.type==1){ // MultiShot if(multishot<3){ multishot++.player. // Powerup Outside Screen if(powerups[i]. if(messages[i].length.intersects(powerups[i])){ if(powerups[i]. messages. } } // Move Messages for(var i=0. } else{ score+=5.length.player.height){ powerups.i<l.i++){ messages[i].i++){ powerups[i].l=messages.1).l=powerups. } } else{ // ExtraPoints score+=5.x. } // Player intersects if(player.player.1). l--.l--. messages.x. continue.y<260){ messages.splice(i--.y>canvas.y)). } } // Move PowerUps for(var i=0.splice(i--. length.health=2.health<1){ enemies[i].splice(i--.width/10)*10. } } enemies[i].length.push(new Rectangle(random(canvas.x=random(canvas. ll--.i++){ if(enemies[i]. enemies[i]. } powerups.x=random(canvas.timer--. if(enemies[i].l=enemies.messages. // Enemy Outside Screen if(enemies[i]. // Shot Intersects Enemy for(var j=0. enemies[i].ll=shots.height){ enemies[i]. l--.y=0.splice(j--. } else{ enemies[i]. .x.health--. enemies[i]. enemies. } shots.width/10)*10.10.0. } } // Move Enemies for(var i=0. enemies[i].1).push(new message('+5'.j++){ if(shots[j].10.y)).y+=5.player.i<l.j<ll.intersects(enemies[i])){ score++.1).timer=1.player.timer>0) enemies[i].2)).width/10)*10.0.y>canvas.y=0. 0. } shots.10. if(r<5){ if(r==0) // New MultiShot powerups. enemies[i].y=0.timer=20. } } .length.health=2. enemies[i]. else // New ExtraPoints powerups.x=random(canvas.enemies[i].push(new Rectangle(enemies[i].health--.10. enemies[i].10.0)).splice(j--.intersects(enemies[i])&&player. } enemies[i]. player.10.2)).j++){ if(shots[j].x.y.push(new Rectangle(random(canvas.enemies[i].health<1){ // Add PowerUp var r=random(20).10.j<ll.y.10.health--.timer=1. } // Player Intersects Enemy if(player.0.timer<1){ player.enemies[i]. enemies.x.ll=shots.intersects(enemies[i])){ score++.width/10)*10.1).health=2. ll--. } else{ enemies[i].width/10)*10. if(enemies[i]. } // Shot Intersects Enemy for(var j=0.push(new Rectangle(enemies[i].1)). wid th.(aTimer%3)*10.width.10. // GameOver if(player.fillStyle='#cc6'.powerups[i].drawImage(spritesheet.10.0.health<1){ GAMEOVER=true. for(var i=0.y.powerups[i].x.powerups[i].10.wid th.p layer.timer--. } } function paint(ctx){ ctx.} // Damaged if(player. ctx. else // ctx.10.player.drawImage(spritesheet.60.0.type==1) // ctx.y.0.width. //ctx.powerups[i].x.fillStyle='#0f0'.l=powerups.clearRect(0. ctx.player.height). .timer>0) player. PAUSE=true.height). lastPress=null.10.height).player.canvas.y.player.0.powerups[i].i<l.canvas.drawImage(spritesheet. ctx.powerups[i].i++){ if(powerups[i].width. if(player.length.fillRect(player.50.10.player.fillStyle='#f90'.height).height). } } // Pause/Unpause if(lastPress==13){ PAUSE=!PAUSE.player.powerups[i].timer%2==0) //ctx.y.x.x.powerups[i]. textAlign='left'.x.fillText('Last Press: '+lastPress.timer%2==0) // ctx.messages[i].fillText('Score: '+score. else ctx. enemies[i].fillText(messages[i]. ctx.100. ctx.10.length.length. ctx.enemies[i].x.fillText('PAUSE'.powerups[i]. ctx.i++) //ctx.10.75.width.fillText('GAME OVER'.enemies[i].l=enemies.enemies[i].height). else // ctx.shots[i].fillText('Shots: '+shots.health.width. if(GAMEOVER) ctx.150).i++) ctx.i<l.y.height).messages[i].drawImage(spritesheet.fillStyle='#fff'.shots[i].fillStyle='#f00'.y). //ctx.0.length.powerups[i].y.70.30).width.40.0.0.x.shots[i].height). } for(var i=0.width. //ctx.fillRect(powerups[i].shots[i].enemies[i].x.x. ctx.height) .20). ctx.20).(aTimer%2)*5.//ctx.0. ctx.i<l. } //ctx.fillStyle='#00f'.5).x.30.enemies[i].enemies[i]. //ctx.width.0.5.75.textAlign='center'.fillText('Health: '+player.10.10. } . for(var i=0.fillStyle='#fff'.y.drawImage(spritesheet.y.height). if(PAUSE){ ctx.enemies[i]. for(var i=0.powerups[i].5.x.5.fillRect(shots[i].150).drawImage(spritesheet.20).length.enemies[i].l=shots.l=messages.shots[i].i<l.y.y.string. enemies[i].enemies[i].i++){ if(enemies[i].fillRect(enemies[i]. y).keyCode. this. PRESSING[evt.function(evt){ lastPress=evt.y=(y==null)?0:y.height. this.y+rect.else{ aTimer++. }.width=(width==null)?0:width.height=(height==null)?this.x&& this.x+this.height>rect.type=(type==null)?1:type. if(aTimer>360) aTimer-=360. function Rectangle(x.keyCode]=false.x.width>rect.y<rect. this.health){ this.width&& this. this.addEventListener('keydown'. . }.type.x=(x==null)?0:x.y.string=(string==null)?'?':string.false).x<rect. } } document.addEventListener('keyup'.keyCode]=true. this.function(evt){ PRESSING[evt.y){ this.height&& this.false).y+this.width:height.intersects=function(rect){ if(rect!=null){ return(this. this. this.timer=0. document. } } } function message(string.health=(health==null)?1:health.x+rect.width. y=(y==null)?0:y. this. } Publicado por Karl Tayfer en 03:00 .this.x=(x==null)?0:x.
Copyright © 2024 DOKUMEN.SITE Inc.