有手就行的模仿羊了个羊

673 阅读4分钟

适合新手练习的前端React小游戏(大佬勿骂)

声明:作者本身只是个刚工作半年的前端小白,所以有关算法优化方面较差,希望各位大佬勿喷。
image.png

简介

设计思路借鉴于此作者:https://juejin.cn/post/7147245442172977189 (有兴趣的可以看看)

此项目基于 ts、umi、react、less 等技术完成(需要了解 react ),并采用纯前端渲染未采用任何后台服务。本文只注重设计思路哦。

仓库地址

gitee.com/hzpenyuyan/…

项目预览

还在弄,勉强看看动图吧。

GIF 2023-1-18 16-30-51.gif

设计思路

由于我习惯于对需求进行分步处理的,所以将此游戏分成以下步骤。
  • 搭建大体场景 (背景图,存放栏,工具栏)
  • 实现格子的随机静态渲染
  • 点击格子后的逻辑
  • 工具栏三个工具逻辑

搭建大体场景 (背景图,存放栏,工具栏)

    这里的简单用下HTML、CSS就能使用,就不过多讨论了。

image.png

处理格子的随机渲染

格子的随机渲染可以分成以下步骤。

  • 实现单个格子的静态渲染
  • 实现多个格子的动态渲染
  • 实现格子的随机渲染

实现单个格子的静态渲染

  1. 每个格子我们需要一个正方形。
<div className={styles.grid}></div>
@grid-size = 20px; ; //格子大小,不支持less的话,直接width/height直接赋值也可以的
.grid {
  box-sizing: border-box;
  display: inline-block;
  width: @grid-size;
  height: @grid-size;
  border: 1px solid #000;
}

有人会问你这格子咋没有设置背景图片啊,太丑了也?

image.png

别急,我们一步步来。到此,我们单个格子就做完了,虽然很丑但是有效果就行。

实现多个格子的静态渲染

  1. 因为我们要指定每个格子的位置,所以得用 定位(position) 来做。
  2. 例如下图,怎么实现下图的效果呢?

image.png

3. 先找一下规律~

1号格子:第一行第一列 1-1

2号格子:第一行第二列 1-2

3号格子:第一行第二列 2-1

    不难发现:通过定位就可以发现:
        left =(列数 - 1) * 格子大小;
        top = (行数 - 1) * 格子大小;  

4. 那么我们就简单了,给每个格子设置一个对象属性然后渲染就行。

{
    col:0,//列数
    row:0,//行数
    left:0,//左侧偏移量
    right:0//右侧偏移量
}

逻辑如下:
element.left = (col - 1) * GridSize;
element.top = (row - 1) * GridSize;

至此,我们就完成了简单的一层格子的渲染。

image.png

实现多层格子的动态渲染

  • 层级很简单,想到用 z-index 来做,那么在格子对象内添加一个 zIndex 属性。
  • 格子对象再添加一个 state 属性,用于判断格子是否能点击 ( 0:可点击 , 1:不可点击 )
{
    zIndex: 0; // 图层(新增)
    col:0,//列数
    row:0,//行数
    left:0,//左侧偏移量
    right:0,//右侧偏移量
    state: 0 //0:可点击 1:不可点击
}

如何实现每层格子的偏移呢?一样,先找规律。

image.png

从上图图中不难发现。

4号格子相比于1号格子往下和往右都偏移了半个格子的长度。  
5号格子相比于2号格子往下和往右都偏移了半个格子的长度。  
格子号层级行数列数索引(层级-行数-列数)
42112-1-1
11111-1-1
52122-1-2
21121-1-2
利用规律,得出以下规律。
element.left = zIndex * 0.5 * GridSize + (col - 1) * GridSize;
element.top = zIndex * 0.5 * GridSize + (row - 1) * GridSize;

这样就能完成以下效果了。

image.png

然后我们要去判断格子是否能被点击?

格子号层级行数列数索引(层级-行数-列数)
42112-1-1
11111-1-1
21121-1-2
31211-2-1
利用规律,得出以下规律。 
    目标格子的层级 < 别的格子的层级 && 两个格子的偏移量相差 < 0.5 * GridSize。
那么格子就不能被点击了。
问题来了,如何有效的循环所有格子对象的进行是否点击判断?
    因为我这只用了单层数组进行简单处理,所以每次判断都得循环所有数组。后续可能得采用树图进行优化。

总结,因为我

image.png

//初始化格子状态
function initGirdState(GridList: GridNode[], GameSetting: GameSetting) {
  for (let i = 0; i < GridList.length; i++) {
    let {
      left: targetLeft,
      top: targetTop,
      zIndex: targetZIndex,
      state: targetState,
    } = GridList[i];
    if (targetState !== 2) {
      let isClick = true;
      //TODO 需要优化
      for (let j = 0; j < GridList.length; j++) {
        let {
          left: OtherLeft,
          top: OtherTop,
          zIndex: OtherZIndex,
          state: OtherState,
        } = GridList[j];
        if (
          targetZIndex < OtherZIndex &&
          Math.abs(OtherLeft - targetLeft) <= GameSetting.GridSize * 0.5 &&
          Math.abs(OtherTop - targetTop) <= GameSetting.GridSize * 0.5 &&
          OtherState !== 2
        ) {
          isClick = false;
          continue;
        }
      }
      GridList[i].state = isClick ? 1 : 0;
    }
  }
}

实现多层格子的随机渲染

这里每个人有自己的生成想法,只要我们生成的格子的 层级、行数、列数 三者都不重复就行了。

以下是我的烂代码(勿骂)。

  • 为了保证格子的唯一性:对象内添加id(层级-行数-列数)属性。
  • 为了添加背景图片:对象内添加type属性,然后require(@/assets/imgs/${type}.png)。
    我知道肯定会有人会说你这么多图片,咋不用雪碧图啊?别问,问就是懒得弄(开玩笑的,主要是后面有支持自己上传图片生成的,觉得雪碧图比较麻烦hhhh,还是懒
{
    id: 0; // 保证图片的唯一值
    type:0; //背景图片
    zIndex: 0; // 图层
    col:0,//列数
    row:0,//行数
    left:0,//左侧偏移量
    right:0,//右侧偏移量
    state: 0 //0:可点击 1:不可点击
}

格子大概样式
<div
  className={styles.grid}
  style={{
    position: gridInfo.state === 2 ? "static" : "absolute",
    top: gridInfo.state === 2 ? undefined : gridInfo.top,
    left: gridInfo.state === 2 ? undefined : gridInfo.left,
    zIndex: gridInfo.zIndex,
    cursor: gridInfo.state === 0 ? "default" : "pointer",
    backgroundImage: `url(${
      imgFiles.length > 0
        ? imgFiles[gridInfo.type]
        : require(`@/assets/imgs/${gridInfo.type}.png`)
    })`,
    backgroundColor: gridInfo.state === 0 ? "#888" : "#fff",
  }}
  onClick={handleClick}
 ></div>

//随机生成格子列表
function initGridList(GameSetting: GameSetting) {
  const { Sort, Layers, Row, Col } = GameSetting;
  let GridList: GridNode[] = []; //总渲染格子列表
  let flagList: flagListProps = {}; //总渲染格子标签列表-判断是否重复

  //初始化格子样式
  const initGirdStyle = () => {
    let zIndex = Math.floor(Math.random() * Layers); //随机生成层数
    return {
      zIndex,
      row: Math.ceil(Math.random() * (Row - zIndex)), //根据层数,随机生成行
      col: Math.ceil(Math.random() * (Col - zIndex)), //根据层数,随机生成列
    };
  };

  //生成3*sort个格子
  for (let i = 0; i < Sort; i++) {
    let type = Math.floor(Math.random() * 12); //获取当前渲染图片值 1-12
    let newGirds: GridNode[] = [];
    //根据type初始化生成三个格子
    for (let j = 0; j < 3; j++) {
      let newGird = {
        id: "",
        state: 0,
        left: 0,
        top: 0,
        type,
        ...initGirdStyle(),
      };
      //判断三个格子是否有重复位置的
      createUniqueGrid(flagList, newGird, GameSetting);
      newGirds.push(newGird);
    }
    //推送新数组到GridList
    GridList.push(...newGirds);
  }

  //初始化所有格子状态
  initGirdState(GridList, GameSetting);

  return GridList;
}

//生成唯一格子
function createUniqueGrid(
  flagList: flagListProps,
  element: GridNode,
  GameSetting: GameSetting
) {
  const { GridSize, Layers, Row, Col } = GameSetting;
  let { zIndex, row, col } = element;
  if (
    flagList[zIndex] &&
    flagList[zIndex].hasOwnProperty(`${row}-${col}`) &&
    flagList[zIndex][`${row}-${col}`] === 1
  ) {
    element.zIndex = Math.floor(Math.random() * Layers);
    element.row = Math.ceil(Math.random() * (Row - element.zIndex));
    element.col = Math.ceil(Math.random() * (Col - element.zIndex));
    createUniqueGrid(flagList, element, GameSetting);
  } else {
    if (!flagList[zIndex]) {
      flagList[zIndex] = {};
    }
    flagList[zIndex][`${row}-${col}`] = 1;
    element.id = `${zIndex}-${row}-${col}`;
    element.left = zIndex * 0.5 * GridSize + (col - 1) * GridSize;
    element.top = zIndex * 0.5 * GridSize + (row - 1) * GridSize;
  }
}

至此,我们完成了多层格子的随机渲染。

后续的点击事件其实比较简单了( 主要是累了,不想写了 )。

还有自定义设置的页面,可以设置游戏难度、最大行数、最大列数、最大层级什么的后续再分享( 因为我觉得这篇文章没有人看 )。

image.png

这里是普通又想做不普通的前端打工人。大家新年快乐哦!!!!!

image.png