100行代码教你写个卡牌翻翻乐小游戏

Posted 登楼痕

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了100行代码教你写个卡牌翻翻乐小游戏相关的知识,希望对你有一定的参考价值。

卡牌翻翻乐这种小游戏相信大家都玩过,一组卡牌随机翻面平铺在面前,用户点击卡牌翻转,两两配对消除卡牌,游戏的玩法主要是通过玩家自己对卡牌位置的记忆,快速匹配出相同的卡牌予以消除。

那么我们自己来写一个这个小游戏,大致的思路会是什么呢?首先我们先找来卡牌素材,这里取的是12张生肖图案【图片来源于网络侵删】。那么我们就一共要展示24张卡片,布局上我们就按6x4的格子摆放吧。那么接下来主要工作分为:

1、打乱顺序随机摆放卡牌

2、卡牌翻转动画

3、卡牌翻转后的匹配规则,如果未匹配,两张牌自行翻转回去;如果匹配,则消失

思路很清晰,就从第一步开始把!

1、打乱顺序随机摆放卡牌

为了方便图片渲染,我们给12张图片取名为1.png,2.png……12.png。把12x2=24张牌放到一个数组里就是[1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12],这个数组手敲起来眼花还容易写错,用个函数生成吧:

const generateArr = useCallback((): Array<number> => 
      let cur = 1;
      const initArr = Array(24).fill(1).map((item, index) => 
         if (index % 2 === 1) 
            item = cur - 1;
          else 
            item = cur++;
         
         return item
      )
      return initArr // 输出[1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12]
   , [])

好了,接下来的步骤就是把24张牌通过洗牌算法打乱顺序。之前说展示6x4的布局,会有人想到说把一维数组转成二维数组然后双层for循环打印出来,其实没必要,毕竟6x4的布局我们可以通过css实现。

打乱顺序:

const getRandomNum = (min: number, max: number) => 
      return Math.floor(Math.random() * (max - min + 1) + min)
   
// 洗牌算法
 const shuffle = useCallback((array: Array<number>): Array<number> => 
      let newArr = array.slice()
      for (let i = 0; i < newArr.length; i++) 
         const j = getRandomNum(0, i)
         const temp = newArr[i]
         newArr[i] = newArr[j]
         newArr[j] = temp
      
      return newArr
   , [])

   const generateArr = useCallback((): Array<number> => 
      let cur = 1;
      const initArr = Array(24).fill(1).map((item, index) => 
         if (index % 2 === 1) 
            item = cur - 1;
          else 
            item = cur++;
         
         return item
      )
      return shuffle(initArr)
   , [shuffle])

这样,我们将24张牌通过flex布局,加上flex-wrap:wrap,摆放处6x4的格子:

【图片来源于网络侵删】

2、卡牌翻转动画

然后我们需要将这些卡牌翻转过去,给个封面,在点击的时候将卡牌再翻转过来。

首先通过position:absolute给卡牌加一个同级的div封面,然后将卡牌的img元素rotateY(-180deg),点击卡牌的时候,将容器再次transform: rotateY(180deg)

.....
<div className="card-page">
            
               arr.map((item, index) => 
                  return (
                     <div onClick=() => handleClick(item, index) key=index className=`wrap $(currentItem[0]?.item === item && currentItem[0]?.index === index) || (currentItem[1]?.item === item && currentItem[1]?.index === index) ? 'active' : '' $resultItems.includes(item) ? 'hide' : ''`>
                        <img className="card-img" src=
                           require(`@/assets/$item.png`)
                         alt="" />
                        <div className="cover">
                           翻翻乐
                        </div>
                     </div>
                  )
               )
            
            
               result ? <span className="result">成功了!</span> : ''
            
</div>
......
// css部分
.wrap
    position: relative;
    width: 14%;
    margin: 8px;
     transition: transform 1s;
    transform-style: preserve-3d;
    cursor: pointer;

.active 
    transform: rotateY(180deg);

.hide
    opacity: 0;

.card-img
    transform-style: preserve-3d;
    padding-top: 38px;
    background: #256ae5;
    z-index: 2;
    transform: rotateY(-180deg);

.cover
    position: absolute;
    width: 100%;
    z-index: 1;
    height: 100%;
    top: 0;
    left: 0;
    backface-visibility: hidden;
    background-color: #7fc5ff;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #ffee0c;
    box-shadow: inset -5px -5px 10px 1px rgba(107, 126, 135 ,0.2);

 3、卡牌翻转后的匹配规则

思路也很清晰,我们用一个长度为2的数组currentItem来临时存放翻转的卡牌,用另一个数组resultItems来存放已消除的卡牌。当两张牌一样时,我们将名字push进resultItems数组;如果不一样,我们清空currentItem数组就行。

根据上面两个数组,我们可以来确定每张卡牌的显示状态:

1、当卡牌在currentItem中,卡牌呈正面显示生肖;

2、当卡牌在resultItems中,卡牌不显示,为已消除的状态;

3、否则,卡牌为背面显示翻翻乐的状态。

所以我们在卡牌的点击事件里维护一下这两数组,再在render里根据数组判断一下每张卡的显示状态就行了:

const handleClick = useCallback((item: number, index: number) => 
      if (resultItems.includes(item)) 
         return;
      
      currentItem.push( item: item, index: index )
      setCurrentItem([...currentItem])

      if (currentItem.length === 2) 
         if (timeoutID.current) 
            clearTimeout(timeoutID.current);
         
         if (timeoutID2.current) 
            clearTimeout(timeoutID2.current);
         
         if (currentItem[1].item !== currentItem[0].item) 
            timeoutID.current = setTimeout(() =>  setCurrentItem([]) , 1000)
          else 
            // 命中!
            timeoutID2.current = setTimeout(() => 
               resultItems.push(item);
               setResultItems([...resultItems])
               setCurrentItem([])
               if (resultItems.length === 12) 
                  setResult(true);
               
            , 1000)

         
      
   , [currentItem, resultItems])

 当resultItems的长度为12,就证明我们消除了所有的卡牌,游戏成功!

 

 

以上是关于100行代码教你写个卡牌翻翻乐小游戏的主要内容,如果未能解决你的问题,请参考以下文章

100行代码教你写个卡牌翻翻乐小游戏

500行代码,教你用python写个微信飞机大战

手把手教你五分钟扒个源码写个无敌外挂

ChatGPT教你写AI包教包会,7段对话写个识别模型,准确度最高达99.7%

Python基础——用 Python 写个消消乐小游戏

学了C语言想装x能干点啥?手把手教你写个聊天软件来玩玩