VUE+Canvas五子棋游戏 VUE+Canvas实现简单五子棋游戏的全过程
登楼痕 人气:0前言
在布局上,五子棋相比那些目标是随机运动的游戏,实现起来相对简单许多,思路也很清晰,总共分为:
(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 class="gobang"> <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 lang="scss"> .gobang { #gobang { background: #2a4546; } } </style>
总结
加载全部内容