翻牌闯关游戏

 

翻牌闯关游戏

一、游戏概述
翻牌闯关游戏是基于 Vue 框架开发的,玩家需要通过点击卡牌,配对相同图案以消除卡牌,完成不同关卡的挑战。

二、游戏规则
关卡设置:游戏共设有 3 关,随着关卡数增加,卡牌格数依次为 12 格、20 格、30 格。
图案数量:游戏使用 12 种不同的图案进行卡牌配对。
玩法说明:玩家每次点击两张卡牌,若两张卡牌的图案一致,则这两张卡牌会被消除。当所有卡牌都被成功消除时,本关闯关成功。

三、游戏参数配置

记忆时长
参数名:memoryDurationTime
默认值:5 秒
可配置:可根据需求调整记忆时长,单位为秒。

提示游戏玩法
参数名:showTipsFlag
默认值:1
可配置:
1:判断 localStorage 值,仅一次提示游戏玩法。
2:每次游戏第一关(12 格关卡)都提示游戏玩法。

提示游戏玩法时机
参数名:showTipsFuncName
默认值:'goInGame'
可配置:
'setTimeRun':牌面已经全部翻转反面未开始计时时提示游戏玩法。
'goInGame':牌面未全部翻转反面时提示游戏玩法。

提示游戏玩法时长
参数名:showTipsTime
默认值:4100 毫秒
可配置:默认时长为 4100 毫秒,其中包含设置的引导手势动画 2 秒,可根据需要调整提示时长,单位为毫秒。

是否需要计时
参数名:ifNeedTime
默认值:2
可配置:
0:不需要计时。
1:正计时。
2:倒计时。

倒计时开始时间
参数名:baseTime
默认值:[20, 60, 90]
可配置:数组形式,第 N 关卡(baseTime 下标对应值)对应的倒计时开始时间,单位为秒。例如,第一关倒计时开始时间为 20 秒,第二关为 60 秒,第三关为 90 秒。

闯关失败重新闯关起始关卡
参数名:ifFailResetLevelStartNum
默认值:true
可配置:
true:闯关失败重新闯关从第一关开始。
false:闯关失败重新闯关不强制从第一关开始,具体逻辑需根据游戏整体设计调整。

每关是否不展示闯关结果,自动进入下关
参数名:autoEnterNextLevel
默认值:false
可配置:
true:每关闯关结束无论成功失败,自动进入下关。
false:每关闯关结束都会展示结果,根据闯关情况进入下关或重新开始。

四、游戏相关方法

开始游戏
方法名:gameRun()
功能:根据闯关情况自动设置对应关卡,开始新的游戏。当当前关卡闯关结束(成功或失败)后,调用此方法可进入下一关或重新开始游戏。

当前关卡闯关游戏结束
方法名:gameOver()
功能:表示当前关卡闯关游戏结束,可用于触发游戏结束相关的逻辑,如展示游戏结果、统计成绩等。此方法会根据闯关情况(是否成功清除所有卡牌、倒计时是否结束等),决定是否自动进入下一关或重新开始游戏。

游戏组件页代码:

<template>
  <div v-show="showGame" class="page game">
    <div class="game_body">
      <div v-if="memoryShow" class="game_time">
        <span>记忆倒计时:</span>
        <span>{{memoryTime}}s</span>
      </div>
      <div v-else class="game_time">
        <span>倒计时:</span>
        <span>{{curTime}}s</span>
      </div>
      <div v-if="isStartGame" class="game_box">
        <ul class="game_list" :class="{'first_level':levelStartNum == 0,'second_level':levelStartNum == 1,'third_level':levelStartNum == 2}">
          <li @click="chooseBrand(index)" v-for="item,index in currLevelData" :key="index">
            <img :class="clearBrandArr.indexOf(index) == -1 && inGame && (currBrandIndex1 !== index && currBrandIndex2 !== index) ? 'show' : 'hide'" src="@/assets/img/game_3.png" />
            <img :class="!inGame || (currBrandIndex1 === index || currBrandIndex2 === index) ? 'show' : 'hide'" :src="item" />
          </li>
        </ul>
      </div>
    </div>
    <!-- 提示游戏玩法 -->
    <div v-show="showTips" class="game_tips">
      <div class="game_tips_body">
        <img src="@/assets/img/game_5.png" />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data(){
    return{
      // 翻牌对应的图片(12张)
      gameBrandList:[
        require('@/assets/img/game/1.png'),
        require('@/assets/img/game/2.png'),
        require('@/assets/img/game/3.png'),
        require('@/assets/img/game/4.png'),
        require('@/assets/img/game/5.png'),
        require('@/assets/img/game/6.png'),
        require('@/assets/img/game/7.png'),
        require('@/assets/img/game/8.png'),
        require('@/assets/img/game/9.png'),
        require('@/assets/img/game/10.png'),
        require('@/assets/img/game/11.png'),
        require('@/assets/img/game/12.png'),
      ],
      LevelNum:[12,20,30], // 第N关卡对应的格子数
      levelStartNum:0, // 第N关卡 从0开始
      currLevelData:[], //当前关卡 翻牌对应的图片
      showGame:false,
      inGame:false, //进入游戏页面后,是否用户可点击参与了
      isStartGame:false,
      currBrandIndex1:'', //每两次点击第1次选择的格子index
      currBrandIndex2:'', //每两次点击第2次选择的格子index
      clearBrandArr:[], //已清除的格子
      memoryDurationTime:5, //记忆时长(秒)
      memoryShow:false, //展示记忆时长倒计时
      memoryTime:0, //记忆时长倒计时

      showTips:false, //是否提示游戏玩法
      showTipsEnd:false, //是否提示游戏玩法 展示结束
      showTipsFlag:1, //1:判断localStorage值,仅一次提示游戏玩法 2:每次游戏第一关都提示游戏玩法
      // showTipsFuncName:'setTimeRun', //在什么时候提示游戏玩法 setTimeRun:牌面已经全部翻转反面未开始计时 goInGame:牌面未全部翻转反面时
      showTipsFuncName:'goInGame', //在什么时候提示游戏玩法 setTimeRun:牌面已经全部翻转反面未开始计时 goInGame:牌面未全部翻转反面时
      showTipsTime:4100, //提示游戏玩法 时长(毫秒)

      ifNeedTime:2, //0:不需要计时; 1:正计时; 2:倒计时
      baseTime:[20,60,90], //倒计时开始时间,秒 第N关卡(baseTime下标对应值)对应的秒数
      timer:null, //定时器
      curTime:'-', //当前 用时 或 倒计时
      timing:0,
      rafId:'',
      isClearAll:false, //清除完毕当前关卡 用于结束计时
      gameEnd:false, //倒计时结束,当前关卡闯关结束
      ifFailResetLevelStartNum:true, //闯关失败重新闯关是否从第一关开始
      autoEnterNextLevel:false, //每关是否不展示闯关结果,自动进入下关 true:每关闯关结束无论成功失败,自动进入下关 false:每关闯关结束都会展示结果,根据闯关情况进入下关或重新开始
    }
  },
  mounted() {
    // this.gameRun();
    this.$emit('flipBrandMounted');
  },
  watch:{
    showTipsEnd:function(newV,oldV){
      if(newV){
        if(this.showTipsFuncName == 'setTimeRun'){
          this.setTimeRun();
        }
        if(this.showTipsFuncName == 'goInGame'){
          this.goInGame();
        }
      }
    }
  },
  methods:{
    // 倒计时
    showTime(){
      this.curTime--;
      // 计时结束
      if(this.curTime == 0){
        clearInterval(this.timer);
        if(!this.isClearAll){
          this.gameEnd = true;
          this.gameOver();
        }
      }
      // 清除完毕当前关卡闯关结束
      if(this.isClearAll == true){
        clearInterval(this.timer);
      }
    },
    // 计时
    changeTime(k){
      // console.log(k);
      if(!this.timing && k){
        this.timing = k
      }
      this.rafId = requestAnimationFrame(this.changeTime);
      this.curTime = ((k - this.timing) / 1000).toFixed(2);
      // console.log(this.curTime);
      this.$nextTick(()=>{
        // 清除完毕当前关卡闯关结束
        if(this.isClearAll == true){
          cancelAnimationFrame(this.rafId);
        }
      })
    },
    // 开始计时
    setTimeRun(){
      this.showTipsDo('setTimeRun',(flag)=>{
        if(flag){
          if(this.ifNeedTime == 1){
            this.timing = 0;
            this.changeTime();
          }
          if(this.ifNeedTime == 2){
            this.curTime = this.baseTime[this.levelStartNum];
            this.timer ? clearInterval(this.timer) : '';
            this.timer = setInterval(this.showTime,1000);
          }
        }
      })
    },

    // 提示游戏玩法 逻辑处理
    showTipsDo(funcName,callback){
      // console.log(funcName , this.showTipsFuncName)
      // 初始化时间
      if(this.ifNeedTime == 1){
        this.curTime = 0;
      }
      if(this.ifNeedTime == 2){
        this.curTime = this.baseTime[this.levelStartNum];
      }
      if(funcName != this.showTipsFuncName){
        callback && callback(true);
        return;
      }
      // 游戏开始前 提示游戏玩法
      if((this.showTipsFlag == 1 && !localStorage.getItem('isShowTips')) || (this.showTipsFlag == 2 && this.levelStartNum == 0 && !this.showTipsEnd)){
        this.showTips = true;
        // 一轮手势引导动画2s
        setTimeout(()=>{
          this.showTips = false;
          this.showTipsEnd = true;
          if(this.showTipsFlag == 1) localStorage.setItem('isShowTips',1)
        },this.showTipsTime)
        callback && callback(false);
      }else{
        callback && callback(true);
      }
    },

    // 开始游戏
    gameRun(){
      // 当前关卡闯关结束后再继续下一关
      if(this.gameEnd || this.isClearAll){
        if(this.isClearAll){
          // 闯关成功
          if(this.levelStartNum < this.LevelNum.length - 1){
            this.levelStartNum++;
          }else{
            // 全部关卡闯关成功,重新第1关再开始
            this.levelStartNum = 0;
          }
        }else{
          // 闯关失败
          if(this.ifFailResetLevelStartNum){
            if(!this.autoEnterNextLevel){
              this.levelStartNum = 0;
            }else{
              // 每关闯关结束无论成功失败,自动进入下关
              this.levelStartNum++;
            }
          }
        }
      }
      this.gameEnd = false;
      this.isClearAll = false;
      this.$nextTick(()=>{
        this.gameStart();
      })
    },

    // 当前关卡闯关结束 | 游戏结束
    gameOver(){
      if(this.autoEnterNextLevel && (this.levelStartNum < this.LevelNum.length - 1)){
        // 每关闯关结束无论成功失败,自动进入下关
        this.gameRun();
      }else{
        this.$emit('gameOver',this.isClearAll,this.gameEnd,this.levelStartNum,this.LevelNum.length);
      }
    },

    // 初始化游戏 当前关卡 翻牌对应的图片
    gameStart(){
      this.isStartGame = false;
      this.inGame = false;
      this.currBrandIndex1 = '';
      this.currBrandIndex2 = '';
      this.clearBrandArr = [];
      this.currLevelData = [];

      let currBrandList = [].concat(this.gameBrandList).sort(function(a, b){
        return 0.5 - Math.random();
      });
      let currLevelLen = this.LevelNum[this.levelStartNum];
      let currLevelData = [];
      for(let i = 0; i < currLevelLen / 2; i++){
        if(i > this.gameBrandList.length - 1){
          // 12个牌子不够双倍匹配
          let randomKey = Math.floor(Math.random() * this.gameBrandList.length);
          currLevelData[i] = currBrandList[randomKey];
          currLevelData[currLevelLen - 1 - i] = currBrandList[randomKey];
        }else{
          currLevelData[i] = currBrandList[i];
          currLevelData[currLevelLen - 1 - i] = currBrandList[i];
        }
      }
      this.currLevelData = currLevelData.sort(function(a, b){
        return 0.5 - Math.random();
      });
      this.$nextTick(()=>{
        this.showGame = true;
        this.isStartGame = true;
        this.goInGame();
      })
    },
    // 用户可以去点击清除操作了
    goInGame(){
      this.showTipsDo('goInGame',(flag)=>{
        if(flag){
          this.memoryTime = this.memoryDurationTime;
          this.memoryDoing();
        }
      })
    },
    // 记忆时长倒计时
    memoryDoing(){
      if(this.memoryTime < 0){
        this.memoryShow = false;
        this.inGame = true;
        this.setTimeRun();
        return;
      }
      this.memoryShow = true;
      clearTimeout(this.timer);
      this.timer = setTimeout(()=>{
        this.memoryTime--
        this.memoryDoing();
      },1000)
    },
    // 点击格子
    chooseBrand(index){
      // 倒计时结束不可点击
      if(this.gameEnd){
        console.log('倒计时结束不可点击');
        return;
      }
      if(!this.inGame || this.currBrandIndex1 === index || (this.currBrandIndex1 && this.currBrandIndex2) || this.clearBrandArr.indexOf(index) !== -1){
        return;
      }
      if(this.currBrandIndex1 !== ''){
        this.currBrandIndex2 = index;
        if(this.currLevelData[index] == this.currLevelData[this.currBrandIndex1] && index != this.currBrandIndex1){
          // 两点击一样
          this.clearBrand(this.currBrandIndex1,index);
        }else{
          // 两点击不一样
          setTimeout(()=>{
            this.currBrandIndex1 = '';
            this.currBrandIndex2 = '';
          },300)
        }
      }else{
        this.currBrandIndex1 = index;
      }
    },
    // 两次点击相同清除格子
    clearBrand(index,index2){
      setTimeout(()=>{
        if(this.clearBrandArr.indexOf(index) !== -1 && this.clearBrandArr.indexOf(index2) !== -1){
          // 快速点击情况不记录
        }else{
          this.currBrandIndex1 = '';
          this.currBrandIndex2 = '';
          this.clearBrandArr.push(index,index2);
          this.$nextTick(()=>{
            if(this.clearBrandArr.length == this.LevelNum[this.levelStartNum]){
              // 清除完毕
              // this.gameRun();
              if(!this.gameEnd){
                this.isClearAll = true;
                this.gameOver();
              }
            }
          })
        }
      },300)
    },

  }
}
</script>

<style scoped>
.page{ width:100%; height:100%; position:absolute; left:0; top:0; overflow: hidden;}

/* 游戏页 */
.page.game{ background-color: #fff;}
.game_body{ height: 1104px; padding-top: 60px; background: url(../assets/img/game_1.png) no-repeat center top; background-size: 100%; position: relative;}
.game_body::after{ content: ""; width: 100%; min-height: calc(100vh - 1104px); height: 396px; background: url(../assets/img/game_2.png); background-size: 100% 100%; position: absolute; left: 0; top: 1102px;}
.game_time{ height: 62px; line-height: 53px; color: #FFFDF4; font-size: 36px; font-weight: bold;
  display: flex; justify-content: center; align-items: center;
  text-shadow: 2px 2px 0 #895F41 , 2px 2px 0 #895F41 ,  -2px -2px 0 #895F41 , -2px -2px 0 #895F41 ,  2px -2px 0 #895F41 , 2px -2px 0 #895F41 ,  -2px 2px 0 #895F41 , -2px 2px 0 #895F41;
}
.game_box{ height: 940px; margin-top: 42px;}
.game_list{
  display: flex; flex-direction: row; flex-wrap: wrap; justify-content: flex-start; align-items: flex-start;
}
.game_list li img{ width: 100%; height: 100%; position: absolute; left: 0; top: 0;
  transition: all .2s linear;
}
.game_list li img.show{
  transform: scaleX(1);
}
.game_list li img.hide{ display: block !important;
  transform: scaleX(0);
}
/* 12格 */
.game_list.first_level{ padding: 0 98px;}
.game_list.first_level li{ width: 168px; height: 222px; position: relative;
  animation: fadeToDown .5s linear both;
}
.game_list.first_level li:nth-child(3n + 2){ margin: 0 calc((100% - 168px * 3) / 2);}
.game_list.first_level li:nth-child(3) ~ li{ margin-top: 15px;}
/* 20格 */
.game_list.second_level{ padding: 0 82px;}
.game_list.second_level li{ width: 131px; height: 173px; position: relative;
  animation: fadeToDown .5s linear both;
}
.game_list.second_level li{ margin-left: calc((100% - 131px * 4) / 3);}
.game_list.second_level li:nth-child(4n + 1){ margin-left: 0;}
.game_list.second_level li:nth-child(4) ~ li{ margin-top: 10px;}
/* 30格 */
.game_list.third_level{ padding: 0 71px;}
.game_list.third_level li{ width: 108px; height: 143px; position: relative;
  animation: fadeToDown .5s linear both;
}
.game_list.third_level li{ margin-left: calc((100% - 108px * 5) / 4);}
.game_list.third_level li:nth-child(5n + 1){ margin-left: 0;}
.game_list.third_level li:nth-child(5) ~ li{ margin-top: 8px;}

/* 提示游戏玩法 */
.game_tips{ width: 100%; height: 100%; padding-top: 438px; background-color: rgba(0,0,0,.6); position: absolute; left: 0; top: 0;}
.game_tips_body{ width: 479px; height: 365px; margin: 0 auto; background: url(../assets/img/game_4.png); background-size: 100% 100%; position: relative;}
.game_tips_body img{ width: 128px; position: absolute; left: 117px; top: 96px;
  transform-origin: 35px 32px;
  animation: gameTips 2s linear both infinite;
}
@keyframes gameTips{
  0%{ transform: translate(0,0) scale(1.15);}
  15%,30%{ transform: translate(0,0) scale(1);}
  45%{ transform: translate(166px,0) scale(1.15);}
  60%,100%{ transform: translate(166px,0) scale(1);}
}
</style>

父组件页代码:

<template>
  <div>

    <div class="page index">
      <div @click="gameStartDo" class="game_start_btn">开始游戏</div>
    </div>

    <flipBrand ref="gameTemp" @flipBrandMounted="flipBrandLoaded" @gameOver="gameOverEnd"></flipBrand>

    <!-- 闯关结果 -->
    <div v-show="popIndex == 1" class="page pop">
      <div class="pop_body">
        <div class="end_body" :class="{
          'success':isClearAll,
          'fail':!isClearAll,
          }">
          <!-- 成功 -->
          <div v-if="isClearAll" class="end_tips">
              <p>恭喜您 {{levelStartNum + 1}} / {{LevelNumLen}} 关卡,闯关成功</p>
          </div>
          <!-- 失败 -->
          <div v-else class="end_tips">
              <p>很遗憾 {{levelStartNum + 1}} / {{LevelNumLen}} 关卡,闯关失败</p>
          </div>
          <!-- 成功-下一关 -->
          <button v-if="isClearAll && levelStartNum + 1 < LevelNumLen" class="end_btn end_btn1" @click="againGame">成功-下一关</button>
          <!-- 成功-所有关卡挑战成功 -->
          <button v-if="isClearAll && levelStartNum + 1 == LevelNumLen" class="end_btn end_btn1" @click="hideGameOver">成功-所有关卡挑战成功</button>
          <!-- 失败-重新挑战 -->
          <button v-if="!isClearAll" class="end_btn end_btn1" @click="againGame">失败-重新挑战</button>
          <!-- 关闭 -->
          <button class="end_btn end_btn2" @click="hideGameOver">关闭</button>
        </div>
      </div>
    </div>

  </div>
</template>

<script>
export default {
  name: 'index',
  components:{
    flipBrand:()=>import("@/views/flipBrand")
  },
  data() {
    return {
      popIndex:0, // 1:闯关结果

      isClearAll:'', //清除完毕游戏结束 正计时时用于结束计时
      gameEnd:'', //倒计时结束,游戏结束
      levelStartNum:'', //当前关卡数
      LevelNumLen:'', //总关卡数
    }
  },
  created(){
  },
  mounted(){
    
  },
  watch: {
  },
  methods:{
    // 游戏组件加载完毕
    flipBrandLoaded(){
      // 开始闯关
      // this.$refs.gameTemp.gameRun();
    },
    // 当前关卡闯关结束 | 游戏结束
    gameOverEnd(isClearAll,gameEnd,levelStartNum,LevelNumLen){
      // isClearAll:'', //清除完毕游戏结束 正计时时用于结束计时
      // gameEnd:'', //倒计时结束,游戏结束
      // levelStartNum 当前关卡数
      // LevelNumLen 总关卡数
      this.isClearAll = isClearAll;
      this.gameEnd = gameEnd;
      this.levelStartNum = levelStartNum;
      this.LevelNumLen = LevelNumLen;
      this.$nextTick(()=>{
        this.gameOver(isClearAll);
      })
    },
    // 开始游戏
    gameStartDo(){
      btnClickDo('.game_start_btn',()=>{
        this.$refs.gameTemp.gameRun();
      })
    },
    // 闯关结果
    gameOver(isClearAll){
      // if(isClearAll){
        this.popIndex = 1;
      // }
    },

    // 闯关结果(成功-下一关 / 失败-重新挑战)
    againGame(){
      btnClickDo('.end_btn1',()=>{
        this.popIndex = 0;
        this.gameStartDo();
      })
    },
    // 游戏结束-关闭
    hideGameOver(){
      btnClickDo('.end_btn2',()=>{
        this.popIndex = 0;
        this.$refs.gameTemp.showGame = false;
      })
    },

  }
}
</script>

<style scoped>
.page{ width:100vw; height:100vh; position:fixed; left:0; top:0; overflow: hidden;}


/* 首页 */
.page.index{ background-color: #fff;
  display: flex; justify-content: center; align-items: center;
}
.game_start_btn{ width: 280px; height: 80px; line-height: 80px; text-align: center; color: #fff; border-radius: 40px; background-color: green;}

/* 弹层 */
.page.pop{ background-color: rgba(0,0,0,.5); padding-bottom: 100px;
  display: flex; justify-content: center; align-items: center;
}
.pop_body{ position: relative;}
/* 游戏结束 */
.end_body{ width: 600px; padding: 80px 20px; border-radius: 20px; background-color: #fff;}
.end_body.success{}
.end_body.fail{}
.end_tips{ padding-bottom: 40px; text-align: center;}
.end_tips p{ line-height: 76px; font-size: 36px; font-weight: bold;}
.end_btn{ display: block; width: 370px; height: 80px; margin: 35px auto 0 auto; color: #fff; font-size: 30px; border-radius: 40px; border: none;}
.end_body.success .end_btn{ background-color: green;}
.end_body.fail .end_btn{ background-color: red;}
</style>

图片资源:

 game_1.png

game_2.png

 

game_3.png

game_4.png

game_5.png

 

game/1.png 至 game/12.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值