新窗口打开 (opens new window)

# 关键点

  • 1 定义怪物属性:坐标 {x,y}及速度{vx,vy},每个都是一个对象,存于数组中
  • 2 canvas绘制
  • 3 requestAnimationFrame 重复绘制每帧画面
  • 4 边界检测,反向运动
  • 5 点击检测
  • 6 显示对话,长大动画
  • 7 关卡入场动画
  • 8 游戏数据的维护和绘制

# 拆解

为了更好的理解1-4,做了个简化的demo如下

//喷射点坐标
sx = w / 2;
sy = h / 2;

 //放置圆点对象的数组
var circles = [];

//初始速度
var vel = 10;

 //创建新的圆点并加入数组
function createCircle() {
  circles.push({
      x: sx,
      y: sy,
      v: {
          x: vel * Math.random(),
          y: vel * Math.random()
      }
  });
}

//清除画布内容
function clear() {
    $.fillStyle = 'black';
    $.fillRect(0,0,w,h);
}

//画圆点
function drawCircle() {
  clear();

  for (var i in circles) {
    if (circles[i].x < 5 || w - circles[i].x < 5) {
        circles[i].v.x *= -1;
        circles[i].v.y -= 1;
    }
    if (circles[i].y < 5 || h - circles[i].y < 5) {
        circles[i].v.y *= -1;
        circles[i].v.x -= 1;
    }

    circles[i].x += circles[i].v.x;
    circles[i].y += circles[i].v.y;

    $.fillStyle = 'red';
    $.beginPath();
    $.arc(circles[i].x, circles[i].y, 20, 0, Math.PI * 2, true);
    $.closePath();
    $.fill();
  }
}

//设置动画的回调函数,使动画持续播放
function updateCanvas() {
    requestAnimationFrame(updateCanvas),
    drawCircle();
}

window.addEventListener('mousedown',createCircle);

//页面载入后自行产生一个圆点
createCircle();

//触发循环回调,动画即可持续进行
updateCanvas();




# 5点击检测

//点击检测
can.addEventListener('mousedown',pickHandle)

function pickHandle(ev){
  //console.log(ev.clientX,ev.clientY)
  !data.gameOver && pool.forEach(function(e,i){
    var rules =  ev.clientX - e.x < spriteSize 
    	&&  ev.clientX - e.x >0 &&   ev.clientY - e.y  >0  &&  ev.clientY - e.y < spriteSize;
    if(rules && e.silent== undefined){
      e.showDialogue = true;//显示对话
    }
    if(rules && e.silent==true){
      //alert('被你找到了')
      e.v.x = 0;
      e.v.y = 0;
      e.toSize  = 80;
      nextPassTimeOut = setTimeout(function(){
        gameReset();//下一关
      },500)
    }
  })
}

其中pool是怪物的数组池,检测方法是根据坐标判断,通过鼠标坐标和数组池坐标匹配。 如上rules成立的前提是绘制怪物以xy为左上角为原点绘制,0 < 点击位置-怪物位置 < 怪物尺寸

# 6显示对话,长大动画

这两个功能需要在绘制时处理。

function draw(){
  //运动
  for(var i in pool){
    if(pool[i].x < 5 ||  WIDTH - pool[i].x < 5){
      pool[i].v.x *= -1;
    };
     if(pool[i].y < 5 ||  HEIGHT - pool[i].y < 5){
      pool[i].v.y *= -1;
    };
    pool[i].x += pool[i].v.x;
    pool[i].y += pool[i].v.y;

   
    //增长动画
    if(pool[i].toSize){
      if( pool[i].growingSize == pool[i].toSize){
         pool[i].growingSize = pool[i].toSize;
      }else{
         pool[i].growingSize+=1;
      }
      ctx.drawImage(img, pool[i].x, pool[i].y, pool[i].growingSize, pool[i].growingSize);
    }else{
      ctx.drawImage(img, pool[i].x, pool[i].y, spriteSize, spriteSize);
    }
    //显示会话
    if(pool[i].showDialogue){
      ctx.save();
      ctx.font = '14px Verdana';
      ctx.fillText(pool[i].dialogue,  pool[i].x,  pool[i].y);
      ctx.restore();
      //dialogueTime时间后,对话消失
      pool[i].dialogueTime<=0 ? pool[i].showDialogue = false  :  pool[i].dialogueTime-= deltaTime;
    }
    
  }
}

# 7关卡入场动画

//开场拉开帷幕动画
var Curtain = function(){
  this.width = WIDTH;
  this.height = HEIGHT;
  this.alpha = 1;
}
Curtain.prototype.play = function(){

  if(this.width>0 || this.alpha > 0){
    this.width < 0 ? 0 : this.width -=15; 
    this.alpha < 0.1 ? 0 : this.alpha -=0.002; 
    ctx.save();
    ctx.fillStyle = 'rgba(0,0,0,'+ this.alpha+')';
    ctx.fillRect(0, 0, this.width, this.height);
    ctx.restore();
  }
 
}

function loop(){
	curtain = new Curtain();
	curtain.play();
}

# 8游戏数据的维护和绘制 同理