飞道的博客

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

340人阅读  评论(0)

之前的canvas小游戏系列欢迎大家戳:

《VUE实现一个Flappy Bird~~~》

《VUE+Canvas实现上吊火柴人猜单词游戏》

《VUE+Canvas 实现桌面弹球消砖块小游戏》

《VUE+Canvas实现雷霆战机打字类小游戏》

《VUE+Canvas实现财神爷接元宝小游戏》


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

(1)画棋盘;

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

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

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

1、画棋盘

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


  
  1. drawCheckerboard() {
  2. // 画棋盘
  3. let _this = this;
  4. _this.ctx.beginPath();
  5. _this.ctx.fillStyle = "#fff";
  6. _this.ctx.rect( 0, 0, 450, 450);
  7. _this.ctx.fill();
  8. for ( var i = 0; i < 15; i++) {
  9. _this.ctx.beginPath();
  10. _this.ctx.strokeStyle = "#D6D1D1";
  11. _this.ctx.moveTo( 15 + i * 30, 15); //垂直方向画15根线,相距30px;
  12. _this.ctx.lineTo( 15 + i * 30, 435);
  13. _this.ctx.stroke();
  14. _this.ctx.moveTo( 15, 15 + i * 30); //水平方向画15根线,相距30px;
  15. _this.ctx.lineTo( 435, 15 + i * 30);
  16. _this.ctx.stroke();
  17. _this.resultArr.push( new Array( 15).fill( 0));
  18. }
  19. }

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

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

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

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

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


  
  1. handleClick(event) {
  2. let x = event.offsetX - 70;
  3. let y = event.offsetY - 70;
  4. if (x < 15 || x > 435 || y < 15 || y > 435) {
  5. // 点出界的
  6. return;
  7. }
  8. this.drawChess(x, y);
  9. if( this.winGame){
  10. this.drawResult();
  11. return;
  12. }
  13. this.whiteTurn = ! this.whiteTurn;
  14. this.drawText();
  15. }

画棋子的代码:


  
  1. drawChess(x, y) {
  2. let _this = this;
  3. let xLine = Math.round((x - 15) / 30); // 竖线第x条
  4. let yLine = Math.round((y - 15) / 30); // 横线第y条
  5. if(_this.resultArr[xLine][yLine] !== 0){
  6. return;
  7. }
  8. let grd = _this.ctx.createRadialGradient(
  9. xLine * 30 + 15,
  10. yLine * 30 + 15,
  11. 4,
  12. xLine * 30 + 15,
  13. yLine * 30 + 15,
  14. 10
  15. );
  16. grd.addColorStop( 0, _this.whiteTurn ? "#fff" : "#4c4c4c");
  17. grd.addColorStop( 1, _this.whiteTurn ? "#dadada" : "#000");
  18. _this.ctx.beginPath();
  19. _this.ctx.fillStyle = grd;
  20. _this.ctx.arc(
  21. xLine * 30 + 15,
  22. yLine * 30 + 15,
  23. 10,
  24. 0,
  25. 2 * Math.PI,
  26. false
  27. );
  28. _this.ctx.fill();
  29. _this.ctx.closePath();
  30. _this.setResultArr(xLine, yLine);
  31. _this.checkResult(xLine, yLine);
  32. }

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


  
  1. setResultArr(m, n) {
  2. let _this = this;
  3. _this.resultArr[m][n] = _this.whiteTurn ? 1 : 2; // 白棋为1;黑棋为2
  4. }

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”这样的字符串存在,有则说明胜利。


  
  1. checkResult(m ,n){ // 判断是否有5子相连
  2. let _this = this;
  3. let checkStr = _this.whiteTurn ? CheckStrWhite : CheckStrBlack;
  4. // 取出[m,n]横竖斜四条线的一维数组
  5. let lineVertical = _this.resultArr[m].join( '');
  6. if(lineVertical.indexOf(checkStr) > -1){
  7. _this.winGame = true;
  8. return;
  9. }
  10. let lineHorizontal = [];
  11. for( let i = 0; i< 15; i++){
  12. lineHorizontal.push(_this.resultArr[i][n]);
  13. }
  14. lineHorizontal = lineHorizontal.join( '');
  15. if(lineHorizontal.indexOf(checkStr) > -1){
  16. _this.winGame = true;
  17. return;
  18. }
  19. let line135 = [];
  20. for( let j = 0; j < 15; j++){
  21. if(m - j >= 0 && n - j >= 0){ // 左上角
  22. line135.unshift(_this.resultArr[m - j][n -j]);
  23. }
  24. if(j > 0 && m + j < 15 && n + j < 15){ // 右下角
  25. line135.push(_this.resultArr[m + j][n + j]);
  26. }
  27. }
  28. line135 = line135.join( '');
  29. if(line135.indexOf(checkStr) > -1){
  30. _this.winGame = true;
  31. return;
  32. }
  33. let line45 = [];
  34. for( let j = 0; j < 15; j++){
  35. if(m + j < 15 && n - j >= 0){ // 右上角
  36. line45.unshift(_this.resultArr[m + j][n -j]);
  37. }
  38. if(j > 0 && m - j >= 0 && n + j < 15){ // 左下角
  39. line45.push(_this.resultArr[m - j][n + j]);
  40. }
  41. }
  42. line45 = line45.join( '');
  43. if(line45.indexOf(checkStr) > -1){
  44. _this.winGame = true;
  45. return;
  46. }
  47. }

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

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

老规矩,源码贴上


  
  1. <template>
  2. <div class="gobang">
  3. <canvas id="gobang" width="800" height="600"> </canvas>
  4. </div>
  5. </template>
  6. <script>
  7. const CheckStrWhite = "11111";
  8. const CheckStrBlack = "22222";
  9. export default {
  10. name: "Gobang",
  11. data() {
  12. return {
  13. ctx: null,
  14. winGame: false,
  15. whiteTurn: false, // 白棋轮;true-黑棋轮
  16. resultArr: [] // 记录棋子位置的数组
  17. };
  18. },
  19. mounted() {
  20. let _this = this;
  21. let container = document.getElementById( "gobang");
  22. container.addEventListener( "click", _this.handleClick);
  23. _this.ctx = container.getContext( "2d");
  24. _this.ctx.translate( 70, 70);
  25. _this.drawCheckerboard();
  26. },
  27. computed:{
  28. chessText(){
  29. return this.whiteTurn ? '白棋' : '黑棋';
  30. }
  31. },
  32. methods: {
  33. drawCheckerboard() {
  34. // 画棋盘
  35. let _this = this;
  36. _this.ctx.beginPath();
  37. _this.ctx.fillStyle = "#fff";
  38. _this.ctx.rect( 0, 0, 450, 450);
  39. _this.ctx.fill();
  40. for ( var i = 0; i < 15; i++) {
  41. _this.ctx.beginPath();
  42. _this.ctx.strokeStyle = "#D6D1D1";
  43. _this.ctx.moveTo( 15 + i * 30, 15); //垂直方向画15根线,相距30px;
  44. _this.ctx.lineTo( 15 + i * 30, 435);
  45. _this.ctx.stroke();
  46. _this.ctx.moveTo( 15, 15 + i * 30); //水平方向画15根线,相距30px;棋盘为14*14;
  47. _this.ctx.lineTo( 435, 15 + i * 30);
  48. _this.ctx.stroke();
  49. _this.resultArr.push( new Array( 15).fill( 0));
  50. }
  51. _this.drawText();
  52. },
  53. drawChess(x, y) {
  54. let _this = this;
  55. let xLine = Math.round((x - 15) / 30); // 竖线第x条
  56. let yLine = Math.round((y - 15) / 30); // 横线第y条
  57. if(_this.resultArr[xLine][yLine] !== 0){
  58. return;
  59. }
  60. let grd = _this.ctx.createRadialGradient(
  61. xLine * 30 + 15,
  62. yLine * 30 + 15,
  63. 4,
  64. xLine * 30 + 15,
  65. yLine * 30 + 15,
  66. 10
  67. );
  68. grd.addColorStop( 0, _this.whiteTurn ? "#fff" : "#4c4c4c");
  69. grd.addColorStop( 1, _this.whiteTurn ? "#dadada" : "#000");
  70. _this.ctx.beginPath();
  71. _this.ctx.fillStyle = grd;
  72. _this.ctx.arc(
  73. xLine * 30 + 15,
  74. yLine * 30 + 15,
  75. 10,
  76. 0,
  77. 2 * Math.PI,
  78. false
  79. );
  80. _this.ctx.fill();
  81. _this.ctx.closePath();
  82. _this.setResultArr(xLine, yLine);
  83. _this.checkResult(xLine, yLine);
  84. },
  85. setResultArr(m, n) {
  86. let _this = this;
  87. _this.resultArr[m][n] = _this.whiteTurn ? 1 : 2; // 白棋为1;黑棋为2
  88. },
  89. checkResult(m ,n){ // 判断是否有5子相连
  90. let _this = this;
  91. let checkStr = _this.whiteTurn ? CheckStrWhite : CheckStrBlack;
  92. // 取出[m,n]横竖斜四条线的一维数组
  93. let lineVertical = _this.resultArr[m].join( '');
  94. if(lineVertical.indexOf(checkStr) > -1){
  95. _this.winGame = true;
  96. return;
  97. }
  98. let lineHorizontal = [];
  99. for( let i = 0; i< 15; i++){
  100. lineHorizontal.push(_this.resultArr[i][n]);
  101. }
  102. lineHorizontal = lineHorizontal.join( '');
  103. if(lineHorizontal.indexOf(checkStr) > -1){
  104. _this.winGame = true;
  105. return;
  106. }
  107. let line135 = [];
  108. for( let j = 0; j < 15; j++){
  109. if(m - j >= 0 && n - j >= 0){ // 左上角
  110. line135.unshift(_this.resultArr[m - j][n -j]);
  111. }
  112. if(j > 0 && m + j < 15 && n + j < 15){ // 右下角
  113. line135.push(_this.resultArr[m + j][n + j]);
  114. }
  115. }
  116. line135 = line135.join( '');
  117. if(line135.indexOf(checkStr) > -1){
  118. _this.winGame = true;
  119. return;
  120. }
  121. let line45 = [];
  122. for( let j = 0; j < 15; j++){
  123. if(m + j < 15 && n - j >= 0){ // 右上角
  124. line45.unshift(_this.resultArr[m + j][n -j]);
  125. }
  126. if(j > 0 && m - j >= 0 && n + j < 15){ // 左下角
  127. line45.push(_this.resultArr[m - j][n + j]);
  128. }
  129. }
  130. line45 = line45.join( '');
  131. if(line45.indexOf(checkStr) > -1){
  132. _this.winGame = true;
  133. return;
  134. }
  135. },
  136. drawText(){
  137. let _this = this;
  138. _this.ctx.clearRect( 435 + 60, 0, 100, 70);
  139. _this.ctx.fillStyle = "#fff";
  140. _this.ctx.font= "20px Arial";
  141. _this.ctx.fillText( '本轮:' + _this.chessText, 435 + 70, 35);
  142. },
  143. drawResult(){
  144. let _this = this;
  145. _this.ctx.fillStyle = "#ff2424";
  146. _this.ctx.font= "20px Arial";
  147. _this.ctx.fillText(_this.chessText+ '胜!', 435 + 70, 70);
  148. },
  149. handleClick(event) {
  150. let x = event.offsetX - 70;
  151. let y = event.offsetY - 70;
  152. if (x < 15 || x > 435 || y < 15 || y > 435) {
  153. // 点出界的
  154. return;
  155. }
  156. this.drawChess(x, y);
  157. if( this.winGame){
  158. this.drawResult();
  159. return;
  160. }
  161. this.whiteTurn = ! this.whiteTurn;
  162. this.drawText();
  163. }
  164. }
  165. };
  166. </script>
  167. <!-- Add "scoped" attribute to limit CSS to this component only -->
  168. <style scoped lang="scss">
  169. .gobang {
  170. #gobang {
  171. background: #2a4546;
  172. }
  173. }
  174. </style>

 


转载:https://blog.csdn.net/denglouhen/article/details/116485952
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场