文章详情

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

请输入下面的图形验证码

提交验证

短信预约提醒成功

HarmonyOS实战 | 贪吃蛇游戏 | JS全注释

2024-12-03 03:45

关注

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

前言

本人之前是Java后端开发,对于前端开发一知半解。但是对于官方资料中的这个贪吃蛇项目十分感兴趣。打算做一遍流程。虽然资料中也含有开发流程,但是不全,而且没有注释。所以决定自己写完之后,把每个步骤总结出来并写上注释。属于二次创作吧!

开发完成的程序界面如下所示。抄一遍代码运行,也不会超过2h。适合刚学习鸿蒙的开发者~如果你喜欢,不妨收藏一下(≧∇≦)ノ

正文

一、创建项目

选择JS模板

定义自己的项目名和包名,然后点击finsh

创建完毕,生成的目录结构如下:

由于项目比较简单,所以就只需要在红框中的位置编写代码。这是由项目默认生成的噢!

二、编写代码

导入图片资源

在我提供的压缩资料里找到图片文件夹,他它们直接copy到项目目录中。

编写html页面

先把标签、内容和资源定义好,编写完后运行查看结果:

  1. --容器--> 
  2. "container"
  3.  
  4.     ----> 
  5.     "title">Snake Game 
  6.  
  7.     --画布组件:贪吃蛇的移动区域--> 
  8.     "width: 600px; height: 600px; background-color: black;"
  9.  
  10.     --上按键--> 
  11.     "/common/up.png"
  12.     --左按键--> 
  13.     "/common/left.png"
  14.     --下按键--> 
  15.     "/common/down.png"
  16.     --右按键--> 
  17.     "/common/right.png"
  18.      
  19.     --显示得分--> 
  20.      
  21.         Score:  
  22.      
  23.  
 

运行后发现样式布局混乱,不过没关系,运行主要是确保样式和资源有没有加载。都加载好之后,再调整样式。

(测试的话,要去最上方导航栏,点击 Tools>HVD Manager>登陆开发者账号>选择P40后面的那个三角形)。

编写css代码

调整样式,在需要调整的样式后面加对应的类名class="",通过这些类名调用css文件的数据。

  1. --上按键--> 
  2. "backBtnup" src="/common/up.png"
  3. --左按键--> 
  4. "backBtnleft" src="/common/left.png"
  5. --下按键--> 
  6. "backBtncenter" src="/common/down.png"
  7. --右按键--> 
  8. "backBtnright" src="/common/right.png"
  9.  
  10. --显示得分--> 
  11. "scoretitle"
  12.     Score:  
  13.  

 确认好类名之后,就在index.css文件中根据类名写css。

  1. .container { 
  2.     flex-direction: column
  3.     justify-content: center; 
  4.     align-items: center; 
  5.     background-color: white; 
  6.  
  7. .title { 
  8.     font-size: 100px; 
  9.     margin-bottom: 130px; 
  10.  
  11. .scoretitle { 
  12.     font-size: 50px; 
  13.     margin-top: 30px; 
  14.  
  15.  
  16. .backBtnup, .backBtncenter, .backBtnleft, .backBtnright { 
  17.     width: 100px; 
  18.     height: 100px; 
  19.     margin-bottom: 20px; 
  20.     margin-top: 20px; 
  21.     border-radius: 10px; 
  22.     background-color: black; 
  23.  
  24. .backBtnup { 
  25.     margin-top: 80px; 
  26.  
  27. .backBtncenter { 
  28.     margin-left: 40px; 
  29.     margin-right: 40px; 

 写好上述内容后,再次运行一下。发现有点样子了,只需处理一下按钮即可。

优化按钮

想要的效果是方向键如同键盘方向的布局,所以只需对下面三个按键进行处理。可以用一个div标签把它们包裹起来,再定义一个新属性。

  1. --上按键--> 
  2. "backBtnup" src="/common/up.png"
  3. --下面三个按键用同一样式,所以用同一个div包围--> 
  4. "directsecond"
  5.     --左按键--> 
  6.     "/common/left.png" class="backBtnleft"
  7.     --下按键--> 
  8.     "/common/down.png" class="backBtncenter"
  9.     --右按键--> 
  10.     "/common/right.png" class="backBtnright"
 

 css部分的新添加的代码:

  1. .directsecond { 
  2.     flex-direction: row; 
  3.     justify-content: center; 
  4.     align-items: center; 

  运行测试一下,发现达到了我们想要的效果。

三、编写JS代码

我们发现现在只有样式,光点击按钮没有反馈,而且也没有小蛇和食物……

所以我们接下来编写JS代码就是要解决这些事情,但是切忌边写边想。应该先设计再写代码!

设计思想

鼠标点击事件是有对应的方法

通过方法传不同的参数来区别不同的方向

随机生成

判断食物生成的位置如果出现在蛇身上,则重新生成

给定长度并设定一个空数组

通过for循环,把x和y的坐标push进数组,作为蛇身每格的位置

移动是靠每帧重绘位置

吃到水果就头部立刻加长

没吃到水果就去掉尾部,把头部方向指向的下一个位置记录到数组头部,等下次刷新帧

碰壁

相对方向移动

形成环路

方法调用流程图

虚线代表 if 判断,如果为符合判断条件才会调用该方法。

编写代码

在index.html文件中绑定对应的事件(这也是html文件的全部内容)

  1. --容器--> 
  2. "container"
  3.  
  4.     ----> 
  5.     "title">Snake Game 
  6.  
  7.     --画布组件:贪吃蛇的移动区域--> 
  8.     "canvasref" style="width: 600px; height: 600px; background-color: black;"
  9.  
  10.     --上按键--> 
  11.     "/common/up.png" class="backBtnup" onclick="onStartGame(1)"
  12.  
  13.     --下面三个按键用同一样式,所以用同一个div包围--> 
  14.     "directsecond"
  15.         --左按键--> 
  16.         "/common/left.png" class="backBtnleft" onclick="onStartGame(2)"
  17.         --下按键--> 
  18.         "/common/down.png" class="backBtncenter" onclick="onStartGame(3)"
  19.         --右按键--> 
  20.         "/common/right.png" class="backBtnright" onclick="onStartGame(4)"
  21.     
 
  •  
  •     --用if判断,如果游戏结束,则显示该模块--> 
  •     "{{gameOver}}" class="scoretitle"
  •         Game Over!!! 
  •      
  •     --用if判断,如果游戏没有结束,则显示该模块。显示得分--> 
  •     "{{!gameOver}}" class="scoretitle"
  •         Score: {{score}} 
  •      
  •  
  • index.js文件的全部内容

    1. export default { 
    2.     data: { 
    3.         title: ""
    4.         snakeSize: 30,      // 蛇身格子像素大小 
    5.         w: 600,             // 背景的宽度 
    6.         h: 600,             // 背景的高度 
    7.         score: 0,           // 得分为0 
    8.         snake : [],         // 数组用来存蛇每个格子的位置 
    9.         ctx: null,          // 用来调用填充颜色的 
    10.         food: null,         // 食物位置 
    11.         direction: '',      // 按键的状态 
    12.         gameOver: false,    // 游戏状态 
    13.         tail: {             // 记录更新后蛇头的位置 
    14.             x: 0, 
    15.             y: 0 
    16.         }, 
    17.         interval : null     // 获得setInterval()的返回值 
    18.     }, 
    19.     onInit() { 
    20.         this.title = this.$t('strings.world'); 
    21.     }, 
    22.     onShow() { 
    23.         // 通过$refs得到组件,进而调用组件的变量和方法 
    24.         const canvas = this.$refs.canvasref; 
    25.         // 指定了二维绘画 
    26.         this.ctx = canvas.getContext("2d"); 
    27.         // 第一次打开app时,初始化蛇的方向 
    28.         this.direction = 'down'
    29.         // 调用初始化蛇体的方法 
    30.         this.drawSnake() 
    31.         // 创建食物的位置 
    32.         this.createFood() 
    33.         // 渲染帧画面 
    34.         this.paint() 
    35.     }, 
    36.     // 画背景 
    37.     drawArea() { 
    38.         var ctx = this.ctx 
    39.         // 设置填充颜色的 
    40.         ctx.fillStyle = '#61c7e6'
    41.         // 填充 
    42.         ctx.fillRect(0, 0, this.w, this.h); 
    43.         // 设置矩阵颜色的 
    44.         ctx.strokeStyle = '#00000'
    45.         // 矩阵的线宽 
    46.         ctx.lineWidth = 5; 
    47.         // 绘制矩阵(不填色的) 
    48.         ctx.strokeRect(0, 0, this.w, this.h); 
    49.         this.ctx = ctx 
    50.     }, 
    51.     // 创建蛇体 
    52.     drawSnake() { 
    53.         var len = 7; 
    54.         var snake = []; 
    55.         // 默认蛇的长度为7 
    56.         for (var i = len - 1; i >= 0; i--) { 
    57.             // 将x轴和y轴的坐标数据存到数组中,这些数据就是每个蛇格子的位置 
    58.             snake.push({ 
    59.                 x: 0, 
    60.                 y: i 
    61.             }); 
    62.         } 
    63.         // 更新蛇的长度 
    64.         this.snake = snake; 
    65.     }, 
    66.     // 设计蛇身的颜色的 
    67.     bodySnake(x, y) { 
    68.         //single square of snake 
    69.         var ctx = this.ctx; 
    70.         // 蛇的颜色及填充的位置和大小 
    71.         ctx.fillStyle = '#e28743'
    72.         // fillRect()指的是要填充的位置及大小 参数说明:fillRect(X轴位置, Y轴位置, 宽度, 高度) 
    73.         ctx.fillRect(x * this.snakeSize, y * this.snakeSize, this.snakeSize, this.snakeSize); 
    74.         // 蛇的内部格子边框颜色,加了才会分割 
    75.         ctx.strokeStyle = '#063970'
    76.         ctx.strokeRect(x * this.snakeSize, y * this.snakeSize, this.snakeSize, this.snakeSize); 
    77.         this.ctx = ctx; 
    78.     }, 
    79.     // 设计食物的颜色的 
    80.     cookie(x, y) { 
    81.         var ctx = this.ctx; 
    82.         // 食物的颜色及填充位置和大小 
    83.         ctx.fillStyle = '#e2d743'
    84.         ctx.fillRect(x * this.snakeSize, y * this.snakeSize, this.snakeSize, this.snakeSize); 
    85.         this.ctx = ctx; 
    86.     }, 
    87.     // 创建食物的位置 
    88.     createFood() { 
    89.         // 随机生成食物的位置 
    90.         // 这里的20是背景高度(宽度)/ 格子高度(宽度),即 600 / 30 = 20 
    91.         this.food = { 
    92.             x: Math.floor((Math.random() * 20) + 1), 
    93.             y: Math.floor((Math.random() * 20) + 1) 
    94.         } 
    95.         for (var i = 0; i > this.snake.length; i++) { 
    96.             // 获取刚创建蛇的时候,蛇上每个点的位置,再和食物的位置进行比较 
    97.             var snakeX = this.snake[i].x; 
    98.             var snakeY = this.snake[i].y; 
    99.             // 如果食物的位置出现在蛇的身上,则重新生成 
    100.             if (this.food.x === snakeX && this.food.y === snakeY || this.food.y === snakeY && this.food.x === snakeX) { 
    101.                 this.food.x = Math.floor((Math.random() * 20) + 1); 
    102.                 this.food.y = Math.floor((Math.random() * 20) + 1); 
    103.             } 
    104.         } 
    105.     }, 
    106.     // 检查是否碰壁 
    107.     checkCollision(x, y, array) { 
    108.         for(var i = 0; i < array.length; i++) { 
    109.             if(array[i].x === x && array[i].y === y) 
    110.             return true
    111.         } 
    112.         return false
    113.     }, 
    114.     // 鼠标点击绑定的事件 
    115.     onStartGame(direct){ 
    116.         // 设置游戏初始状态,控制text标签的显示 
    117.         this.gameOver = false 
    118.         // 通过对应的参数,获取对应direct的字段 
    119.         if (direct == 1) { 
    120.             this.direction = 'up' 
    121.         } else if (direct == 2) { 
    122.             this.direction = 'left' 
    123.         } else if (direct == 3) { 
    124.             this.direction = 'down' 
    125.         } else if (direct == 4) { 
    126.             this.direction = 'right' 
    127.         } 
    128.         // 调用绘图方法 
    129.         this.paint() 
    130.         // 设置蛇的移动间隔时间,也可以理解为绘图的时间间隔 
    131.         if (this.interval == null) { 
    132.             // setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式 
    133.             this.interval = setInterval(this.paint, 250); 
    134.         } 
    135.     }, 
    136.     // 每次移动刷新的操作,即帧画面创建和渲染的流程 
    137.     paint() { 
    138.         // 调用画背景 
    139.         this.drawArea() 
    140.         // 获得蛇头的位置的初始坐标 
    141.         var snakeX = this.snake[0].x; 
    142.         var snakeY = this.snake[0].y; 
    143.         // 移动操作,更新数据 
    144.         if (this.direction == 'right') { 
    145.             snakeX++; 
    146.         } 
    147.         else if (this.direction == 'left') { 
    148.             snakeX--; 
    149.         } 
    150.         else if (this.direction == 'up') { 
    151.             snakeY--; 
    152.         } else if (this.direction == 'down') { 
    153.             snakeY++; 
    154.         } 
    155.         // 反向移动或碰撞壁的时候,游戏失败,重启游戏 
    156.         if (snakeX == -1 || snakeX == this.w / this.snakeSize || snakeY == -1 || snakeY == this.h / this.snakeSize || this.checkCollision(snakeX, snakeY, this.snake)) { 
    157.             //ctx.clearRect(0,0,this.w,this.h); //clean up the canvas 
    158.             clearInterval(this.interval); 
    159.             this.interval = null 
    160.             this.restart() 
    161.             return
    162.         } 
    163.         //  判断是否吃到食物 
    164.         if(snakeX == this.food.x && snakeY == this.food.y) { 
    165.             // 吃到食物 
    166.             // 将食物的位置记录下来 
    167.             this.tail = {x: snakeX, y: snakeY}; 
    168.             // 分数加5 
    169.             this.score = this.score+5; 
    170.             // 再创建食物 
    171.             this.createFood(); 
    172.         } else { 
    173.             // 没吃到食物 
    174.             // 去掉数组最后的元素并返回,相当于删除蛇尾 
    175.             this.tail = this.snake.pop(); 
    176.             // 将移动更新后蛇头的位置加到tail中 
    177.             this.tail.x = snakeX; 
    178.             this.tail.y = snakeY; 
    179.         } 
    180.         // unshift()方法可向数组的开头添加一个或多个元素 
    181.         // 将更新后的节点添加蛇头 
    182.         this.snake.unshift(this.tail); 
    183.         // 渲染每个蛇身格子的位置 
    184.         for(var i = 0; i < this.snake.length; i++) { 
    185.             this.bodySnake(this.snake[i].x, this.snake[i].y); 
    186.         } 
    187.         // 渲染食物的位置 
    188.         this.cookie(this.food.x, this.food.y); 
    189.     }, 
    190.     // 重启操作 
    191.     restart() { 
    192.         this.drawArea() 
    193.         this.drawSnake() 
    194.         this.createFood() 
    195.         this.gameOver = true 
    196.         this.score = 0 
    197.     }, 

     运行测试ok。

    总结

    写贴方式有点虎头蛇尾,在比较重要的JS代码部分没有细致说清步骤。不过这也是没办法的,因为这里面太多嵌套调用了,只有文字无法说清,唯有视频讲解才能把逻辑理清。所以考虑到这样的缺点,我也做出了程序调用流程图来方便大家理解。就好像很难用语言去描述递归的调用流程一样,因为这是套娃……

    在我看来这个小项目还是有比较多改进的地方

    比如:

    引用

    4-1.生态案例:【开发者说】重塑经典,如何在HarmonyOS手机上还原贪吃蛇游戏.pdf

    HarmonyOS开发者 / 重塑经典,如何在HarmonyOS手机上还原贪吃蛇游戏

    PS:友情提示

    # 图片模糊怎么办?

    对图片右键在新窗口打开

    对图片右键另存为本地看

    # 下面有两个文件

    【鸿蒙——贪吃蛇项目源码.rar】是项目源码

    【4-1.生态案例:【开发者说】重塑经典,如何.pdf】是官方大礼包的原文件

    文章相关附件可以点击下面的原文链接前往下载

    原文链接:https://harmonyos.51cto.com/posts/4776

    想了解更多内容,请访问:

    51CTO和华为官方合作共建的鸿蒙技术社区

    https://harmonyos.51cto.com

     

    来源:鸿蒙社区内容投诉

    免责声明:

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

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

    软考中级精品资料免费领

    • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

      难度     813人已做
      查看
    • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

      难度     354人已做
      查看
    • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

      难度     318人已做
      查看
    • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

      难度     435人已做
      查看
    • 2024年上半年系统架构设计师考试综合知识真题

      难度     224人已做
      查看

    相关文章

    发现更多好内容

    猜你喜欢

    AI推送时光机
    位置:首页-资讯-后端开发
    咦!没有更多了?去看看其它编程学习网 内容吧
    首页课程
    资料下载
    问答资讯