当前位置: 首页 > news >正文

【俄罗斯方块】单机游戏-微信小程序项目开发入门

这是一个仿俄罗斯方块小游戏的微信小程序,只需要写一小段代码就实现出来了,有兴趣的同学完全可以自己动手开发,来看看实现过程是怎样的呢,边写边做,一起来回忆小时候玩过的经典俄罗斯方块游戏吧。

文章目录

  • 创建小程序
    • 页面设计
    • 页面逻辑
    • 初始化
    • 定时刷新
    • 完善细节
  • 运行小程序

💡 给读者一个小小提示
需要有HTML和CSS基础
还有JavaScript基础
电脑有安装好微信小程序开发工具

满足以上三个条件就能看懂接下来的一些内容了

创建小程序

首先,打开微信小程序开发工具,如下图所示,

新建一个小程序项目minigame-tetris,就选择小程序,注意不要选择使用云服务和模板,然后点确定即可,
在这里插入图片描述

这时项目在开发者工具中自动创建好了,会发现生成的文件不会太多,这是为方便精简项目源码,避免新建的项目自动生成一大堆对新手来说不懂的东西,此项目开发对新手非常友好的!

页面设计

在项目根目录下,打开一个页面布局文件,文件路径为pages/index/index.wxml,如下图所示,这是我们要做的页面显示效果,准备编写布局标签实现它吧
在这里插入图片描述
可能新手想不太明白,那么说下原理,整个页面的布局只用到了一个画布Canvas组件和两个图片image,然后设置css样式层层叠加放置就可以实现效果,布局文件内容参考如下,简单就好!

<!--index.wxml-->
<view class="container">
  <view class="canvas-box">
    <image src="{{bgImg}}" class="canvas-bg" />
    <image src="{{foreImg}}" class="canvas-bg" />
    <canvas type="2d" id="canv" class="canvas"></canvas>
  </view>
  <view class="footer">
    <view class="btngroup">
    	<!-- 此处省略更多 -->
    </view>
  </view>
</view>

页面逻辑

接下来,想到需要编写游戏的逻辑,这下会不会感到难写了,以我们对俄罗斯方块游戏互动逻辑的了解,似乎实现起来不太难吧,别担心,站在巨人的肩膀上,看看怎么写得呢,

打开文件路径为pages/index/index.js,开始编写代码,如下所示,理清一下逻辑,一边写一边思考

// index.js
const canvasId = 'canv';
//...

Page({
  data:{
    context:{},//存放其它的值
    bgImg:'',//背景图
    //...
  },
  //页面加载时,从此处开始执行
  onLoad(){
  	//先获取canvas画布组件 大小size 和 节点node
    wx.createSelectorQuery().in(this).select(`#${canvasId}`).fields({
      size:true, node:true
    },res=>{
      const canvas = res.node;
      //这里需要设置一下画布的宽高,不然画布实际大小会与实际不一致
      canvas.width = res.width;
      canvas.height = res.height;
      //获取绘制2d画布的工具集合对象ctx
      const ctx = canvas.getContext('2d');
      //设置到页面的局部变量context里去,方便在其它方法中使用
      Object.assign(this.data.context,{ canvas, ctx });
      const base64 = this.initGrid();
      this.initTimer();
      //页面显示背景图
      this.setData({
        bgImg: base64
      });
    }).exec()
  },
  //页面卸载时
  onUnload(){
    //...
  },
  //初始化网格,或绘制游戏背景图
  initGrid(){
    //...
  },
  //初始化定时器
  initTimer(){
    //...
  },
  //用户按下按键,或点击的事件
  onclickkey(e){
    //...
  },
  //用户松开按键事件
  onclickend(){
    //...
  }
})

初始化

初始化方法initGrid(),这里面做的是初始化一些属性,和绘制背景图,还有网格数据,大致的实现逻辑如下,可以这样理解,游戏中的一些方块都是有自己位置的,需要网络来作为参照物,定方位

// index.js
import Tetris from '../../static/utils/tetris';//导入一个模块

Page({
  data:{
  	context:{},
    tetris:null,//方块相关的对象
    //...
  },
  //...
  //初始化网格,或绘制游戏背景图
  initGrid(){
		const { ctx, canvas } = this.data.context;
	    const cols = 28;//这里设定每一行内的方块数量
	    const gs = Math.trunc(canvas.width/cols);
	    const rows = Math.trunc(canvas.height/gs);
	    const padding = Math.trunc(canvas.width%cols/2);
	    //计算好了一些属性,如果要用到,就在局部变量context中存上去
	    Object.assign(this.data.context,{ padding, gs, rows, cols });
	    //这里我们用到了一个模块,这是对处理方块相关的封装,使用前需要先导入它
	    const tetris = new Tetris({rows,cols});
	    //先保存一下对画布的设置
	    ctx.save();
	    ctx.fillStyle='#000000';
	    ctx.strokeStyle='#fff';
	    ctx.rect(0,0,canvas.width,canvas.height);
	    ctx.fill();
	    ctx.fillStyle='#fff';
	    ctx.strokeStyle='#000';
	    ctx.beginPath();
	    // 这两行是绘制网格显示的,在测试中可以参照,游戏中我们会将它隐藏起来
	    // const grids = tetris.getGrids();
	    // grids.forEach(g=>this.drawGrid(g));
	    ctx.rect(padding,0,cols*gs,rows*gs);
	    ctx.fill();
	    ctx.stroke();
	    //绘制完,可以恢复设置了
	    ctx.restore();
	    this.data.tetris = tetris;
	    const data = canvas.toDataURL();
	    ctx.clearRect(0,0,canvas.width,canvas.height);//需要清除了,下次绘制用
	    return data;//绘制好了,返回图片信息
	},
	//...
}

定时刷新

初始化定时器方法initTimer(),这方法做的是让游戏运动起来的意思,因为是用定时器实现刷新游戏状态的,如果对此不太了解,那么实现过程会是难以理解的,这个实现逻辑是较复杂的,大致代码如下

const TIME = 900;
//...
Page({
  data:{ 
    foreImg:'',//前置图,在背景图上一层
    tetris:null,//方块相关的对象
    timer:null,//定时器
    isContinue:true,//游戏开始或暂停
    downKey:null,//按下按键的信息
    speed:1,//刷新速度调节阈值
    score:0,//游戏得分
    time:0,//游戏时长
  	//...
  },
  //游戏更新逻辑
  refresh(offset = 0){
    //...
  },
  //游戏结束
  gameEnd(){
    //...
  },
  //画方块(格子)
  drawGrid(g,fill){
    //...
  },
  //初始化定时器
  initTimer(){
    const { tetris } = this.data;
    //使用模块对象的方法 这里先创建一个随机方块
    let tetri = tetris.createReadomTetris();
    //定义一个重绘的方法,实现它的逻辑
    const redraw=()=>{
      const { ctx, canvas } = this.data.context;
      const { isContinue, downKey, speed, score, time } = this.data;
      //如果游戏可以继续  并且  在没有按下键时
      if(isContinue && !downKey){
        let offset = 1;//向下移动1格
        //方块在下落了,然后刷新显示,最后判断下落是否触底
        if(this.refresh(offset)){
          //方块无法向下移动 就判断游戏结束
          if(tetri.gt==0){
            this.gameEnd();
            return;
          }
          //将方块加入到网格集合中,就是合并入,装下去的意思 
          tetris.joinGrids(tetri);
          //接着尝试处理清理逻辑,会自动判断底下的所有方块是否装满,如果装满了一行会自动消除,返回成功消除后的得分
          let scope = tetris.clearFillGrids();
          //重新创建一个随机方块
          tetri = tetris.createReadomTetris();
          //获取网格集合
          let grids = tetris.getGrids(true);
          //清空画布
          ctx.clearRect(0,0,canvas.width,canvas.height);
          //将网格集合中所有方块绘制出来
          grids.forEach(g=>this.drawGrid(g,true));
          //绘制好了,弄成一个图片数据,放在页面前置背景图中显示
          this.setData({
            foreImg: canvas.toDataURL(),
            score: score+scope,//更新显示得分加分
          });
          ctx.clearRect(0,0,canvas.width,canvas.height);
        }else{
          //方块下落一格
          tetri.gt+=offset;
        }
      }
      //需要调用这个方法,在不需要的时候会自动挂起,节省CPU处理开销
      canvas.requestAnimationFrame(()=>{
        let t = Math.trunc(TIME*speed);
        //等待下一次重绘
        this.data.timer = setTimeout(redraw,t);
        this.setData({
          time:time+t
        })
      });
    };
	//开始重绘
    redraw();
  },
  //...
 }

完善细节

上面讲得一些的方法都是比较详细的,应该能看得明白吧,对了,那个模块文件只需要实现一些方法就可以了,文件路径为/static/utils/tetris.js,具体怎样写得,参考代码大致如下

//定义一个基本方块宽度,所占格数
const COLS = 4;
//定义一些基本方块,形状各不相同
const RetrisMini = [
  [0,1,5,9],[0,1,2,5],[0,4,8,12],[0,1,4,5],[2,4,5,6],[1,4,5,9],[0,1,2,3],[0,4,8,9],[1,4,5,6],[0,1,2,4],[0,4,5,8]
];
/**
* 方块方法封装对象类
**/
export default class Tetris{
  //一些私有属性
  #grids;
  #cols;
  #rows;
  #coordis;
  #tetri;

  constructor(config){
    const { rows, cols } = config;//传参数两个属性,分别为行数和列数
    const grids = new Array(rows*cols);
    for(let i=0; i<rows; i++){
      for(let j=0; j<cols; j++){
        grids[i*cols+j]={ l:j, t:i };
      }
    }
    this.#grids=grids;//初始化一些属性
    this.#cols=cols;
    this.#rows=rows;
    this.#coordis=[];
  }

  moveTetriAtHorizontal(l,success){
    //...左右移动方块
  }

  getGrids(filter){
    //...获取那些方块占用的网格集合
  }

  getGridsIndex(l,t){
    //...根据表格坐标计,算出一格的索引值返回
  }

  joinGrids(titri,l,t){
    //...将一个方块并入网格集合中
  }

  clearFillGrids(){
    let scope=0;
    //...如果有,清理填平的一行方块,加分
    return scope;
  }

  isIntoTetris(g){
    //...判断方块位置是否与其它实物发生重叠,也就是说是否发生碰撞
    return true;
  }

  isTouchTetris(titri,l,t){
    //...判断方块位置是否触底,也就是说是否还能往下落
  }
  
  createCoordis(cs){
    //...给方块创建一个位置坐标
  }

  createReadomTetris(){
    //...创建一个随机的方块
  }

  getTetris(){
    return this.#tetri;
  }

  rotateRightAngle(success){
    //...方块顺时针方向旋转角度
  }
}

💡一个问题
可能有注意到了,代码中的RetrisMini 是什么东西,看不懂正常,来解说一下,第一个[0,1,5,9],表格形式填充如下,仔细看一看,好像是一个方块形状的图案

ABCD
01xx
x5xx
x9xx

这就是基本方块形状,方便后面绘制的,

还有一些实现细节,剩下的方法这里就不讲了,由于篇幅有限,没法在这里将每个实现细节都讲得清楚,项目代码写得是不多,就讲到这了,请自己动手完善,相信自己,努力就会有收获。

如果想要看完整项目源码的请"点击这里",点击进去展开资源一栏往下仔细找一找就有了。

运行小程序

下面是游戏项目源码运行的效果动图,是否心动了呢,欢迎尝试,项目源码完整,运行无问题,请放心下载学习,谢谢!
请添加图片描述

💡关于游戏剧情
俄罗斯方块游戏原本是没有剧情的,让本人想起来了,跟两个神话故事有异曲同工之妙呢,分别是《精卫填海》和《女娲补天》故事,这故事来做游戏剧情好像可以,还有其它剧情吗,这里不多说了,剧情怎么写,请自己脑补。

请添加图片描述

相关文章:

  • ip138域名查询/成都百度推广排名优化
  • 社区网站开发需求文档/今天重大新闻
  • 深圳网站建设素材网站/关键词研究工具
  • 机械加工外协网站/最优化方法
  • 网站微信付款调用/移动排名提升软件
  • wordpress用还得shopif好用/磁力猫官网cilimao
  • 聊聊设计模式-解释器模式?
  • 关于IO流的基础理论
  • Apache Airflow Hive Provider <5.0.0 存在操作系统命令注入漏洞
  • 安装nnpy出现错误以及解决
  • BaseAdapter实现的投票案例
  • java微信支付v3系列——6.微信支付查询订单API
  • 【JavaScript】14_window对象与JS提升
  • 8Manage:提高项目执行力的策略有哪些?
  • 【用户交互】
  • 【MATLAB教程案例67】基于Actor-Critic结构强化学习的车杆平衡控制系统matlab仿真
  • 预约挂号系统技术点详解(二)
  • 谈谈vue的路由守卫和keep-alive后生命周期