欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

VUE+Canvas 實現(xiàn)桌面彈球消磚塊小游戲的示例代碼

 更新時間:2021年04月13日 09:13:52   作者:登樓痕  
這篇文章主要介紹了VUE+Canvas 實現(xiàn)桌面彈球消磚塊小游戲,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

大家都玩過彈球消磚塊游戲,左右鍵控制最底端的一個小木板平移,接住掉落的小球,將球彈起后消除畫面上方的一堆磚塊。

那么用VUE+Canvas如何來實現(xiàn)呢?實現(xiàn)思路很簡單,首先來拆分一下要畫在畫布上的內(nèi)容:

(1)用鍵盤左右按鍵控制平移的木板;

(2)在畫布內(nèi)四處彈跳的小球;

(3)固定在畫面上方,并且被球碰撞后就消失的一堆磚塊。

將上述三種對象,用requestAnimationFrame()函數(shù)平移運動起來,再結(jié)合各種碰撞檢查,就可以得到最終的結(jié)果。

先看看最終的效果:

一、左右平移的木板

最底部的木板是最簡單的一部分,因為木板的y坐標(biāo)是固定的,我們設(shè)置木板的初始參數(shù),包括其寬度,高度,平移速度等,然后實現(xiàn)畫木板的函數(shù):

pannel: {
        x: 0,
        y: 0,
        height: 8,
        width: 100,
        speed: 8,
        dx: 0
},
 
....
 
drawPannel() {
      this.drawRoundRect(
        this.pannel.x,
        this.pannel.y,
        this.pannel.width,
        this.pannel.height,
        5
      );
},
drawRoundRect(x, y, width, height, radius) { // 畫圓角矩形
      this.ctx.beginPath();
      this.ctx.arc(x + radius, y + radius, radius, Math.PI, (Math.PI * 3) / 2);
      this.ctx.lineTo(width - radius + x, y);
      this.ctx.arc(
        width - radius + x,
        radius + y,
        radius,
        (Math.PI * 3) / 2,
        Math.PI * 2
      );
      this.ctx.lineTo(width + x, height + y - radius);
      this.ctx.arc(
        width - radius + x,
        height - radius + y,
        radius,
        0,
        (Math.PI * 1) / 2
      );
      this.ctx.lineTo(radius + x, height + y);
      this.ctx.arc(
        radius + x,
        height - radius + y,
        radius,
        (Math.PI * 1) / 2,
        Math.PI
      );
      this.ctx.fillStyle = "#008b8b";
      this.ctx.fill();
      this.ctx.closePath();
}

程序初始化的時候,監(jiān)聽鍵盤的左右方向鍵,來移動木板,通過長度判斷是否移動到了左右邊界使其不能繼續(xù)移出畫面:

document.onkeydown = function(e) {
      let key = window.event.keyCode;
      if (key === 37) {
        // 左鍵
        _this.pannel.dx = -_this.pannel.speed;
      } else if (key === 39) {
        // 右鍵
        _this.pannel.dx = _this.pannel.speed;
      }
};
document.onkeyup = function(e) {
      _this.pannel.dx = 0;
};
....
 
 movePannel() {
      this.pannel.x += this.pannel.dx;
      if (this.pannel.x > this.clientWidth - this.pannel.width) {
        this.pannel.x = this.clientWidth - this.pannel.width;
      } else if (this.pannel.x < 0) {
        this.pannel.x = 0;
      }
},

二、彈跳的小球和碰撞檢測

小球的運動和木板類似,只是不僅有dx的偏移,還有dy的偏移。

而且還要有碰撞檢測:

(1)當(dāng)碰撞的是上、右、左墻壁以及木板上的時候則反彈;

(2)當(dāng)碰撞到是木板以外的下邊界的時候,則輸?shù)粲螒颍?/p>

(3)當(dāng)碰撞的是磚塊的時候,被碰的磚塊消失,分?jǐn)?shù)+1,小球反彈。

于是和木板一樣,將小球部分分為畫小球函數(shù)drawBall()和小球運動函數(shù)moveBall():

drawBall() {
      this.ctx.beginPath();
      this.ctx.arc(this.ball.x, this.ball.y, this.ball.r, 0, 2 * Math.PI);
      this.ctx.fillStyle = "#008b8b";
      this.ctx.fill();
      this.ctx.closePath();
},
moveBall() {
      this.ball.x += this.ball.dx;
      this.ball.y += this.ball.dy;
      this.breaksHandle();
      this.edgeHandle();
},
breaksHandle() {
      // 觸碰磚塊檢測
      this.breaks.forEach(item => {
        if (item.show) {
          if (
            this.ball.x + this.ball.r > item.x &&
            this.ball.x - this.ball.r < item.x + this.breaksConfig.width &&
            this.ball.y + this.ball.r > item.y &&
            this.ball.y - this.ball.r < item.y + this.breaksConfig.height
          ) {
            item.show = false;
            this.ball.dy *= -1;
            this.score ++ ;
            if(this.showBreaksCount === 0){
              this.gameOver = true;
            }
          }
        }
      });
},
edgeHandle() {
      // 邊緣檢測
      // 碰到頂部反彈
      if (this.ball.y - this.ball.r < 0) {
        this.ball.dy = -this.ball.dy;
      }
      if (
        // 碰到左右墻壁
        this.ball.x - this.ball.r < 0 ||
        this.ball.x + this.ball.r > this.clientWidth
      ) {
        this.ball.dx = -this.ball.dx;
      }
      if (
        this.ball.x >= this.pannel.x &&
        this.ball.x <= this.pannel.x + this.pannel.width &&
        this.ball.y + this.ball.r >= this.clientHeight - this.pannel.height
      ) {
        // 球的x在板子范圍內(nèi)并觸碰到了板子
        this.ball.dy *= -1;
      } else if (
        (this.ball.x < this.pannel.x ||
          this.ball.x > this.pannel.x + this.pannel.width) &&
        this.ball.y + this.ball.r >= this.clientHeight
      ) {
        // 球碰到了底邊緣了
        this.gameOver = true;
        this.getCurshBreaks();
      }
}

三、磚塊的生成

磚塊的生成也比較簡單,這里我們初始了一些數(shù)據(jù):

breaksConfig: {
        row: 6, // 排數(shù)
        height: 25, // 磚塊高度
        width: 130, // 磚塊寬度
        radius: 5, // 矩形圓角
        space: 0, // 間距
        colunm: 6 // 列數(shù)
}

根據(jù)這些配置項以及畫布寬度,我們可以計算出每個磚塊的橫向間隙是多少:

// 計算得出磚塊縫隙寬度
      this.breaksConfig.space = Math.floor(
        (this.clientWidth -
          this.breaksConfig.width * this.breaksConfig.colunm) /
          (this.breaksConfig.colunm + 1)
      );

于是我們可以得到每個磚塊在畫布中的x,y坐標(biāo)(指的磚塊左上角的坐標(biāo))

for (let i = 0; i < _this.breaksConfig.row; i++) {
        for (let j = 0; j < _this.breaksConfig.colunm; j++) {
          _this.breaks.push({
            x: this.breaksConfig.space * (j + 1) + this.breaksConfig.width * j,
            y: 10 * (i + 1) + this.breaksConfig.height * i,
            show: true
          });
        }
      }

再加上繪制磚塊的函數(shù):

drawBreaks() {
      let _this = this;
      _this.breaks.forEach(item => {
        if (item.show) {
          _this.drawRoundRect(
            item.x,
            item.y,
            _this.breaksConfig.width,
            _this.breaksConfig.height,
            _this.breaksConfig.radius
          );
        }
      });
}

四、讓上面三個部分動起來

(function animloop() {
      if (!_this.gameOver) {
        _this.movePannel();
        _this.moveBall();
        _this.drawAll();
      } else {
        _this.drawCrushBreaks();
      }
      window.requestAnimationFrame(animloop);
})();
....
 drawAll() {
      this.ctx.clearRect(0, 0, this.clientWidth, this.clientHeight);
      this.drawPannel();
      this.drawBall();
      this.drawScore();
      this.drawBreaks();
}

五、游戲結(jié)束后的效果

在最開始的動圖里可以看到,游戲結(jié)束后,磚塊粉碎成了若干的小球掉落,這個其實和畫單獨的小球類似,思路就是把剩余的磚塊中心坐標(biāo)處生產(chǎn)若干大小不等,運動軌跡不等,顏色不等的小球,然后繼續(xù)動畫。

getCurshBreaks() {
      let _this = this;
      this.breaks.forEach(item => {
        if (item.show) {
          item.show = false;
          for (let i = 0; i < 8; i++) { // 每個磚塊粉碎為8個小球
            this.crushBalls.push({
              x: item.x + this.breaksConfig.width / 2,
              y: item.y + this.breaksConfig.height / 2,
              dx: _this.getRandomArbitrary(-6, 6),
              dy: _this.getRandomArbitrary(-6, 6),
              r: _this.getRandomArbitrary(1, 4),
              color: _this.getRandomColor()
            });
          }
        }
      });
},
drawCrushBreaks() {
      this.ctx.clearRect(0, 0, this.clientWidth, this.clientHeight);
      this.crushBalls.forEach(item => {
        this.ctx.beginPath();
        this.ctx.arc(item.x, item.y, item.r, 0, 2 * Math.PI);
        this.ctx.fillStyle = item.color;
        this.ctx.fill();
        this.ctx.closePath();
        item.x += item.dx;
        item.y += item.dy;
        if (
          // 碰到左右墻壁
          item.x - item.r < 0 ||
          item.x + item.r > this.clientWidth
        ) {
          item.dx = -item.dx;
        }
        if (
          // 碰到上下墻壁
          item.y - item.r < 0 ||
          item.y + item.r > this.clientHeight
        ) {
          item.dy = -item.dy;
        }
      });
},

以上就是桌面彈球消磚塊小游戲的實現(xiàn)思路和部分代碼,實現(xiàn)起來很簡單,兩三百行代碼就可以實現(xiàn)這個小游戲。在小球的運動上可以進(jìn)行持續(xù)優(yōu)化,并且也可以增加難度選項操作。

最后附上全部的vue文件代碼,供大家參考學(xué)習(xí):

<template>
  <div class="break-ball">
    <canvas id="breakBall" width="900" height="600"></canvas>
    <div class="container" v-if="gameOver">
      <div class="dialog">
        <p class="once-again">本輪分?jǐn)?shù):{{score}}分</p>
        <p class="once-again">真好玩!</p>
        <p class="once-again">再來一次~~</p>
        <el-button class="once-again-btn" @click="init">開始</el-button>
      </div>
    </div>
  </div>
</template>
 
<script>
const randomColor = [
  "#FF95CA",
  "#00E3E3",
  "#00E3E3",
  "#6F00D2",
  "#6F00D2",
  "#C2C287",
  "#ECFFFF",
  "#FFDC35",
  "#93FF93",
  "#d0d0d0"
];
export default {
  name: "BreakBall",
  data() {
    return {
      clientWidth: 0,
      clientHeight: 0,
      ctx: null,
      crushBalls: [],
      pannel: {
        x: 0,
        y: 0,
        height: 8,
        width: 100,
        speed: 8,
        dx: 0
      },
      ball: {
        x: 0,
        y: 0,
        r: 8,
        dx: -4,
        dy: -4
      },
      score: 0,
      gameOver: false,
      breaks: [],
      breaksConfig: {
        row: 6, // 排數(shù)
        height: 25, // 磚塊高度
        width: 130, // 磚塊寬度
        radius: 5, // 矩形圓角
        space: 0, // 間距
        colunm: 6 // 列數(shù)
      }
    };
  },
  mounted() {
    let _this = this;
    let container = document.getElementById("breakBall");
    this.ctx = container.getContext("2d");
    this.clientHeight = container.height;
    this.clientWidth = container.width;
    _this.init();
    document.onkeydown = function(e) {
      let key = window.event.keyCode;
      if (key === 37) {
        // 左鍵
        _this.pannel.dx = -_this.pannel.speed;
      } else if (key === 39) {
        // 右鍵
        _this.pannel.dx = _this.pannel.speed;
      }
    };
    document.onkeyup = function(e) {
      _this.pannel.dx = 0;
    };
    (function animloop() {
      if (!_this.gameOver) {
        _this.movePannel();
        _this.moveBall();
        _this.drawAll();
      } else {
        _this.drawCrushBreaks();
      }
      window.requestAnimationFrame(animloop);
    })();
  },
  computed:{
    showBreaksCount(){
      return this.breaks.filter(item=>{
        return item.show;
      }).length;
    }
  },
  methods: {
    init() {
      let _this = this;
      _this.gameOver = false;
      this.pannel.y = this.clientHeight - this.pannel.height;
      this.pannel.x = this.clientWidth / 2 - this.pannel.width / 2;
      this.ball.y = this.clientHeight / 2;
      this.ball.x = this.clientWidth / 2;
      this.score = 0;
      this.ball.dx = [-1,1][Math.floor(Math.random() * 2)]*4;
      this.ball.dy = [-1,1][Math.floor(Math.random() * 2)]*4;
      this.crushBalls = [];
      this.breaks = [];
      // 計算得出磚塊縫隙寬度
      this.breaksConfig.space = Math.floor(
        (this.clientWidth -
          this.breaksConfig.width * this.breaksConfig.colunm) /
          (this.breaksConfig.colunm + 1)
      );
 
      for (let i = 0; i < _this.breaksConfig.row; i++) {
        for (let j = 0; j < _this.breaksConfig.colunm; j++) {
          _this.breaks.push({
            x: this.breaksConfig.space * (j + 1) + this.breaksConfig.width * j,
            y: 10 * (i + 1) + this.breaksConfig.height * i,
            show: true
          });
        }
      }
    },
    drawAll() {
      this.ctx.clearRect(0, 0, this.clientWidth, this.clientHeight);
      this.drawPannel();
      this.drawBall();
      this.drawScore();
      this.drawBreaks();
    },
    movePannel() {
      this.pannel.x += this.pannel.dx;
      if (this.pannel.x > this.clientWidth - this.pannel.width) {
        this.pannel.x = this.clientWidth - this.pannel.width;
      } else if (this.pannel.x < 0) {
        this.pannel.x = 0;
      }
    },
    moveBall() {
      this.ball.x += this.ball.dx;
      this.ball.y += this.ball.dy;
      this.breaksHandle();
      this.edgeHandle();
    },
    breaksHandle() {
      // 觸碰磚塊檢測
      this.breaks.forEach(item => {
        if (item.show) {
          if (
            this.ball.x + this.ball.r > item.x &&
            this.ball.x - this.ball.r < item.x + this.breaksConfig.width &&
            this.ball.y + this.ball.r > item.y &&
            this.ball.y - this.ball.r < item.y + this.breaksConfig.height
          ) {
            item.show = false;
            this.ball.dy *= -1;
            this.score ++ ;
            if(this.showBreaksCount === 0){
              this.gameOver = true;
            }
          }
        }
      });
    },
    edgeHandle() {
      // 邊緣檢測
      // 碰到頂部反彈
      if (this.ball.y - this.ball.r < 0) {
        this.ball.dy = -this.ball.dy;
      }
      if (
        // 碰到左右墻壁
        this.ball.x - this.ball.r < 0 ||
        this.ball.x + this.ball.r > this.clientWidth
      ) {
        this.ball.dx = -this.ball.dx;
      }
      if (
        this.ball.x >= this.pannel.x &&
        this.ball.x <= this.pannel.x + this.pannel.width &&
        this.ball.y + this.ball.r >= this.clientHeight - this.pannel.height
      ) {
        // 球的x在板子范圍內(nèi)并觸碰到了板子
        this.ball.dy *= -1;
      } else if (
        (this.ball.x < this.pannel.x ||
          this.ball.x > this.pannel.x + this.pannel.width) &&
        this.ball.y + this.ball.r >= this.clientHeight
      ) {
        // 球碰到了底邊緣了
        this.gameOver = true;
        this.getCurshBreaks();
      }
    },
    drawScore(){
      this.ctx.beginPath();
      this.ctx.font="14px Arial";
      this.ctx.fillStyle = "#FFF";
      this.ctx.fillText("分?jǐn)?shù):"+this.score,10,this.clientHeight-14);
      this.ctx.closePath();
    },
    drawCrushBreaks() {
      this.ctx.clearRect(0, 0, this.clientWidth, this.clientHeight);
      this.crushBalls.forEach(item => {
        this.ctx.beginPath();
        this.ctx.arc(item.x, item.y, item.r, 0, 2 * Math.PI);
        this.ctx.fillStyle = item.color;
        this.ctx.fill();
        this.ctx.closePath();
        item.x += item.dx;
        item.y += item.dy;
        if (
          // 碰到左右墻壁
          item.x - item.r < 0 ||
          item.x + item.r > this.clientWidth
        ) {
          item.dx = -item.dx;
        }
        if (
          // 碰到上下墻壁
          item.y - item.r < 0 ||
          item.y + item.r > this.clientHeight
        ) {
          item.dy = -item.dy;
        }
      });
    },
    getRandomColor() {
      return randomColor[Math.floor(Math.random() * randomColor.length)];
    },
    getRandomArbitrary(min, max) {
      return Math.random() * (max - min) + min;
    },
    getCurshBreaks() {
      let _this = this;
      this.breaks.forEach(item => {
        if (item.show) {
          item.show = false;
          for (let i = 0; i < 8; i++) {
            this.crushBalls.push({
              x: item.x + this.breaksConfig.width / 2,
              y: item.y + this.breaksConfig.height / 2,
              dx: _this.getRandomArbitrary(-6, 6),
              dy: _this.getRandomArbitrary(-6, 6),
              r: _this.getRandomArbitrary(1, 4),
              color: _this.getRandomColor()
            });
          }
        }
      });
    },
    drawBall() {
      this.ctx.beginPath();
      this.ctx.arc(this.ball.x, this.ball.y, this.ball.r, 0, 2 * Math.PI);
      this.ctx.fillStyle = "#008b8b";
      this.ctx.fill();
      this.ctx.closePath();
    },
    drawPannel() {
      this.drawRoundRect(
        this.pannel.x,
        this.pannel.y,
        this.pannel.width,
        this.pannel.height,
        5
      );
    },
    drawRoundRect(x, y, width, height, radius) {
      this.ctx.beginPath();
      this.ctx.arc(x + radius, y + radius, radius, Math.PI, (Math.PI * 3) / 2);
      this.ctx.lineTo(width - radius + x, y);
      this.ctx.arc(
        width - radius + x,
        radius + y,
        radius,
        (Math.PI * 3) / 2,
        Math.PI * 2
      );
      this.ctx.lineTo(width + x, height + y - radius);
      this.ctx.arc(
        width - radius + x,
        height - radius + y,
        radius,
        0,
        (Math.PI * 1) / 2
      );
      this.ctx.lineTo(radius + x, height + y);
      this.ctx.arc(
        radius + x,
        height - radius + y,
        radius,
        (Math.PI * 1) / 2,
        Math.PI
      );
      this.ctx.fillStyle = "#008b8b";
      this.ctx.fill();
      this.ctx.closePath();
    },
    drawBreaks() {
      let _this = this;
      _this.breaks.forEach(item => {
        if (item.show) {
          _this.drawRoundRect(
            item.x,
            item.y,
            _this.breaksConfig.width,
            _this.breaksConfig.height,
            _this.breaksConfig.radius
          );
        }
      });
    }
  }
};
</script>
 
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
.break-ball {
  width: 900px;
  height: 600px;
  position: relative;
  #breakBall {
    background: #2a4546;
  }
  .container {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background-color: rgba(0, 0, 0, 0.3);
    text-align: center;
    font-size: 0;
    white-space: nowrap;
    overflow: auto;
  }
  .container:after {
    content: "";
    display: inline-block;
    height: 100%;
    vertical-align: middle;
  }
  .dialog {
    width: 400px;
    height: 300px;
    background: rgba(255, 255, 255, 0.5);
    box-shadow: 3px 3px 6px 3px rgba(0, 0, 0, 0.3);
    display: inline-block;
    vertical-align: middle;
    text-align: left;
    font-size: 28px;
    color: #fff;
    font-weight: 600;
    border-radius: 10px;
    white-space: normal;
    text-align: center;
    .once-again-btn {
      background: #1f9a9a;
      border: none;
      color: #fff;
    }
  }
}
</style>

到此這篇關(guān)于VUE+Canvas 實現(xiàn)桌面彈球消磚塊小游戲的文章就介紹到這了,更多相關(guān)vue彈球消磚塊小游戲內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論