VUE+Canvas實現(xiàn)簡單五子棋游戲的全過程
前言
在布局上,五子棋相比那些目標(biāo)是隨機(jī)運動的游戲,實現(xiàn)起來相對簡單許多,思路也很清晰,總共分為:
(1)畫棋盤;
(2)監(jiān)聽點擊事件畫黑白棋子;
(3)每次落子之后判斷是否有5子相連,有則贏。
最復(fù)雜的恐怕就是如何判斷五子棋贏了,那么就先從簡單的開始,畫個棋盤吧~
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循環(huán)里,我們還初始化了一個15 * 15的二維數(shù)組,并全部填上0,沒錯,就是記錄落子的。

2、監(jiān)聽點擊事件畫黑白棋子
好了,我們在獲取dom的時候順便監(jiān)聽一下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);
}
很容易可以計算出點擊坐標(biāo)最近的那個棋盤交叉點,當(dāng)然,如果那里已經(jīng)落了子,就得return。然后在交點處畫上白子或者黑子,這里用漸變填充使棋子看起來更像那么回事。接著,在對應(yīng)的二維數(shù)組里記錄一下棋子狀況:
setResultArr(m, n) {
let _this = this;
_this.resultArr[m][n] = _this.whiteTurn ? 1 : 2; // 白棋為1;黑棋為2
}

3、檢查五子棋輸贏結(jié)果
輸贏結(jié)果怎么判斷?肉眼看去,無非就是以當(dāng)前落子為0,0原點建立坐標(biāo)系,然后判斷0°,180°,45°和135°四條線上是否有連續(xù)5子。相比于直接遍歷計數(shù),更好的方法就是取出四條線上的數(shù)據(jù),然后判斷是否有相連的5個1或者2字符。
假設(shè)我們落子的數(shù)組坐標(biāo)是[m, n]。
(1)橫線的結(jié)果數(shù)組字符串:this.resultArr[m].join('');
(2)豎線的結(jié)果數(shù)組字符串:
for(let i = 0; i<15; i++){
lineHorizontal.push(_this.resultArr[i][n]);
}
(3)135°(左上到右下):j從0-15,分別取this.resultArr[m - j][n -j]結(jié)果unshift進(jìn)臨時數(shù)組頭部,取this.resultArr[m + j][n + j]放到臨時數(shù)組尾部,行成結(jié)果;
(4)45°(左下到右上):j從0-15,分別取this.resultArr[m + j][n -j]結(jié)果unshift進(jìn)臨時數(shù)組頭部,取this.resultArr[m - j][n + j]放到臨時數(shù)組尾部,行成結(jié)果;
當(dāng)然這里面都是要判斷一下數(shù)組越界。
得到結(jié)果字符串后,我們判斷是否有“22222”或者“11111”這樣的字符串存在,有則說明勝利。
checkResult(m ,n){ // 判斷是否有5子相連
let _this = this;
let checkStr = _this.whiteTurn ? CheckStrWhite : CheckStrBlack;
// 取出[m,n]橫豎斜四條線的一維數(shù)組
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;
}
}
最后勝出,我們顯示一下是哪方獲勝。

至此,一個簡單的黑白棋游戲就做好了~~~~~
老規(guī)矩,源碼貼上:
<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: [] // 記錄棋子位置的數(shù)組
};
},
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]橫豎斜四條線的一維數(shù)組
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>
總結(jié)
到此這篇關(guān)于VUE+Canvas實現(xiàn)簡單五子棋游戲的文章就介紹到這了,更多相關(guān)VUE+Canvas五子棋游戲內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue使用cesium創(chuàng)建數(shù)據(jù)白模方式
這篇文章主要介紹了vue使用cesium創(chuàng)建數(shù)據(jù)白模方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-10-10
基于vue-ssr的靜態(tài)網(wǎng)站生成器VuePress 初體驗
VuePress為每一個由它生成的頁面提供預(yù)加載的html,不僅加載速度極佳,同時對seo非常友好。這篇文章主要介紹了基于vue-ssr的靜態(tài)網(wǎng)站生成器VuePress 初體驗,需要的朋友可以參考下2018-04-04
Vue響應(yīng)式原理及雙向數(shù)據(jù)綁定示例分析
這篇文章主要為大家介紹了Vue響應(yīng)式原理及雙向數(shù)據(jù)綁定的示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
Element-UI結(jié)合遞歸組件實現(xiàn)后臺管理系統(tǒng)左側(cè)菜單
在Vue.js中使用遞歸組件可以方便地構(gòu)建多層級的菜單結(jié)構(gòu),遞歸組件適用于處理具有嵌套關(guān)系的數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-09-09

