阅读 39 SEO

VUE+Canvas实现简单的五子棋游戏

VUE+Canvas实现简单的五子棋游戏

在布局上,五子棋相比那些目标是随机运动的游戏,实现起来相对简单许多,思路也很清晰,总共分为:


(1)画棋盘;


(2)监听点击事件画黑白棋子;


(3)每次落子之后判断是否有5子相连,有则赢。


最复杂的恐怕就是如何判断五子棋赢了,那么就先从简单的开始,画个棋盘吧~


1、画棋盘

棋盘很简单,我们画个15*15的棋盘,横线竖线相交错:


drawCheckerboard() {

      // 画棋盘

      let _this = this;

      _this.ctx.beginPath();

      _this.ctx.fillStyle = "#fff";

      _this.ctx.rect(0, 0, 450, 450);

      _this.ctx.fill();

      for (var i = 0; i < 15; i++) {

        _this.ctx.beginPath();

        _this.ctx.strokeStyle = "#D6D1D1";

        _this.ctx.moveTo(15 + i * 30, 15); //垂直方向画15根线,相距30px;

        _this.ctx.lineTo(15 + i * 30, 435);

        _this.ctx.stroke();

        _this.ctx.moveTo(15, 15 + i * 30); //水平方向画15根线,相距30px;

        _this.ctx.lineTo(435, 15 + i * 30);

        _this.ctx.stroke();

 

        _this.resultArr.push(new Array(15).fill(0));

      }

}

先用一个450 * 450 的正方形打底,四周留15宽度的空白,然后画上间隔为30的线。在for循环里,我们还初始化了一个15 * 15的二维数组,并全部填上0,没错,就是记录落子的。




2、监听点击事件画黑白棋子

好了,我们在获取dom的时候顺便监听一下click事件,来画棋子:


let container = document.getElementById("gobang");


container.addEventListener("click", _this.handleClick);


handleClick(event) {

      let x = event.offsetX - 70;

      let y = event.offsetY - 70;

      if (x < 15 || x > 435 || y < 15 || y > 435) {

        // 点出界的

        return;

      }

      this.drawChess(x, y);

      if(this.winGame){

        this.drawResult();

        return;

      }

      this.whiteTurn = !this.whiteTurn;

      this.drawText();

}

画棋子的代码:


drawChess(x, y) {

      let _this = this;

      let xLine = Math.round((x - 15) / 30); // 竖线第x条

      let yLine = Math.round((y - 15) / 30); // 横线第y条

      if(_this.resultArr[xLine][yLine] !== 0){

        return;

      }

      let grd = _this.ctx.createRadialGradient(

        xLine * 30 + 15,

        yLine * 30 + 15,

        4,

        xLine * 30 + 15,

        yLine * 30 + 15,

        10

      );

      grd.addColorStop(0, _this.whiteTurn ? "#fff" : "#4c4c4c");

      grd.addColorStop(1, _this.whiteTurn ? "#dadada" : "#000");

      _this.ctx.beginPath();

      _this.ctx.fillStyle = grd;

      _this.ctx.arc(

        xLine * 30 + 15,

        yLine * 30 + 15,

        10,

        0,

        2 * Math.PI,

        false

      );

      _this.ctx.fill();

      _this.ctx.closePath();

 

      _this.setResultArr(xLine, yLine);

      _this.checkResult(xLine, yLine);

}

很容易可以计算出点击坐标最近的那个棋盘交叉点,当然,如果那里已经落了子,就得return。然后在交点处画上白子或者黑子,这里用渐变填充使棋子看起来更像那么回事。接着,在对应的二维数组里记录一下棋子状况:


setResultArr(m, n) {

      let _this = this;

      _this.resultArr[m][n] = _this.whiteTurn ? 1 : 2; // 白棋为1;黑棋为2

 

}



3、检查五子棋输赢结果

输赢结果怎么判断?肉眼看去,无非就是以当前落子为0,0原点建立坐标系,然后判断0°,180°,45°和135°四条线上是否有连续5子。相比于直接遍历计数,更好的方法就是取出四条线上的数据,然后判断是否有相连的5个1或者2字符。


假设我们落子的数组坐标是[m, n]。


(1)横线的结果数组字符串:this.resultArr[m].join('');


(2)竖线的结果数组字符串:


for(let i = 0; i<15; i++){

        lineHorizontal.push(_this.resultArr[i][n]);


}


(3)135°(左上到右下):j从0-15,分别取this.resultArr[m - j][n -j]结果unshift进临时数组头部,取this.resultArr[m + j][n + j]放到临时数组尾部,行成结果;


(4)45°(左下到右上):j从0-15,分别取this.resultArr[m + j][n -j]结果unshift进临时数组头部,取this.resultArr[m - j][n + j]放到临时数组尾部,行成结果;


当然这里面都是要判断一下数组越界。


得到结果字符串后,我们判断是否有“22222”或者“11111”这样的字符串存在,有则说明胜利。


checkResult(m ,n){ // 判断是否有5子相连

      let _this = this;

      let checkStr = _this.whiteTurn ? CheckStrWhite : CheckStrBlack;

      // 取出[m,n]横竖斜四条线的一维数组

      let lineVertical = _this.resultArr[m].join('');

      if(lineVertical.indexOf(checkStr) > -1){

        _this.winGame = true;

        return;

      }

      let lineHorizontal = [];

      for(let i = 0; i<15; i++){

        lineHorizontal.push(_this.resultArr[i][n]);

      }

      lineHorizontal = lineHorizontal.join('');

      if(lineHorizontal.indexOf(checkStr) > -1){

        _this.winGame = true;

        return;

      }

      let line135 = [];

      for(let j = 0; j < 15; j++){

        if(m - j >= 0 && n - j >= 0){ // 左上角

          line135.unshift(_this.resultArr[m - j][n -j]);

        }

        if(j > 0 && m + j < 15 && n + j < 15){ // 右下角

          line135.push(_this.resultArr[m + j][n + j]);

        }

      }

      line135 = line135.join('');

      if(line135.indexOf(checkStr) > -1){

        _this.winGame = true;

        return;

      }

      let line45 = [];

      for(let j = 0; j < 15; j++){

        if(m + j < 15 && n - j >= 0){ // 右上角

          line45.unshift(_this.resultArr[m + j][n -j]);

        }

        if(j > 0 && m - j >=0 && n + j < 15){ // 左下角

          line45.push(_this.resultArr[m - j][n + j]);

        }

      }

      line45 = line45.join('');

      if(line45.indexOf(checkStr) > -1){

        _this.winGame = true;

        return;

      }

}

最后胜出,我们显示一下是哪方获胜。




至此,一个简单的黑白棋游戏就做好了~~~~~


老规矩,源码贴上:


<template>

  <div>

    <canvas id="gobang" width="800" height="600"></canvas>

  </div>

</template>

 

<script>

const CheckStrWhite = "11111";

const CheckStrBlack = "22222";

export default {

  name: "Gobang",

  data() {

    return {

      ctx: null,

      winGame: false,

      whiteTurn: false, // 白棋轮;true-黑棋轮

      resultArr: [] // 记录棋子位置的数组

    };

  },

  mounted() {

    let _this = this;

    let container = document.getElementById("gobang");

 

    container.addEventListener("click", _this.handleClick);

 

    _this.ctx = container.getContext("2d");

    _this.ctx.translate(70,70);

    _this.drawCheckerboard();

  },

  computed:{

    chessText(){

      return this.whiteTurn ? '白棋' : '黑棋';

    }

  },

  methods: {

    drawCheckerboard() {

      // 画棋盘

      let _this = this;

      _this.ctx.beginPath();

      _this.ctx.fillStyle = "#fff";

      _this.ctx.rect(0, 0, 450, 450);

      _this.ctx.fill();

      for (var i = 0; i < 15; i++) {

        _this.ctx.beginPath();

        _this.ctx.strokeStyle = "#D6D1D1";

        _this.ctx.moveTo(15 + i * 30, 15); //垂直方向画15根线,相距30px;

        _this.ctx.lineTo(15 + i * 30, 435);

        _this.ctx.stroke();

        _this.ctx.moveTo(15, 15 + i * 30); //水平方向画15根线,相距30px;棋盘为14*14;

        _this.ctx.lineTo(435, 15 + i * 30);

        _this.ctx.stroke();

 

        _this.resultArr.push(new Array(15).fill(0));

      }

      _this.drawText();

    },

    drawChess(x, y) {

      let _this = this;

      let xLine = Math.round((x - 15) / 30); // 竖线第x条

      let yLine = Math.round((y - 15) / 30); // 横线第y条

      if(_this.resultArr[xLine][yLine] !== 0){

        return;

      }

      let grd = _this.ctx.createRadialGradient(

        xLine * 30 + 15,

        yLine * 30 + 15,

        4,

        xLine * 30 + 15,

        yLine * 30 + 15,

        10

      );

      grd.addColorStop(0, _this.whiteTurn ? "#fff" : "#4c4c4c");

      grd.addColorStop(1, _this.whiteTurn ? "#dadada" : "#000");

      _this.ctx.beginPath();

      _this.ctx.fillStyle = grd;

      _this.ctx.arc(

        xLine * 30 + 15,

        yLine * 30 + 15,

        10,

        0,

        2 * Math.PI,

        false

      );

      _this.ctx.fill();

      _this.ctx.closePath();

 

      _this.setResultArr(xLine, yLine);

      _this.checkResult(xLine, yLine);

    },

    setResultArr(m, n) {

      let _this = this;

      _this.resultArr[m][n] = _this.whiteTurn ? 1 : 2; // 白棋为1;黑棋为2

 

    },

    

    checkResult(m ,n){ // 判断是否有5子相连

      let _this = this;

      let checkStr = _this.whiteTurn ? CheckStrWhite : CheckStrBlack;

      // 取出[m,n]横竖斜四条线的一维数组

      let lineVertical = _this.resultArr[m].join('');

      if(lineVertical.indexOf(checkStr) > -1){

        _this.winGame = true;

        return;

      }

      let lineHorizontal = [];

      for(let i = 0; i<15; i++){

        lineHorizontal.push(_this.resultArr[i][n]);

      }

      lineHorizontal = lineHorizontal.join('');

      if(lineHorizontal.indexOf(checkStr) > -1){

        _this.winGame = true;

        return;

      }

      let line135 = [];

      for(let j = 0; j < 15; j++){

        if(m - j >= 0 && n - j >= 0){ // 左上角

          line135.unshift(_this.resultArr[m - j][n -j]);

        }

        if(j > 0 && m + j < 15 && n + j < 15){ // 右下角

          line135.push(_this.resultArr[m + j][n + j]);

        }

      }

      line135 = line135.join('');

      if(line135.indexOf(checkStr) > -1){

        _this.winGame = true;

        return;

      }

      let line45 = [];

      for(let j = 0; j < 15; j++){

        if(m + j < 15 && n - j >= 0){ // 右上角

          line45.unshift(_this.resultArr[m + j][n -j]);

        }

        if(j > 0 && m - j >=0 && n + j < 15){ // 左下角

          line45.push(_this.resultArr[m - j][n + j]);

        }

      }

      line45 = line45.join('');

      if(line45.indexOf(checkStr) > -1){

        _this.winGame = true;

        return;

      }

    },

    drawText(){

      let _this = this;

      _this.ctx.clearRect(435 + 60, 0, 100, 70);

      _this.ctx.fillStyle = "#fff";

      _this.ctx.font="20px Arial";

      _this.ctx.fillText('本轮:' + _this.chessText, 435 + 70,  35);

    },

    drawResult(){

      let _this = this;

      _this.ctx.fillStyle = "#ff2424";

      _this.ctx.font="20px Arial";

      _this.ctx.fillText(_this.chessText+'胜!', 435 + 70,  70);

    },

    handleClick(event) {

      let x = event.offsetX - 70;

      let y = event.offsetY - 70;

      if (x < 15 || x > 435 || y < 15 || y > 435) {

        // 点出界的

        return;

      }

      this.drawChess(x, y);

      if(this.winGame){

        this.drawResult();

        return;

      }

      this.whiteTurn = !this.whiteTurn;

      this.drawText();

    }

  }

};

</script>

 

<!-- Add "scoped" attribute to limit CSS to this component only -->

<style scoped>

.gobang {

  #gobang {

    background: #2a4546;

  }

}

</style>

可怕,记录一下,留个念想

————————————————

版权声明:本文为CSDN博主「登楼痕」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/denglouhen/article/details/116485952


文章分类
后端
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 gxwowoo@163.com 举报,一经查实,本站将立刻删除。
相关推荐