【国际象棋】棋盘游戏-微信小程序开发流程详解

news2024/11/25 12:22:58

与中国象棋类似的,还有国际象棋,知道有人爱玩,于是凭着好奇心,网上研究了一下,跟中国象棋有相似之处,玩法是有些许不一样,不知道象棋最早出于谁之手呢,抽空做一做,最终完成,玩一玩看着还不错吧,这里给讲一讲它的实现过程。

可能在国内的有些同学没有玩过国际版的象棋,在此简要说明以下规则,以便了解:

  • 后棋:👸皇后,不可越棋;
  • 王棋:🤴国王,限走一格,特殊走法,首次使用可与未使用过的车易位一次(向车方向走两格),若被吃掉就判断为败;
  • 象棋:🐘丞相,斜着走,不可越棋;
  • 马棋:🏇骑士,斜着走一格,再前进一格;
  • 车棋:🛞马车,不可斜着走,不可越棋;
  • 兵棋:♟️卒兵,只能前进一格,首次使用可前进两格,斜着吃,可吃过路兵(左右两边的格子),当前进到对方底线时需要升变换为除王和兵之外的其它棋子;

准备开始做了,打开微信开发者工具,新建一个项目时,如下图
在这里插入图片描述

例如,项目名称为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)

  • 参数xy是相对位置偏移量;
  • 参数next是执行下一步的方法,是递归调用的;

很难理解的话就先放着,等到自己水平提升以后,有好奇心再回来研究弄清楚就好

测试游戏

就讲到这里,小程序项目基本上就算完成了,运行的动图效果如下

在这里插入图片描述

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

请添加图片描述

国际象棋-特殊规则:王车易位

其余的细节动图这里就不展示了,从编写实现难度上看,国际象棋是要比中国象棋的实现要复杂一些,原理上大同小异,

想要小程序项目源码请点这里查看,在资源一栏下可找到 国际象棋-小程序项目源码,请放心下载,值得研究学习,感谢支持!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/478303.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

倾斜摄影超大场景的三维模型的顶层合并,提升模型在WEB三维展示效果

倾斜摄影超大场景的三维模型的顶层合并&#xff0c;提升模型在WEB三维展示效果 倾斜摄影超大场景的三维模型的顶层合并后&#xff0c;可以采取以下措施来提升模型在WEB三维展示效果&#xff1a; 1、优化模型数据&#xff1a;对于倾斜摄影超大场景的三维模型&#xff0c;需要进…

网络安全:namp扫描工具

-sP可以扫描一个网段ip以及状态和基本信息&#xff0c;10.1.1.2-3就是扫描2和3这两个ip的主机 -p可以扫描指定ip对应主机的端口号&#xff0c;可以是一个范围 nmap简单扫描&#xff1a;nmap 地址 检查地址是否在线以及open的端口号 在端口开放&#xff0c;不一定可以与对方正常…

数据库之事务隔离级别详解

事务隔离级别详解 一、事务的四大特性&#xff08;ACID&#xff09;1. 原子性(atomicity)&#xff1a;2. 一致性(consistency)&#xff1a;3. 隔离性(isolation)&#xff1a;4. 持久性(durability)&#xff1a; 二、事务的四种隔离级别1. 读未提交(Read uncommitted)&#xff1…

WSL怎么使用本机进行代理联网

文章目录 WSL怎么使用本机代理进行联网问题来源设置v2rayN设置wsl总结参考 WSL怎么使用本机代理进行联网 问题来源 使用WSL克隆github的代码网速很慢&#xff0c;无响应&#xff0c;导致项目无法下载&#xff0c;真的愁人。就想到为WSL设置xx上网&#xff0c;是否就会好很多。…

Photoshop如何使用文字之实例演示?

文章目录 0.引言1.给图像素材添加透明水印2.创建路径文字3.创建每日一签海报4.给图像添加复杂水印5.制作个人简历模板 0.引言 因科研等多场景需要进行绘图处理&#xff0c;笔者对PS进行了学习&#xff0c;本文通过《Photoshop2021入门教程》及其配套素材结合网上相关资料进行学…

React | React组件化开发

✨ 个人主页&#xff1a;CoderHing &#x1f5a5;️ React .js专栏&#xff1a;React .js React组件化开发 &#x1f64b;‍♂️ 个人简介&#xff1a;一个不甘平庸的平凡人&#x1f36c; &#x1f4ab; 系列专栏&#xff1a;吊打面试官系列 16天学会Vue 11天学会React Node…

不良条件视觉感知专栏(一)任务前言

前言 随着深度学习的流行&#xff0c;CNN的强大特征学习能力给计算机视觉领域带来了巨大的提升。2D/3D目标检测、语义分割是常见的视觉感知任务&#xff0c;本专栏我们将围绕着它们展开阐述。 本教程禁止转载。同时&#xff0c;本教程来自知识星球【CV技术指南】更多技术教程&…

Photoshop如何使用图像调色之实例演示?

文章目录 0.引言1.将一张偏冷调的图像调整成暖调2.将图像调整成不同季节色彩倾向3.变换花朵的颜色4.创建人像轮廓风景5.修饰蓝天白云6.调换花草颜色 0.引言 因科研等多场景需要进行绘图处理&#xff0c;笔者对PS进行了学习&#xff0c;本文通过《Photoshop2021入门教程》及其配…

【经典论文解读】YOLACT 实例分割(YOLOv5、YOLOv8实例分割的基础)

前言 YOLACT是经典的单阶段、实时、实例分割方法&#xff0c;在YOLOv5和YOLOv8中的实例分割&#xff0c;也是基于 YOLACT实现的&#xff0c;有必要理解一下它的模型结构和设计思路。 论文&#xff1a;YOLACT: Real-time Instance Segmentation 开源地址&#xff1a;https://gi…

01-Shiro550漏洞流程

1. 漏洞原理 Apache Shiro框架提供了记住密码的功能&#xff08;RememberMe&#xff09;&#xff0c;用户登录成功后会生成经过加密并编码的cookie。在服务端对rememberMe的cookie值&#xff0c;先base64解码然后AES解密再反序列化&#xff0c;就导致了反序列化RCE漏洞。 那么…

hadoop伪分布式搭建教程

官方参数文档 Apache Hadoop 3.3.5 – HDFS Users Guide Hadoop是一个分布式存储和计算框架&#xff0c;由以下几个组件组成&#xff1a; 1. Hadoop Distributed File System (HDFS)&#xff1a;Hadoop分布式文件系统&#xff0c;用于存储大量数据&#xff0c;并提供高可靠性和…

Spring源码:动态代理的增强顺序(AOP与事务的先后)

前文&#xff1a; 《Spring AOP源码&#xff1a;开启注解读取》 《Spring AOP源码2&#xff1a;查找增强器》 《Spring AOP源码3&#xff1a;实现代理》 《Spring事务源码&#xff1a;创建代理类》 《Spring事务源码&#xff1a;事务创建》 《Spring事务源码&#xff1a;…

(05)基础强化:字符串拘留池,格式化,StringBuilder,垃圾回收,弱引用

一、复习 1.什么是接口&#xff1f;说说你对接口的理解。 &#xff08;提示&#xff1a;概念、语法、应用场景&#xff0c;与抽象类的区别。说出最特别的&#xff09; 接口是一种规范、标准&#xff0c;一种抽象的概念&#xff0c;所以本身无法实现&#…

Redis基础——Java客户端Jedis

2.1.Jedis客户端 Jedis的官网地址&#xff1a; https://github.com/redis/jedis 2.1.1.快速入门 我们先来个快速入门&#xff1a; 1&#xff09;引入依赖&#xff1a; <!--jedis--> <dependency><groupId>redis.clients</groupId><artifactId&…

山东专升本计算机第九章-信息安全

信息安全 计算机病毒 考点 4病毒的定义与特点 定义 • 一组人为设计的程序满足一定条件即被激活 特点 • 可执行性 • 破坏性 • 占用系统资源 • 破坏或删除程序或数据文件 • 传染性 • 潜伏性 • 隐蔽性 • 针对性 • 宏病毒只感染docx • 衍生性 • 抗反病毒软…

102-Linux_I/O复用方法之poll

文章目录 1.poll系统调用的作用2.poll的原型3.poll支持的事件类型4.poll实现TCP服务器(1)服务器端代码:(2)客户端代码:(3)运行结果截图: 1.poll系统调用的作用 poll 系统调用和 select 类似&#xff0c;也是在指定时间内轮询一定数量的文件描述符&#xff0c;以测试其中是否有…

docker容器原理及简单且详细的使用

docker原理简单介绍 docker是一种虚拟化容器技术。 虚拟化&#xff1a;早期为了节约成本和学习只有在宿主机中基于 kvm&#xff08;基于内核的虚拟机&#xff09;等技术虚拟出来完整的操作系统&#xff0c;而这个完整的操作系统会大量的占用宿主机的硬件资源&#xff0c;当创建…

spring的安装 -- IEDA-创建 Java 工程的jar包教程--以及spring5的核心组件

目录 Spring Spring 基本介绍 Spring5 下载 进入 Spring5 进入 Spring5 的 github 进入 Spring5 的 github下拉 Access to Binaries, 进入 Spring -----拷贝下载地址&#xff0c;打开--- -----选择 5.3.8, 点击进入, 即可下载- 在线文档: 离 线 文 档 : 离 线 API: …

【刷题之路Ⅱ】LeetCode 92. 反转链表 II

【刷题之路Ⅱ】LeetCode 92. 反转链表 II 一、题目描述二、解题1、方法1——穿针引线法1.1、思路分析1.2、代码实现 2、方法2——针对进阶的头插法2.1、思路分析2.2、代码实现 一、题目描述 原题连接&#xff1a; 92. 反转链表 II 题目描述&#xff1a; 给你单链表的头指针 he…

基于全过程通道相关像素值顺序的彩色图像可逆数据隐藏

文献学习&#xff1a; 基于全过程通道相关像素值顺序的彩色图像可逆数据隐藏 原文题目&#xff1a; Reversible data hiding for color images based on pixel value order of overall process channel. 发表期刊&#xff1a; Signal Processing&#xff08;中科院二区&#xf…