目前已经实现的功能:
V1.0 : 实现棋子的布局,画布及游戏场景的初始化
V2.0 : 实现棋子的颜色改变
V3.0 :实现所有象棋的走棋规则
V4.0 : 实现所有棋子的吃子功能
GItHub源码下载地址:https://github.com/xiugangzhang/ChineseChess
如果需要直接体验的朋友也可以直接通过这个连接体验哈:
http://htmlpreview.github.io/?https://github.com/xiugangzhang/ChineseChess/blob/master/ChineseChess.html
其他项目推荐:
视频网站项目:https://github.com/xiugangzhang/vip.github.io(在线体验地址:http://vip.52tech.tech)
超级马里奥游戏项目:https://github.com/xiugangzhang/SuperMarioGame
如果觉得不错,也欢迎大家给个star支持一下,你的支持就是我前进的动力!
中国象棋项目源码:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title> <style> * { margin: 0; padding: 0; } </style></head> <body><canvas id="myCanvas" width="600px" height="660px"></canvas><script> //V1.0 : 实现棋子的布局,画布及游戏场景的初始化 //V2.0 : 实现棋子的颜色改变 //V3.0 :实现所有象棋的走棋规则 //V4.0 : 实现所有棋子的吃子功能 var canvas = document.getElementById("myCanvas"); var context = canvas.getContext("2d"); /** * 棋子对象 * @param x * @param y * @param color * @constructor */ function Chess(x, y, color, type) { this.x = x; this.y = y; this.color = color; this.type = type; // 记录颜色 this.chessColor = color; // 是否死掉 this.isDead = false; } // 成员函数 Chess.prototype = { _getChessText: function () { switch (this.type) { case "CHE": return "车"; case "MA": return "马"; case "PAO": return "炮"; case "BING": return "兵"; case "JIANG": return "将"; case "SHI": return "士"; case "XIANG": return "相"; } return "错误"; } } /** * 游戏场景 * @constructor */ function Game() { // 存放当前棋盘上所有的棋子 this._chesses = []; //初始位置 this._initX = 60; this._initY = 60; // 判断鼠标是否点击有棋子 this._selectedChess = null; this._initCount = 0; } Game.prototype = { // 场景的初始化 execute: function () { this._initChesses(); this._start(); }, _initChesses: function () { // 绘制棋子(初始位置) // 总共需要绘制32颗棋子 // 红色棋子 var redChess; for (var i = 0; i < 4; i++) { for (var j = 0; j < 9; j++) { if ((i == 0 && j == 0) || (j == 8 && i == 0)) { // 把棋子的颜色传进去 redChess = new Chess(j, i, "red", "CHE"); this._chesses.push(redChess); } else if ((j == 1 && i == 0) || (j == 7 && i == 0 )) { redChess = new Chess(j, i, "red", "MA"); this._chesses.push(redChess); } else if ((j == 2 && i == 0) || (j == 6 && i == 0)) { redChess = new Chess(j, i, "red", "XIANG"); this._chesses.push(redChess); } else if ((j == 3 && i == 0) || (j == 5 && i == 0)) { redChess = new Chess(j, i, "red", "SHI"); this._chesses.push(redChess); } else if ((j == 4 && i == 0)) { redChess = new Chess(j, i, "red", "JIANG"); this._chesses.push(redChess); } else if ((j == 1 && i == 2) || (j == 7 && i == 2)) { redChess = new Chess(j, i, "red", "PAO"); this._chesses.push(redChess); } else if ((j == 0 && i == 3) || (j == 2 && i == 3) || (j == 4 && i == 3) || (j == 6 && i == 3) || (j == 8 && i == 3)) { redChess = new Chess(j, i, "red", "BING"); this._chesses.push(redChess); } } } // 绘制黑色棋子 var blackChess; for (var i = 6; i < 10; i++) { for (var j = 0; j < 10; j++) { if ((j == 0 && i == 9) || (j == 8 && i == 9)) { blackChess = new Chess(j, i, "black", "CHE"); this._chesses.push(blackChess); } else if ((j == 1 && i == 9) || (j == 7 && i == 9)) { blackChess = new Chess(j, i, "black", "MA"); this._chesses.push(blackChess); } else if ((j == 2 && i == 9) || (j == 6 && i == 9)) { blackChess = new Chess(j, i, "black", "XIANG"); this._chesses.push(blackChess); } else if ((j == 3 && i == 9) || (j == 5 && i == 9)) { blackChess = new Chess(j, i, "black", "SHI"); this._chesses.push(blackChess); } else if (j == 4 && i == 9) { blackChess = new Chess(j, i, "black", "JIANG"); this._chesses.push(blackChess); } else if ((j == 1 && i == 7) || (j == 7 && i == 7)) { blackChess = new Chess(j, i, "black", "PAO"); this._chesses.push(blackChess); } else if ((j == 0 && i == 6) || (j == 2 && i == 6) || (j == 4 && i == 6) || (j == 6 && i == 6) || (j == 8 && (i == 6))) { blackChess = new Chess(j, i, "black", "BING"); this._chesses.push(blackChess); } } } //console.log(redChess); //console.log(blackChess); console.log(this._chesses); }, /** * 绘制棋子 车马相士将 * @private */ _drawChesses: function () { var isDeadChess = null; // 根据游戏中棋子的数目来绘制棋子 // 游戏中的棋子始终是32颗, 因此只能根据棋子是否存活来决定是否绘制这个棋子(原来的位置还是有棋子, 这是让他隐藏起来) for (var i = 0; i < this._chesses.length; i++) { var chess = this._chesses[i]; // 原来的位置还是有棋子, 这是让他隐藏起来 if (!chess.isDead) { // 只有当这个棋子的属性isDead = false; 才绘制这个棋子 // 根据棋子的属性来绘制 context.fillStyle = "#C78843"; context.beginPath(); // 绘制棋子(注意要把棋子的初始位置复位) context.arc(chess.x * 60 + this._initX, chess.y * 60 + this._initY, 25, 0, Math.PI * 2, true); context.closePath(); context.fill(); // 绘制文本 if (chess.color == "red") { context.fillStyle = 'red'; } if (chess.color == "black") { context.fillStyle = 'black'; } if (chess.color == "blue") { context.fillStyle = 'blue'; } context.font = '43px 华文新魏'; context.fillText(chess._getChessText(), chess.x * 60 + this._initX - 22, chess.y * 60 + this._initY + 10); } else { isDeadChess = chess; } } // 有棋子被吃了,就重新开始 //this._selectedChess = null; if (this._initCount == 0) { //console.log(chess.x+","+chess.y+","+chess.type+"已经隐藏"); } }, _drawChessboard: function () { context.strokeStyle = "black"; // 绘制棋盘外边界 context.lineWidth = 3; context.beginPath(); context.moveTo(0, 0); context.lineTo(600, 0); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(0, 0); context.lineTo(0, 660); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(600, 0); context.lineTo(600, 660); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(0, 660); context.lineTo(660, 660); context.closePath(); context.stroke(); // 内部的外边界 context.beginPath(); context.moveTo(40, 40); context.lineTo(40, 620); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(40, 40); context.lineTo(560, 40); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(560, 40); context.lineTo(560, 620); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(560, 620); context.lineTo(40, 620); context.closePath(); context.stroke(); //棋盘横线 context.lineWidth = 1; for (var i = 1; i < 11; i++) { context.beginPath(); context.moveTo(60, 60 * i); context.lineTo(540, 60 * i); context.closePath(); context.stroke(); } // 棋盘纵线 for (var i = 1; i < 10; i++) { context.beginPath(); context.moveTo(i * 60, 60); context.lineTo(i * 60, 300); context.closePath(); context.stroke(); } for (var i = 1; i < 10; i++) { context.beginPath(); context.moveTo(i * 60, 360); context.lineTo(i * 60, 600); context.closePath(); context.stroke(); } context.beginPath(); context.moveTo(60, 300); context.lineTo(60, 360); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(540, 300); context.lineTo(540, 360); context.closePath(); context.stroke(); // 棋盘斜线 context.beginPath(); context.moveTo(240, 60); context.lineTo(360, 180); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(360, 60); context.lineTo(240, 180); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(240, 480); context.lineTo(360, 600); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(360, 480); context.lineTo(240, 600); context.closePath(); context.stroke(); // 绘制炮的位置(左上) context.beginPath(); context.moveTo(115, 160); context.lineTo(115, 175); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(115, 175); context.lineTo(100, 175); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(125, 160); context.lineTo(125, 175); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(125, 175); context.lineTo(140, 175); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(115, 185); context.lineTo(115, 200); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(115, 185); context.lineTo(100, 185); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(125, 185); context.lineTo(125, 200); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(125, 185); context.lineTo(140, 185); context.closePath(); context.stroke(); // 右上 context.beginPath(); context.moveTo(475, 175); context.lineTo(460, 175); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(475, 175); context.lineTo(475, 160); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(485, 160); context.lineTo(485, 175); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(485, 175); context.lineTo(500, 175); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(485, 185); context.lineTo(485, 200); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(485, 185); context.lineTo(500, 185); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(475, 185); context.lineTo(475, 200); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(475, 185); context.lineTo(460, 185); context.closePath(); context.stroke(); // 左下 context.beginPath(); context.moveTo(115, 485); context.lineTo(115, 500); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(115, 485); context.lineTo(100, 485); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(115, 475); context.lineTo(100, 475); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(115, 475); context.lineTo(115, 460); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(125, 475); context.lineTo(140, 475); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(125, 475); context.lineTo(125, 460); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(125, 485); context.lineTo(140, 485); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(125, 485); context.lineTo(125, 500); context.closePath(); context.stroke(); // 右下 context.beginPath(); context.moveTo(475, 485); context.lineTo(475, 500); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(475, 485); context.lineTo(460, 485); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(475, 475); context.lineTo(475, 460); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(475, 475); context.lineTo(460, 475); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(485, 475); context.lineTo(500, 475); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(485, 475); context.lineTo(485, 460); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(485, 485); context.lineTo(500, 485); context.closePath(); context.stroke(); context.beginPath(); context.moveTo(485, 485); context.lineTo(485, 500); context.closePath(); context.stroke(); }, _drawnTitle: function () { //context.clearRect(0, 0, canvas.width, canvas.height); context.fillStyle = 'black'; context.font = '40px 华文新魏'; context.fillText("楚河", 130, 340); context.fillText("汉界", 380, 340); }, _start: function () { // 监听事件, 把触发这个事件的对象也传递过去 document.addEventListener("click", this._mouseClick.bind(this)); var that = this; // 利用定时器不断重绘canvas var timer = setInterval(function () { that._draw(); }, 30); }, _draw: function () { context.clearRect(0, 0, canvas.width, canvas.height); this._drawChessboard(); // 绘制棋子 this._drawChesses(); this._drawnTitle(); }, _mouseClick: function (e) { // 把棋盘归一化 var px = e.clientX - this._initX + 25; var py = e.clientY - this._initY + 25; //console.log(px + " "+py); // 当前鼠标点击棋盘的位置 var x = parseInt(px / 60); var y = parseInt(py / 60); if (x > 8 || x < 0 || y > 9 || y < 0 || px < 0) { return; } console.log(this._selectedChess); // 之前没有选中棋子(一直点击的是空白位置) if (this._selectedChess == null) { //如果当前鼠标点击的位置有棋子,就是选中了棋子 for (var i = 0; i < this._chesses.length; i++) { var chess = this._chesses[i]; // 每次拿出来棋子都要看一下这个棋子是不是还活着, 已经死了的棋子不处理 if (!chess.isDead) { if (x == chess.x && y == chess.y) { // 如果有棋子 this._selectedChess = chess; this._selectedChess.color = "blue"; break; } else { // 没有棋子 //alert("没有棋子"); //break; } } } } else { var color = this._selectedChess.chessColor; var srcX = this._selectedChess.x; var srcY = this._selectedChess.y; var destX = x; var destY = y; this._canKill(srcX, srcY, destX, destY); // 如果已经有了选择的棋子,就看这一次点击的位置有没有棋子 /*var chess = this._getChess(x, y); if (chess){ // 如果这个位置有棋子 console.log("有棋子"); if (chess.chessColor == this._selectedChess.chessColor){ // 如果这两个棋子的颜色相同 alert("请不要自相残杀!"); //同一个棋子点击了两下就恢复初始状态 //this._selectedChess = null; this._selectedChess.color = this._selectedChess.chessColor; this._selectedChess = null; return; } else { // 如果颜色不相同 // 首先看一下这个棋子能不能移动 var type = this._selectedChess.type; var srcX = this._selectedChess.x; var srcY = this._selectedChess.y; var destX = x; var destY = y; var color = this._selectedChess.chessColor; // 我们先来判断能不能移动棋子, 然后再来判断能不能吃子 switch (type) { case "CHE": if (this._canMoveChe(color, srcX, srcY, destX, destY)) { // 移动棋子 this._moveChess(srcX, srcY, destX, destY); return; } else { // 点击了颜色相同但是属性不同的其他棋子 alert("不能移动棋子!"); // 颜色相同的同类棋子就恢复初始状态 this._selectedChess.color = this._selectedChess.chessColor; this._selectedChess = null; return; } break; case "MA": if (this._canMoveMa(color, srcX, srcY, destX, destY)) { // 移动棋子 this._moveChess(srcX, srcY, destX, destY); return; } else { // 点击了颜色相同但是属性不同的其他棋子 alert("不能移动棋子!"); // 颜色相同的同类棋子就恢复初始状态 this._selectedChess.color = this._selectedChess.chessColor; this._selectedChess = null; return; &n