【跳棋】跳棋游戏-多人游戏-微信小程序开发流程详解

看到跳棋游戏,一个2到6人可一起玩的游戏,于是联想起,自己上小学时候陪同学们玩过的弹珠游戏,是不是跟跳棋游戏很像呢,看了跳棋玩法,有兴趣就研究,这里把跳棋游戏给弄出来了,想知道地图怎么画,对此感兴趣的同学可以研究研究,让家人孩子们一起参与游戏互动吧,珍惜陪伴的幸福时光。

打开微信开发工具,选择小程序,创建一个项目,

例如项目名称为miniprogram_chinese_draughts,然后选择以下,再确定创建

  • AppID 使用自己的测试号
  • 不使用云服务
  • JavaScript - 基础模板

开始页

新建小程序项目后,开发工具会自动生成一些基础模板文件,

在位置/pages/index/index这里,把index.wxml改成开始页面,添加一个选择人数的表单,和按钮,显示如下
在这里插入图片描述

跳棋游戏中的是不能有5个人参与的,因为第5个参与者没有对手,不公平吧,

index.js这里修改一下,添加一个方法enterGame(e),点击开始游戏会调用到,代码如下,

enterGame(e){
    
    
    const {
    
     count } = e.detail.value;
    wx.navigateTo({
    
    
      url: '/pages/game/game?args='+count,
    })
}

跳转到游戏页面,同时会传一个参数count表示参与人数

游戏页面

添加一个游戏页面,文件位置在pages/game/game,页面game.wxml里的添加布局,内容如下

<view class="page">
    <canvas class="canvas" id="canv" type="2d" bindtouchstart="onTouchStart"></canvas>
    <scroll-view class="scroll-view" scroll-y="true">
        <view class="game-info">
        <!-- 这里显示游戏状态信息的布局 --->
        </view>
    </scroll-view>
</view>

其中canvas画布,是最重要的组件,可用来绘制画面,
另外放置的组件是用于显示每个参与选手(玩家)的游戏状态,

初始化

开始页有传来的参数count,就从游戏页面onLoad(options)加载方法里获取,代码如下

/**
 * 页面的初始数据
 */
data: {
    
    
    players:[],//参与的玩家列表
    currentPlayer:0,//指定哪个玩家下棋
},
onLoad(options) {
    
    
    let index = 0;//默认的选择
    //如果从上一页有传来参数,这里改变默认的选择
    if(options?.args) index = ['2','3','4','6'].indexOf(options.args);
    //按照选择人数分配玩家
    let players = [[0,5],[1,2,5],[1,2,3,4],[0,1,2,3,4,5]][index].map(id=>{
    
    
        return {
    
    
            id: parseInt(id),//玩家的id
            color: ChessColors[id],//对应的颜色
            step: 0,//开始步数
            rank: 0,//用于记录的排名
        }
    });
    //更新玩家列表显示
    this.setData({
    
    
        players
    })
},

接下来,从游戏页面的准备完成事件onReady()方法里,

获取一下页面布局中的canvas,然后绑定触摸开始事件,代码如下

onReady() {
    
    
  wx.createSelectorQuery().select('#canv').fields({
    
    
        size:true,
        node:true
    },res=>{
    
    
        //...
        //获取画布数据
        this.canvasData = {
    
    
            canvas:res.node,//画布canvas元素的节点
            ctx:res.node.getContext('2d'),//画布canvas的绘制工具
        };
        //实例化地图对象
		const map = new Map(this.canvasData,this.data.players.map(p=>p.id));
		//绘制地图的异步方法
		map.drawMap(()=>{
    
    
			//绘制完成了,这里绘制所有参与玩家的棋子
		    map.drawChesses();
		});
		this.map = map;
        //...
    }).exec()
},
onTouchStart(e) {
    
    
	//...这里是canvas画布触摸开始的处理方法
}

从上面看,其中的Map是指地图模块,

在使用模块前,需要在代码文件头写一行导入模块的代码,也就是引用,代码如下

import {
    
     Map, ChessColors } from '../../utils/map.js';

还有ChessColors ,是默认分配玩家的棋子颜色集合,若想自定义,可在此修改,
模块文件代码共有240行(包括空格行),看着不多,实现了地图数据和地图绘制方法

游戏背景

在上面的初始化逻辑里,游戏背景已经绘制好了,

地图的绘制方法是drawMap(callback),参数callback是传入的回调方法,绘制完成后会调用,

这里简单说一下它的实现过程,代码如下

drawMap(callback){
    
    
   const {
    
     canvas, ctx } = this.canvasData;
   //定义它们分别是网格,所有棋子,格子大小,格子半径,圆半径
   const {
    
     grids, chesses, gSize, gR, gRp } = this;
	//初始化值
   const angle = Math.PI*2;
   //修改设置
   ctx.strokeStyle=BorderColor;
   grids.forEach((g,i)=>{
    
    
   		//这个方法是扫描一个格子位置中相邻的一些格子(圆)
       let gs = this.scanGrids(g);
       if (gs.length<=0)  return;
       //在这里画上一些格子之间的连接线
       gs.forEach(g1=>{
    
    
           ctx.beginPath();
           ctx.moveTo(g.x,g.y);
           ctx.lineTo(g1.x,g1.y);
           ctx.stroke();
       });
   });
   ctx.fillStyle=BorderColor;
   //接下来,画出所有的格子(圆),就是覆盖着画
   grids.forEach((g,i)=>{
    
    
       //...
       ctx.beginPath();
       ctx.arc(g.x,g.y,gRp,0,angle);
       ctx.fill();
       ctx.stroke();
   });
	//这里就算画好了,导出图像
   let img = canvas.createImage();
   img.onload = () => callback(img);
   img.src = canvas.toDataURL();
   //网格地图,需要设置到bgImg缓存起来(背景图),将来有用
   this.bgImg = img;
}

由于这里是设置到bgImg缓存中,每次更新就要重新绘制出来,
也可以将绘制的图像设置到背景图中,每次更新就不用重新绘制地图,直接在底层显示,
然后绘制其它的,例如绘制改变位置后的棋子

选手和棋子

绘制不同选手阵营中的棋子,也可以看作是珠子,不同选手代表的棋子和格子颜色不同,

如果棋子的位置改变后,就需要调用地图的方法drawChesses(selectIndex)重新绘制,

显示最新的所有棋子布局,代码如下

drawChesses(selectIndex){
    
    
	const {
    
     canvas, ctx } = this.canvasData;
    const {
    
     grids, chesses, gSize, gR, gRp } = this;
    const angle = Math.PI*2;
    //每次绘制前,都要清空画布
    ctx.clearRect(0,0,canvas.width,canvas.height);
    //将缓存的背景图重新绘制出来
    ctx.drawImage(this.bgImg, 0, 0);
    //然后,再去绘制所有的棋子
    let isSelect=false;
    chesses.forEach(chess=>{
    
    
    	//判断是否有选择,记录一下
        if(chess.index==selectIndex) isSelect=true;
    	//如果有选择棋子,就在棋子周围画一个圆(代表选择)
        this.drawChesse(chess,selectIndex);
    });
    //这里还要判断,如果没有棋子被选择过,那剩下可能是选择了没有棋子占用的格子
    if(!isSelect) {
    
    
        this.drawChesse(undefined,selectIndex);
    }
}

从上面看出,传入的参数selectIndex就是指玩家选择的格子索引,
同时也传给了方法drawChesse(chess,selectIndex),另一个参数chess就是指选择的棋子,没有选择就传undefined,这个方法实现了怎样绘制网格中的棋子

讲到这里,游戏页面就算绘制好了,编译运行看效果,

显示出来是下图这样的,一共6个玩家参与的跳棋游戏
在这里插入图片描述

游戏逻辑

从画布的触摸开始事件中去处理,就是上面提到的onTouchStart(e)方法,

游戏规则

这里再想一下游戏逻辑,当用户点击棋子,选择自己的棋子后,接下来应该怎样实现呢,

这里了解以下游戏规则,

按照游戏规则,理清一下实现思路:

  • 判断每个玩家的步数,在30步内未走出自己的阵营的,就判出局,不能再继续;
  • 走棋,有两个走法:第一个就是只走一步;第二个,就是以旁边的棋子为方向,跳到棋子方向的另一边,可连续跳;
  • 步数,走一步算一步,跳一次也算一步,如果是连续跳也算一步,可以修改过来的;
  • 将对方阵营所有的棋子空位全部占领就判赢,如果是三人以上的,赢的按排名计算,剩下的判出局;

怎么实现游戏逻辑的呢,实现游戏逻辑是最复杂的地方吧,说来也是,只要理解它的实现过程基本就可以自己尝试了,

选择棋子

第一步,要选择棋子,然后移动棋子,就在它的触摸事件中处理,代码如下

onTouchStart(e) {
    
    
	//...
   const touch = e.touches[0];//第一个触摸点(位置)
   const {
    
     grids, gRp, chesses } = this.map;
   //所有玩家,指向当前下棋的玩家
   const {
    
     players, currentPlayer } = this.data;
	//通过触摸点位置判断,从网格中找出选择的棋子
   let chess;
   let grid = grids.find(grid=>{
    
    
   		//先从网格位置判断...如果是在这个格子里,再判断是否有棋子在这个格子上...
   });
   //没有点到格子的画,就返回不处理了
   if (grid==undefined) return;
   //给出当前在下棋的玩家
   const player = players[currentPlayer];
   let {
    
     selectGridData } = this;
   //判断之前是否选择过网格(棋子)了,还有就是选择的棋子是当前在下棋的玩家的,如果条件满足了,反之就是不满足
   if (!(selectGridData?.chess?.id==player.id)) {
    
    
   		//不满足以上条件,就重新设置选择数据
       this.selectGridData = {
    
     grid, chess, moveCount:0 };
       //重新绘制所有棋子
       this.map.drawChesses(grid.i);
       //不再继续处理
       return;
   }
	//如果选择到棋子
   if (chess) {
    
    
   		//不是自己的,提示一下
       if (chess.id!=player.id) {
    
    
           this.showToast(`${
      
      player.id+1}号玩家操作`);
           return;
       }
       //如果是相邻跳过的,又选择下一个棋子,就不是连续跳了
       if (selectGridData.moveCount>1) {
    
    
       		//换下一个玩家
           this.setNextPlayer();
           return;
       }
       //剩下的,只有选择棋子了吧,直接给它更新显示
       Object.assign(this.selectGridData, {
    
     chess, grid });
       this.map.drawChesses(grid.i);
       return;
   }
	//获得移动到下一个位置的距离,相对格子的,一个格子算一个单位距离
   let count = this.map.gotoGrid(selectGridData.grid,grid);
   if (count>0) {
    
    
   		//判断是否只移动1个(走),然后就是上次移动的是否大于1个(跳),满足条件
       if (!(count==1 && selectGridData.moveCount>count)) {
    
    
       		//反之不满足,这里更新选择的棋子位置
           chess = selectGridData.chess;
           chess.index =  grid.i;
           //记录移动距离
           selectGridData.moveCount = count;
           //棋子步数加1
           player.step++;
           //获取玩家在自己阵营内的所有棋子
           let chesses2 = this.map.getPlayerChessesAtGrids(player.id);
           //判断这个玩家是否在游戏,还有步数
           if (player.rank==0 && player.step==MinStep) {
    
    
           		//步数大于MinStep限制,还未走出自己阵营,就得通知这个玩家出局了...
           }
           //更新显示玩家的状态
           this.setData({
    
    
               players
           });
       }
   }
   //若只移动1个(走),就更新绘制棋子,换下一个玩家
   if (count==1){
    
    
       this.map.drawChesses(grid.i);
       this.setNextPlayer();
       return;
   }
	//剩下的,就是其它操作了,直接更新显示即可
   Object.assign(this.selectGridData, {
    
     chess, grid });
   this.map.drawChesses(grid.i);
},

看上面,就会注意到,还有一些细节都在地图模块的方法里了,

上面还有个重要的方法setNextPlayer(),是处理换下一个玩家来,切换玩家前还要加个判断,

就是判断玩家输赢,最后若是剩下一个玩家还没赢的话,那么游戏就结束,代码如下

setNextPlayer(){
    
    
   const {
    
     players, currentPlayer } = this.data;
	//定义当前参与的玩家
   const player = players[currentPlayer];
   //找出当前玩家的所有棋子
   let chesses2 = this.map.getPlayerChessesAtGrids(player.id);
   //计算当前玩家是否全部占领对方的阵营
   let isWin = chesses2.reduce((current,next,index)=>{
    
    
   		//棋子flag属性判断即可...
   });
   if (isWin) {
    
    
       let rank=0;
       //全部占领了,更新排名,不再参与...
       player.rank = rank+1;
       //可通过排名和参与人数判断是否到最后了
       if (player.rank+1 >= players.length) {
    
    
           //这里是最后的了,直接弹出游戏结束...
           this.showGameOver(`${
      
      player.id+1}号玩家赢了,成功占领对方的阵营`);
           return;
       }else{
    
    
       		//提示一下,然后继续
           this.showModal(`${
      
      player.id+1}号玩家赢了,成功占领对方的阵营`);
       }
   }
	//换下一个玩家
   let current = (currentPlayer+1)%players.length;
   //此处省略了...
   this.setData({
    
    
       currentPlayer: current,
       players
   });
   this.showToast(`${
      
      players[current].id+1}号玩家操作`);
},

游戏测试

就讲到这里,跳棋游戏的实现思路大概清晰了吧,

调用的其它方法不对,虽然没有讲,但是,见其名知其意,相信自己可以实现出来的,

来看一下,游戏运行的效果动图如下,这是两个人参与的,测试完感觉没问题,
请添加图片描述

想看项目源码的,请点此查看,找到资源类别一栏(如果是手机上看可能找不到,就换成电脑浏览器访问 ),

资源列表里面,找到其中的跳棋游戏项目源码,请放心下载,感谢支持。

游戏启示:对此你有什么启发呢

  • 选好目标,尽快占领,类似五行斗法里的相生相克,
  • 在这个游戏规则里,尽可能快速移动,落后就要挨打

猜你喜欢

转载自blog.csdn.net/zs1028/article/details/130119079