Seguramente con la hoja de sprites en la ocasión pasada, el gran asteroide en la imagen te dio una pista del juego que estamos haciendo. Y si aun no estás seguro, te lo confirmo de una vez: Estamos creando un juego de tipo "asteroides".
Una de las características que identifican a este juego, es que al explotar cada uno de los gigantescos asteroides, estos se fragmentan en asteroides más pequeños, hasta que los menores son finalmente destruidos. En esta ocasión, aprenderemos a hacer dicho efecto.
Comenzaremos declarando el arreglo que contendrá los asteroides:
Una de las características que identifican a este juego, es que al explotar cada uno de los gigantescos asteroides, estos se fragmentan en asteroides más pequeños, hasta que los menores son finalmente destruidos. En esta ocasión, aprenderemos a hacer dicho efecto.
Comenzaremos declarando el arreglo que contendrá los asteroides:
var enemies=[];
Para asegurar que siempre tengamos asteroides a los cuales disparar durante la prueba, tres nuevos serán agregados cada vez que el arreglo esté vacío, fuera de pantalla, en alguna dirección al azar: // New Enemies
if(enemies.length<1){
for(var i=0;i<3;i++){
var e=new Circle(-20,-20,20);
e.rotation=random(360);
enemies.push(e);
}
}
Mover los asteroides no representan reto alguno para nosotros. Tampoco el hacer que al colisionar el asteroide con uno de nuestros disparos, el puntuaje se sume y ambos sean removidos de sus respectivos arreglos: // Move Enemies
for(var i=0,l=enemies.length;i<l;i++){
enemies[i].move((enemies[i].rotation-90)*Math.PI/180,2);
for(var j=0,ll=shots.length;j<ll;j++){
if(enemies[i].distance(shots[j])<0){
score++;
enemies.splice(i--,1);
l--;
shots.splice(j--,1);
ll--;
}
}
}
La novedad en este código, es crear los nuevos fragmentos del asteroide al destruir uno grande. Para ello, antes de sumar el nuevo puntuaje, si el radio del asteroide era mayor a 5, crearemos tres nuevos asteroides de la mitad de radio del asteroide original. Estos nuevos asteroides tendrán la rotación del disparo mas 120 grados al anterior; de esta forma, los nuevos asteroides se expandirán de forma triangular al asteroide original, en dirección opuesta al origen de su explosión: if(enemies[i].radius>5){
for(var k=0;k<3;k++){
var e=new Circle(enemies[i].x,enemies[i].y,enemies[i].radius/2);
e.rotation=shots[j].rotation+120*k;
enemies.push(e);
}
}
Con esto tendremos el efecto deseado al destruir los asteroides grandes. Por último, solo queda dibujarlos: ctx.strokeStyle='#00f';
for(var i=0,l=enemies.length;i<l;i++)
enemies[i].drawImageArea(ctx,spritesheet, 0,10,40,40);
Dado que drawImageArea dibuja las imágenes de acuerdo al radio del círculo, no es necesario ningún código extra para dibujar los asteroides en los distintos tamaños.Código final:
(function(){
'use strict';
window.addEventListener('load',init,false);
var KEY_SPACE=32;
var KEY_LEFT=37;
var KEY_UP=38;
var KEY_RIGHT=39;
var KEY_DOWN=40;
var canvas=null,ctx=null;
var lastPress=null;
var pressing=[];
var score=0;
var aTimer=0;
var player=new Circle(150,100,5);
var shots=[];
var enemies=[];
var spritesheet=new Image();
var background=new Image();
spritesheet.src='assets/asteroids.png';
background.src='assets/nebula2.jpg';
function random(max){
return ~~(Math.random()*max);
}
function init(){
canvas=document.getElementById('canvas');
ctx=canvas.getContext('2d');
canvas.width=300;
canvas.height=200;
run();
repaint();
}
function run(){
setTimeout(run,50);
act(0.05);
}
function repaint(){
requestAnimationFrame(repaint);
paint(ctx);
}
function act(deltaTime){
// Set Rotation
if(pressing[KEY_RIGHT]){
player.rotation+=10;
}
if(pressing[KEY_LEFT]){
player.rotation-=10;
}
// Set Acceleration
if(pressing[KEY_UP]){
if(player.speed<5)
player.speed++;
}
if(pressing[KEY_DOWN]){
if(player.speed>-5)
player.speed--;
}
// Move Player
player.move((player.rotation-90)*Math.PI/180,player.speed);
// New Shot
if(lastPress==KEY_SPACE){
var s=new Circle(player.x,player.y,2.5);
s.rotation=player.rotation;
s.speed=player.speed+10;
s.timer=15;
shots.push(s);
}
// Move Shots
for(var i=0,l=shots.length;i<l;i++){
shots[i].timer--;
if(shots[i].timer<0){
shots.splice(i--,1);
l--;
continue;
}
shots[i].move((shots[i].rotation-90)*Math.PI/180,shots[i].speed);
}
// New Enemies
if(enemies.length<1){
for(var i=0;i<3;i++){
var e=new Circle(-20,-20,20);
e.rotation=random(360);
enemies.push(e);
}
}
// Move Enemies
for(var i=0,l=enemies.length;i<l;i++){
enemies[i].move((enemies[i].rotation-90)*Math.PI/180,2);
for(var j=0,ll=shots.length;j<ll;j++){
if(enemies[i].distance(shots[j])<0){
if(enemies[i].radius>5){
for(var k=0;k<3;k++){
var e=new Circle(enemies[i].x,enemies[i].y,enemies[i].radius/2);
e.rotation=shots[j].rotation+120*k;
enemies.push(e);
}
}
score++;
enemies.splice(i--,1);
l--;
shots.splice(j--,1);
ll--;
}
}
}
aTimer+=deltaTime;
if(aTimer>3600)
aTimer-=3600;
lastPress=null;
}
function paint(ctx){
ctx.fillStyle='#000';
if(background.width)
ctx.drawImage(background,0,0);
else
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.strokeStyle='#00f';
for(var i=0,l=enemies.length;i<l;i++)
enemies[i].drawImageArea(ctx,spritesheet, 0,10,40,40);
ctx.strokeStyle='#f00';
for(var i=0,l=shots.length;i<l;i++)
shots[i].drawImageArea(ctx,spritesheet, 30,(~~(aTimer*10)%2)*5,5,5);
ctx.strokeStyle='#0f0';
if(pressing[KEY_UP])
player.drawImageArea(ctx,spritesheet, (~~(aTimer*10)%3)*10,0,10,10);
else
player.drawImageArea(ctx,spritesheet, 0,0,10,10);
ctx.fillStyle='#fff';
//ctx.fillText('Rotation: '+player.rotation,0,20);
ctx.fillText('Score: '+score,0,20);
}
document.addEventListener('keydown',function(evt){
lastPress=evt.keyCode;
pressing[evt.keyCode]=true;
},false);
document.addEventListener('keyup',function(evt){
pressing[evt.keyCode]=false;
},false);
function Circle(x,y,radius){
this.x=(x==null)?0:x;
this.y=(y==null)?0:y;
this.radius=(radius==null)?0:radius;
//this.scale=1;
this.rotation=0;
this.speed=0;
this.timer=0;
}
Circle.prototype.distance=function(circle){
if(circle!=null){
var dx=this.x-circle.x;
var dy=this.y-circle.y;
return (Math.sqrt(dx*dx+dy*dy)-(this.radius+circle.radius));
}
}
Circle.prototype.move=function(angle,speed){
if(speed!=null){
this.x+=Math.cos(angle)*speed;
this.y+=Math.sin(angle)*speed;
// Out Screen
if(this.x>canvas.width)
this.x=0;
if(this.x<0)
this.x=canvas.width;
if(this.y>canvas.height)
this.y=0;
if(this.y<0)
this.y=canvas.height;
}
}
Circle.prototype.stroke=function(ctx){
ctx.beginPath();
ctx.arc(this.x,this.y,this.radius,0,Math.PI*2,true);
ctx.stroke();
}
Circle.prototype.drawImageArea=function(ctx,img,sx,sy,sw,sh){
if(img.width){
ctx.save();
ctx.translate(this.x,this.y);
//ctx.scale(this.scale,this.scale);
ctx.rotate(this.rotation*Math.PI/180);
ctx.drawImage(img,sx,sy,sw,sh,-this.radius,-this.radius,this.radius*2,this.radius*2);
ctx.restore();
}
else
this.stroke(ctx);
}
window.requestAnimationFrame=(function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function(callback){window.setTimeout(callback,17);};
})();
})();
No hay comentarios.:
Publicar un comentario