我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Vue+TailWindcss怎么实现一个简单的闯关小游戏

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Vue+TailWindcss怎么实现一个简单的闯关小游戏

今天小编给大家分享一下Vue+TailWindcss怎么实现一个简单的闯关小游戏的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

游戏介绍

Vue+TailWindcss怎么实现一个简单的闯关小游戏

这是一款2d益智闯关游戏,玩家须躲避敌人与陷阱到达终点 拥有多个关卡

Vue+TailWindcss怎么实现一个简单的闯关小游戏

可进行关卡的自定义并留存数据

Vue+TailWindcss怎么实现一个简单的闯关小游戏

实现技术

vue tailwindcss

本游特色

  • 自定义关卡

  • 敌人自动索敌

  • 低技术力

  • you win!

技术实现

初始化页面

创建一个json文件,用来存放初始关卡的变量(只有一关。。。) 为方块设定大小,初始化变量speed设置为176,棋盘的宽高就各位4个speed,方块宽高就是1个speed,方块移动一格就是speed * 1,两格就是speed * 2

<!-- 棋盘 --><div :>    <!-- 每一个小方块 -->    <div :></div></div>
const speed = ref(176);

Level是一个json文件,里面放着第一关的各种变量,用来在没有关卡的时候初始化一个关卡

level.json

[    {        "id": 1,// 第一关        "speed": 176,// 方块大小        "top": 528,// 主角top值        "left": 0,// 主角left值        "enemy_top": 0,// 敌人top值        "enemy_left": 352,// 敌人left值        "enemy_top_2": 528,// 敌人2的top值        "enemy_left_2": 352,// 敌人2的left值        "obstacle_top": 176,// 障碍top值        "obstacle_left": 352,// 障碍left值        "trap_top": 352,// 陷阱top值        "trap_left": 176,// 陷阱left值        "spot_top": 0,// 终点top值        "spot_left": 528// 终点left值    }]

在加载页面的时候判断是否有数据如果没有的话添加

import Level from "../../api/level.json";let res = JSON.parse(localStorage.getItem("data"));if (!res) {    localStorage.setItem("data", JSON.stringify(Level));}

小方块设置

使用绝对定位,用transition-all让方块看起来有动画效果

<div class="absolute transition-all"></div>

为小方块设置特定的top和left,声明变量然后设置给小方块上

<!-- 终点,我用的spot前缀 --><div :></div><!-- 敌人,我用的enemy前缀(敌人2后缀直接-2) --><div :></div>
const Level = JSON.parse(localStorage.getItem("data"));const spot_top = ref(Level[index].spot_top);const spot_left = ref(Level[index].spot_left);const enemy_top = ref(Level[index].enemy_top);const enemy_left = ref(Level[index].enemy_left);

主角移动

当按下相应按键后执行相应的函数

document.addEventListener("keydown", (e) => {  switch (e.key) {    case "a":      if (is_run.value) {        moveProtagonistA();      }      break;    case "w":      if (is_run.value) {        moveProtagonistW();      }      break;    case "d":      if (is_run.value) {        moveProtagonistD();      }      break;    case "s":      if (is_run.value) {        moveProtagonistS();      }      break;    case "r":      againGame();// 重新开始      break;  }});

四个函数的意思分别是主角块上下左右的移动,本质其实都差不多,差别就在于每个的top和left是不同的,所以咱就挑一个详细说明一下:

当想让主角向左移动时

const moveProtagonistA = () => {  // 自杀判断  if (    left.value == enemy_left.value + speed.value &&    top.value == enemy_top.value  ) {    left.value -= speed.value;    return false;  }  if (left.value == 0) {    // 边界判断    left.value = -20;    setTimeout(() => {      left.value = 0;    }, 100);    return false;  }  // 障碍判断  obstacle = obstacle_left.value + speed.value;  if (top.value == obstacle_top.value && left.value == obstacle) {    left.value = obstacle - 20;    setTimeout(() => {      left.value = obstacle;    }, 100);  } else {    left.value -= speed.value;    freeFindEnemy(enemy_top, enemy_left);    freeFindEnemy(enemy_top_2, enemy_left_2);  }};

函数整体的内容有点小多,咱们来分开解释:

自杀判断

因为在主角移动时,敌人的自动索敌功能也会开启,所以导致当主角向敌人移动的时候因为敌人自动索敌的原因会与主角错开,于是便诞生了这个逻辑,就是判断如果主角的下一步有敌人的话,敌人原地不动,装上敌人game over

 // 自杀判断  if (    left.value == enemy_left.value + speed.value &&    top.value == enemy_top.value  ) {    left.value -= speed.value;    return false;// 如自杀成功则阻止下面的索敌判断  }

Vue+TailWindcss怎么实现一个简单的闯关小游戏

边界判断

如果出界会被拦截并且给一个被拦截的效果提示,因为这个示例是想左移动的时候,所以判断条件也是左边

if (left.value == 0) {    // 这个效果可以让方块回弹一下    left.value = -20;    setTimeout(() => {      left.value = 0;    }, 100);    return false;// 如果碰到边界则阻止像下面的索敌判断  }

Vue+TailWindcss怎么实现一个简单的闯关小游戏

障碍判断 && 索敌

如果关卡中存在障碍的话,当主角触碰到障碍的时候,会跟边界判断拥有一样回弹效果来提示此路不通

如果主角移动没有碰到障碍阻拦的话,则执行正常移动的命令并且执行自动索敌

obstacle = obstacle_left.value + speed.value;if (top.value == obstacle_top.value && left.value == obstacle) {    // 跟上面一样,回弹一下  left.value = obstacle - 20;  setTimeout(() => {    left.value = obstacle;  }, 100);} else {  left.value -= speed.value;// 移动命令  freeFindEnemy(enemy_top, enemy_left);// 敌人1的索敌  freeFindEnemy(enemy_top_2, enemy_left_2);// 敌人2的索敌}

也许你已经看到了(朵拉摆手),在索敌的最后使用的两个函数,这个函数就是自动索敌的逻辑,接下来继续深入~

自动索敌

当主角移动时敌人自动索敌

// 自动索敌const freeFindEnemy = (Etop: any, Eleft: any) => {  let _top = top.value - Etop.value;  let _left = left.value - Eleft.value;  if (Math.abs(_top) > Math.abs(_left)) {    if (_top > 0) {      moveEnemyS(Etop, Eleft);    } else {      moveEnemyW(Etop, Eleft);    }  } else {    if (_left > 0) {      moveEnemyD(Etop, Eleft);    } else {      moveEnemyA(Etop, Eleft);    }  }};

这个里面出现的函数moveEnemy系列是敌人方块的方向移动,逻辑就是判断主角距离敌人的top和left来决定敌人方块的走向,Etop与Eleft需要分别传入的敌人的top和left值,判断拿边距离大就往哪边行动,有大于、小于等于两种情况

由自动索敌又延申出了--敌人移动

敌人移动

敌人移动也是拥有四个函数,基本与主角移动没有区别,但是敌人在碰到障碍的时候会选择绕开,且敌人碰到陷阱的时候会被“吃掉”

拿敌人向下移动来举例

const moveEnemyS = (Etop: any, Eleft: any) => {  // 陷阱判断  if (trap_top.value == Etop.value && trap_left.value == Eleft.value) return;  // 障碍检测判断  obstacle = obstacle_top.value - speed.value;  if (Etop.value == obstacle && Eleft.value == obstacle_left.value) {    // 判断如果碰到障碍    let _left = left.value - Eleft.value;    if (_left > 0) {      Eleft.value += speed.value;    } else {      Eleft.value -= speed.value;    }  } else {    Etop.value += speed.value;  }};

首先是陷阱的判断,如果敌人的top和left与陷阱一致的话则判断敌人掉进了陷阱里,将终止敌人的所有移动

接下来是障碍,判断如果敌人即将要走的方向有障碍挡着的话,就去判断与主角的距离来向左或者向右避开

胜利与失败

在胜利和失败后肯定是要终止所有行动的,正好所有的行动也是由主角移动的函数来触发的,所以先声明一个变量用来控制游戏的进行,然后通过按键在判断这个变量,如果游戏正在进行中则触发移动函数函数,如果游戏未开始或已失败则跳过触发事件,即无响应

case "a":  // is_run即声明的变量,在游戏失败或未开始阶段该变量为false  if (is_run.value) {    moveProtagonistA();  }  break;

当胜利条件符合(即主角碰到终点)时,触发win,即显示win字样并使is_run置为false

Vue+TailWindcss怎么实现一个简单的闯关小游戏

// 主角的topleft是否与终点的topleft重合if (top.value == spot_top.value && left.value == spot_left.value) {    winShow.value = true;    is_run.value = false;  }

当失败条件符合(即主角碰到敌人1或2或者陷阱)时,触发lose,即显示lose字样并使is_run置为false

Vue+TailWindcss怎么实现一个简单的闯关小游戏

if (    (top.value == enemy_top.value && left.value == enemy_left.value) ||    (top.value == enemy_top_2.value && left.value == enemy_left_2.value) ||    (top.value == trap_top.value && left.value == trap_left.value)  ) {    is_run.value = false;    loseShow.value = true;    return;  }

最后一个return的作用是截断,当触发了lose后就不再继续执行了(否则会接着执行win)

编辑关卡

移入移出变色

16个黑块,通过鼠标移入移出判断颜色

<div  v-for="(item, index) in blockList"  :key="index"  :style="{    width: `${speed}px`,    height: `${speed}px`,    background: item.background,  }"  @mousemove="editMove($event, item)"  @mouseleave="editLeave"  class="transition-all"></div><!-- transition-all使样式变换具有过渡效果 -->
const editMove = (event, item) => {  // 如果该方块已经被选中则什么都不做  if (!item.is_confirm) {    for (let i in blockList.value) {      // 选中相应的方块进行变色      if (blockList.value[i].id == item.id) {        blockList.value[i].background = "";      } else if (blockList.value[i].is_confirm) {        blockList.value[i].background = "";      } else {        blockList.value[i].background = "#000";      }    }  }};const editLeave = () => {  for (let i in blockList.value) {    // 如果该方块已经被选中则什么都不做    if (blockList.value[i].is_confirm) {      blockList.value[i].background = "";    } else {       // 选中相应的方块进行变色      blockList.value[i].background = "#000";    }  }};

因为方块被设置后是不能被改变颜色的,所以需要这两个方法对已经被设置的方块进行判断

点击设置

需先点击左侧图例使颜色选中,再点击方块使其变色

Vue+TailWindcss怎么实现一个简单的闯关小游戏

图例

<div  v-for="(item, index) in legendList"  :key="index"  class="flex mb-4 items-center text-xl"  @click="colorClick($event, item)">  <div class="legend_sign" :class="item.color"></div>  <div class="w-10"></div>  <div    class="transition-all p-2 rounded-lg"    :class="color == item.color ? color : ''"  >    {{ item.introduce }}  </div></div>
const legendList = [  {    id: 0,    color: "bg-green-500",    introduce: "终点",  },  {    id: 1,    color: "bg-red-500",    introduce: "敌人",  },  {    id: 2,    color: "bg-blue-500",    introduce: "主角",  },  {    id: 3,    color: "bg-gray-500",    introduce: "障碍",  },  {    id: 4,    color: "bg-purple-500",    introduce: "陷阱",  },];

变色逻辑

<!-- 跟移入移出变色的div是同一个div --><!-- 重点看这句::class="item.color" --><div  v-for="(item, index) in blockList"  :key="index"  :style="{    width: `${speed}px`,    height: `${speed}px`,    background: item.background,  }"  :class="item.color"  @click="editClick($event, item)"  @mousemove="editMove($event, item)"  @mouseleave="editLeave"  class="transition-all"></div>
const editMove = (event, item) => {  if (!item.is_confirm) {    for (let i in blockList.value) {      if (blockList.value[i].id == item.id) {        // 重点在这两句        blockList.value[i].background = "";        blockList.value[i].color = color.value;      } else if (blockList.value[i].is_confirm) {        blockList.value[i].background = "";      } else {        blockList.value[i].background = "#000";      }    }  }};const editClick = (event, item) => {  // json添加  switch (color.value) {    case "bg-green-500":      if (json.spot_top != 9999) {        tips.value = "终点只能有一个";        return;      }      json.spot_top = item.top;      json.spot_left = item.left;      break;    case "bg-red-500":      if (json.enemy_top != 9999) {        if (json.enemy_top_2 != 9999) {          tips.value = "敌人只能有两个";          return;        }        json.enemy_top_2 = item.top;        json.enemy_left_2 = item.left;        break;      }      json.enemy_top = item.top;      json.enemy_left = item.left;      break;    case "bg-blue-500":      if (json.top != 9999) {        tips.value = "主角只能有一个";        return;      }      json.top = item.top;      json.left = item.left;      break;    case "bg-gray-500":      if (json.obstacle_top != 9999) {        tips.value = "障碍只能有一个";        return;      }      json.obstacle_top = item.top;      json.obstacle_left = item.left;      break;    case "bg-purple-500":      if (json.trap_top != 9999) {        tips.value = "陷阱只能有一个";        return;      }      json.trap_top = item.top;      json.trap_left = item.left;      break;    default:      tips.value = "请先选择颜色~";      return;  }  // 状态保留  for (let i in blockList.value) {    if (blockList.value[i].id == item.id) {      blockList.value[i].background = "";      blockList.value[i].color = color.value;      blockList.value[i].is_confirm = true;    } else if (blockList.value[i].is_confirm) {      blockList.value[i].background = "";    } else {      blockList.value[i].background = "#000";    }  }};

首先是通过点击图例来保存颜色,然后在鼠标移入黑块的时候不再是白色,而是选中的颜色,在点击的时候能将颜色固定到黑块上

因为style的优先级要比class大(background比bg-red-500大),所以在悬浮时需要将背景颜色去掉:

blockList.value[i].background = "";blockList.value[i].color = color.value;

在点击的时候需要保留这个颜色,所以在点击的时候要将本来的颜色改变,并且在悬浮上去后不会变色

blockList.value[i].background = "";blockList.value[i].color = color.value;blockList.value[i].is_confirm = true;

is_confirm在上面已经出现过一两次,表示的是这个块是否被设置,如果被设置了则不对它做任何操作

const editMove = (event, item) => {  if (!item.is_confirm) {    ...  }};

保存关卡

对每个被设置的块记住位置,在点击保存关卡的时候将它放到本地存储里,这样一个新的关卡就生成了

【gif保存关卡】

初始时将所有top left全都设置为9999,在点击方块的时候记录方块的top left和颜色来向一个数组中传入数据,并且对块的数量做出限制,这里拿主角来举例:

switch(color.value){  case "bg-blue-500":      if (json.top != 9999) {        tips.value = "主角只能有一个";        return;      }      // 将主角的top lef填入对应的地方      json.top = item.top;      json.left = item.left;      break;}

在点击保存关卡时将数组添加进本地存储

const Level = JSON.parse(localStorage.getItem("data"));let json = {  id: Level.length + 1,  speed: 176,  top: 9999,  left: 9999,  enemy_top: 9999,  enemy_left: 9999,  enemy_top_2: 9999,  enemy_left_2: 9999,  obstacle_top: 9999,  obstacle_left: 9999,  trap_top: 9999,  trap_left: 9999,  spot_top: 9999,  spot_left: 9999,};...const saveClick = () => {  Level.push(json);  localStorage.setItem("data", JSON.stringify(Level));  button_text.value = "保存成功";  router.push("/main");};

以上就是“Vue+TailWindcss怎么实现一个简单的闯关小游戏”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Vue+TailWindcss怎么实现一个简单的闯关小游戏

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Vue+TailWindcss怎么实现一个简单的闯关小游戏

今天小编给大家分享一下Vue+TailWindcss怎么实现一个简单的闯关小游戏的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧
2023-06-29

Python怎么实现简单2048小游戏

这篇文章主要介绍了Python怎么实现简单2048小游戏,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。简单的2048小游戏不多说,直接上图,这里并未实现GUI之类的,需要的话
2023-06-15

java怎么实现简单猜拳小游戏

本篇内容主要讲解“java怎么实现简单猜拳小游戏”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“java怎么实现简单猜拳小游戏”吧!User.javaimport java.util.Scanne
2023-06-25

怎么用Js写一个简单的五子棋小游戏

这篇文章主要讲解了“怎么用Js写一个简单的五子棋小游戏”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用Js写一个简单的五子棋小游戏”吧!这里的五子棋只做一些基础的功能,对于相对专业的规则
2023-07-02

python怎么实现简单的井字棋小游戏

这篇文章主要讲解了“python怎么实现简单的井字棋小游戏”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“python怎么实现简单的井字棋小游戏”吧!窗口万能的窗口,实现窗口都可以进行简单的修
2023-06-30

怎么使用python实现一个简单的贪吃蛇游戏

本篇内容主要讲解“怎么使用python实现一个简单的贪吃蛇游戏”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么使用python实现一个简单的贪吃蛇游戏”吧!pygame 写的“贪吃蛇”小游戏:
2023-07-02

java怎么实现简单五子棋小游戏

本篇文章为大家展示了java怎么实现简单五子棋小游戏,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。讲解五子棋,实际上就是用一个数组来实现的。没有其他很复杂的结构。首先我们制作五子棋,先要有一个棋盘。
2023-06-26

怎么使用Python+Pygame实现简单的单词小游戏

本篇内容主要讲解“怎么使用Python+Pygame实现简单的单词小游戏”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么使用Python+Pygame实现简单的单词小游戏”吧!一、环境准备1)
2023-07-05

C语言怎么实现简单的抽卡小游戏

以下是一个简单的抽卡小游戏的C语言实现示例:```c#include #include #include int main() {int cards[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};int num
2023-08-15

怎么用Python编写一个简单的游戏

本篇内容介绍了“怎么用Python编写一个简单的游戏”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!在这个系列中,我们要用不同的编程语言编写相
2023-06-15

怎么利用Java实现简单的猜数字小游戏

这篇文章主要讲解了“怎么利用Java实现简单的猜数字小游戏”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么利用Java实现简单的猜数字小游戏”吧!实现思路由计算机随机产生1~100的整数。
2023-06-30

使用js怎么实现简单贪吃蛇小游戏

本篇文章给大家分享的是有关使用js怎么实现简单贪吃蛇小游戏,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。
2023-06-15

Python中怎么实现一个猜数小游戏

Python中怎么实现一个猜数小游戏,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。实现效果: 程序截图:点击(此处)折叠或打开from random import rand
2023-06-04

怎么用C语言实现简单五子棋小游戏

这篇文章主要讲解了“怎么用C语言实现简单五子棋小游戏”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用C语言实现简单五子棋小游戏”吧!本文实例为大家分享了C语言实现简单五子棋小游戏的具体代
2023-06-20

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录