与中国象棋类似的,还有国际象棋,知道有人爱玩,于是凭着好奇心,网上研究了一下,跟中国象棋有相似之处,玩法是有些许不一样,不知道象棋最早出于谁之手呢,抽空做一做,最终完成,玩一玩看着还不错吧,这里给讲一讲它的实现过程。
可能在国内的有些同学没有玩过国际版的象棋,在此简要说明以下规则,以便了解:
后棋:👸皇后,不可越棋;王棋:🤴国王,限走一格,特殊走法,首次使用可与未使用过的车易位一次(向车方向走两格),若被吃掉就判断为败;象棋:🐘丞相,斜着走,不可越棋;马棋:🏇骑士,斜着走一格,再前进一格;车棋:🛞马车,不可斜着走,不可越棋;兵棋:♟️卒兵,只能前进一格,首次使用可前进两格,斜着吃,可吃过路兵(左右两边的格子),当前进到对方底线时需要升变换为除王和兵之外的其它棋子;
准备开始做了,打开微信开发者工具,新建一个项目时,如下图

讯享网
例如,项目名称为
chess_game,依次选择
- 小程序
- 不使用云服务
- 使用JavaScript - 基础模板
这时,开发工具会自动创建生成一些文件,不用管它,
新建一个游戏页面,文件在pages/game/game.wxml,页面的布局大致如下
<view class="page"> <view class="top-content"> <view class="game-panel"> <image class="bg" src="{
{bgImg}}" /> <image class="bg" src="{
{chessesImg}}" /> <canvas class="fore" id="canv" type="2d" bindtouchstart="onTouchStart"></canvas> </view> <view class="padding"> <text>🔈 {
{showFlagStatus}}</text> </view> </view> <scroll-view scroll-y="true" class="scroll-view"> <view class="scroll-view-content"> <!-- other layout --> </view> </scroll-view> </view>
讯享网
上面布局中,使用两个图片组件作为静动态背景,还有一个画布用于绘制
先看一下,要做出来的页面是这样的,看如下运行显示效果

再去修改一下主页(第一个页面),文件在pages/index/index.js,
在加载事件里添加一段代码,可实现自动跳转到游戏页面
讯享网wx.navigateTo({
url: '/pages/game/game', })
同时打开新建的游戏页面逻辑代码,文件在pages/game/game.js,
看好了,接下来开始写游戏逻辑代码,
获取画布组件
首先从页面初次渲染完成时开始写,获取到画布canvas组件,并绑定触摸开始事件,看如下代码
Page({
/ * 页面的初始数据 */ data: {
bgImg:'',//棋盘背景图 chessesImg:'',//棋子布局图 innertHtml:'',//游戏说明内容 showFlagStatus:'请选择任意一方棋子为先手' }, / * 生命周期函数--监听页面初次渲染完成 */ onReady() {
wx.createSelectorQuery().select('#canv').fields({
size:true, node:true }, res=>{
//此处省略...获取并设置画布实例 this.canvasData = {
canvas:res.node, context:res.node.getContext('2d') }; //初始化棋盘 this.initChessBg(); }).exec() }, / * 触摸开始事件 */ onTouchStart(e){
const touch = e.touches[0]; //判断是否游戏结束 正在移动棋子 if(this.isGameEnd || this.animatiing) return; const {
grids, nSize, myFlag } = this.chessImgData; //根据坐标点查找指定格子数据 let grid = grids.find(g=>g.left<=touch.x && g.left+nSize>touch.x && g.top<=touch.y && g.top+nSize>touch.y); //此处省略了实现下棋的规则逻辑,这个要复杂一些,稍后会讲 //判断是否自己来下棋 if (myFlag!=null && myFlag!=grid.uid) return; //将选择的棋子 用矩形框画出来 this.drawSelectGrid(grid); },
生成棋盘
画布组件获取到后,可以在上面绘制棋盘了,
在初始化棋盘方法initChessBg()里实现,生成棋盘图片数据,显示在页面上,代码如下
讯享网const {
canvas, context:ctx } = this.canvasData; //先画一个灰色背景 ctx.fillStyle = '#bebebe'; ctx.rect(0,0,canvas.width,canvas.height); ctx.fill(); //在画一格白色的格子,每隔一格绘制 ctx.fillStyle = '#ffffff'; const cols = Cols;//常量值 8列 let size = Math.floor(canvas.width/cols); const grids = []; //绘制所有格子 棋盘 for(let r=0; r<cols; r++){
for(let c=0; c<cols; c++){
let grid = {
//...格子数据 }; //此处省略了... ctx.rect(grid.left,grid.top,size,size); ctx.fill(); grids.push(grid); } } //将画出来的生成图片数据 let base64 = canvas.toDataURL(); //该清空了 ctx.clearRect(0,0,canvas.width,canvas.height); //定义格子的配置数据 let chessImgData = {
grids, //... chesses: [],//所有棋子存放在这里 myFlag: null, }; //此处省略了...所有棋子数据 this.chessImgData = chessImgData; //将棋子图片全部绘制出来 let foreImg = canvas.createImage(); foreImg.onload=()=>{
chessImgData.foreImg = foreImg; //绘制所有棋子 this.drawAllChess(); }; foreImg.onerror=err=>{
console.error(err) }; //从静态资源中加载棋子分布的图片 foreImg.src='/static/chess.png'; this.setData({
bgImg:base64 });
把图片弄到背景图片中,这样就可以了,看看显示的棋盘,运行效果如下

生成所有棋子
棋盘绘制好了,还差点什么呢,还要绘制所有棋子,

使用方法drawAllChess()可画出所有的棋子,在棋盘之上布局,
然后,生成图片,弄到画布的下层,叠加起来,类似图层,看如下代码
const {
canvas, context:ctx } = this.canvasData; const {
chessImgData } = this; //避免污染,先清理一下画布 ctx.clearRect(0,0,canvas.width,canvas.height); chessImgData.grids.forEach(g=>{
//格子上没有棋子,就不绘制 if(g.id==undefined) return; let chess = chessImgData.chesses.find(p=>p.id==g.id && p.uid==g.uid); this.drawChess(chess,g); }); //绘制完成,生成图片,显示到页面上 let base64 = canvas.toDataURL(); ctx.clearRect(0,0,canvas.width,canvas.height); this.setData({
chessesImg:base64 })
上面用到绘制棋子的方法是
drawChess(),传入的参数是棋子和格子,可以从格子上绘制棋子
实现移动棋子
讲到这里,不得不说,这里实现用户选择棋子移动的逻辑要复杂一些,看看实现步骤,能否看懂全靠领悟,
从用户开始触摸发生的事件里,根据触摸坐标获取到的指定格子为grid,作为判断,看如下代码,
讯享网//上次选择的格子 let selectGrid = this.selectGrid; //格子上是否有棋子,通过棋子id判断 if (grid.id!=undefined){
//uid 是用户id,通过myFlag来指定哪个用户可以下棋 if (selectGrid && selectGrid.uid!=undefined && selectGrid.uid==myFlag){
//判断不是自己的棋,就吃棋 if (grid.uid!=selectGrid.uid){
//通过走棋规则方法判断是否可以移动棋子 if (this.isMoveChess(selectGrid,grid)){
//置反myFlag方法,自己下棋后,给对方下 this.reverseMyFlag(selectGrid.uid); //更新移动棋的数据,貌似没用,实际上是用于吃过路兵判断的 this.updateMoveData(selectGrid,grid); //吃棋方法,包括动画逻辑 this.takeChess(selectGrid,grid); //判断棋id,若吃掉是对方的王,则游戏结束 if(grid.id==1) this.endGame(selectGrid.uid); else if(selectGrid.id==5){
//判断兵是否到对方底线,国际象棋的规则:兵升变 if((selectGrid.uid==1 && grid.y==0) || (selectGrid.uid!=1 && grid.y==Cols-1)){
//弹出选择对话框,玩家要选择把兵变换成其它棋子 this.showModalSelectChangeChess(grid); } } return; } else {
//国际象棋的规则:吃过路兵 const {
movedData } = this; //通过之前棋子移动的数据,判断是否吃过路兵 if(movedData && movedData.id==grid.id && grid.id==5 && movedData.x2==grid.x && movedData.y2==grid.y && movedData.x1==movedData.x2 && movedData.uid!=selectGrid.uid){
let offsetX = 0; //...此处省略了 if(offsetX!=0 && selectGrid.y==grid.y){
//...此处省略了,这里处理吃掉过路兵 this.reverseMyFlag(selectGrid.uid); this.updateMoveData(selectGrid,grid); this.takeChess(selectGrid,grid); } } } } } }else{
//判断自己的棋子 if (selectGrid && selectGrid.uid==myFlag){
//是否可以移动 if (this.isMoveChess(selectGrid,grid)){
this.reverseMyFlag(selectGrid.uid); this.updateMoveData(selectGrid,grid); //移动棋子 产生的动画是异步处理的, 当动画完成时会调用replaceChess() 会将两个格子数据(包括棋子)替换 this.moveChess(selectGrid,grid,()=>this.replaceChess(selectGrid,grid)); //判断是兵 当前进到对方底线时,就按国际象棋规则 兵升变 处理 if(selectGrid.id==5){
if((selectGrid.uid==1 && grid.y==0) || (selectGrid.uid!=1 && grid.y==Cols-1)){
this.showModalSelectChangeChess(grid); } } return; } //判断是否是王的棋子,是否要朝车方向走,按照国际象棋规则:王车易位 处理 else if(selectGrid.x==4 && selectGrid.isUsed!=true && grid.id==undefined && (selectGrid.y==0 || selectGrid.y==Cols-1)){
let offsetX=0; let chess; //...此处省略了,判断王棋子是否未使用过,满足王车易位条件 if(offsetX!=0 && chess.id==4 && chess.isUsed!=true){
//...扫描王前进的路线 let scan = () => {
//...此处省略了 }; if(scan()){
//可以走了,王和车同时移动,这里会出现两次移动动画效果:王先过去,然后让车过来 this.moveChess(selectGrid,grid,()=>{
this.reverseMyFlag(selectGrid.uid); //...此处省略了 this.updateMoveData(selectGrid,grid); this.moveChess(selectGrid,grid,()=>this.replaceChess(selectGrid,grid)); }); } } } } }
实现下棋规则
下棋规则的判断实现逻辑看似易懂,要通过代码实现就要复杂多了,实现过程需要有耐心,思路要清晰,一步一步来,相信自己,
上面已经讲出 “吃过路兵,兵升变,王车易位”的规则判断逻辑了,
还有其余的下棋规则,都是交给方法isMoveChess(selectGrid,grid)去处理的,看如下代码
//用point保存一下上次选择的格子坐标 const point = {
x:selectGrid.x, y:selectGrid.y }; //定义预测棋子是否可移动的方法 const isMove = (x,y,next) => {
point.x = selectGrid.x+x; point.y = selectGrid.y+y; //...此处省略了 return false; }; //根据不同的棋子来判断走棋规则,如果可以走,就返回true; //对照['后','王','象','马','车','兵'][selectGrid.id] switch(selectGrid.id){
case 0: {
//...此处省略了,都与王的走棋大概一致,得看怎么调用isMove() } break; case 1: {
//王的走棋规则,是这样的调用的 if(isMove(-1,-1)) return true; if(isMove(0,-1)) return true; if(isMove(1,-1)) return true; if(isMove(1,0)) return true; if(isMove(1,1)) return true; if(isMove(0,1)) return true; if(isMove(-1,1))return true; if(isMove(-1,0)) return true; } break; case 2: {
//...此处省略了 } break; case 3: {
//...此处省略了 } break; case 4: {
//...此处省略了 } break; case 5: {
//...此处省略了 } break; default: //其它,打印测试用的,正常的话,是不会执行到此处 console.log({
name:ChessNames[selectGrid.id], selectGrid }) } return false;
从上面代码来看,几乎所有走棋判断都会用到预测棋子是否可移动的方法
isMove(x,y,next),
- 参数
x与y是相对位置偏移量;- 参数
next是执行下一步的方法,是递归调用的;很难理解的话就先放着,等到自己水平提升以后,有好奇心再回来研究弄清楚就好
测试游戏
就讲到这里,小程序项目基本上就算完成了,运行的动图效果如下

国际象棋-特殊规则:吃过路兵

国际象棋-特殊规则:王车易位
其余的细节动图这里就不展示了,从编写实现难度上看,国际象棋是要比中国象棋的实现要复杂一些,原理上大同小异,
想要小程序项目源码请点这里查看,在资源一栏下可找到 国际象棋-小程序项目源码,请放心下载,值得研究学习,感谢支持!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/13598.html