Unity手机游戏开发:从搭建到发布上线全流程实战

Posted 是Dream呀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity手机游戏开发:从搭建到发布上线全流程实战相关的知识,希望对你有一定的参考价值。

前言: 技术书籍是学习技术知识的重要资源之一。读技术书可以帮助我们学习新技能和知识,技术书籍提供了可靠的、全面的信息,帮助我们快速学习新技能和知识。同时技术书籍有助于保持你的竞争力,因为它们提供了最新的技术知识和实践。这在当今快速发展的技术领域尤为重要,不断学习新知识和技能才能保持竞争力。总之,读技术书对于学习技术知识、提高职业素养和保持竞争力都非常重要。
Dream联合金主爸爸给大家送书啦!本期为大家带来的是 《Unity手机游戏开发:从搭建到发布上线全流程实战》,再次感谢 北京大学出版社 的大力支持;为Dream粉丝带来的丰厚福利。

Dream推荐

游戏大厂资深主程结合10年游戏开发经验和3年高校实际教学经验,钻研上百款手游案例,总结了自身从初学者成长为技术总监的成长之路,为Unity初学者倾心打造一条全流程实践路线,帮助初学者开发并上线属于自己的第一款开放世界类手游。附赠8节教学视频和案例工程源文件。

内容简介

本书将以一款开放世界类游戏的实践过程为主线,为读者呈现从零开始上线一款游戏的实践路线、游戏引擎Unity的开发模式,以及游戏开发的核心框架。

本书共分为3篇,第1篇是场景搭建篇,第2篇是脚本开发篇,第3篇是发布上线篇。第1篇包含第1章和第2章,主要介绍在Unity中如何创建一个游戏项目,如何搭建一款游戏的场景和界面。第2篇包含第3章到第5章,主要介绍一款游戏的核心模块,即游戏控制、角色动画和核心玩法,同时实践一款游戏《小猪奇奇》的完整开发流程。第3篇包含第6章到第8章,主要介绍游戏的移动端发布流程,同时对游戏进行测试与完善,最终把书中的游戏案例打造成一款符合上线标准的游戏。

本书目录

第1篇 场景搭建篇

第1章 场景搭建

1.1 创建工程

1.1.1 创建目录

1.1.2 设置参数

1.1.3 Unity的界面布局

1.2 创建场景

1.2.1 创建物体

1.2.2 关联脚本

1.3 美化游戏

1.3.1 丰富场景

1.3.2 增加计分功能

1.3.3 试运行

1.4 Beta版本

1.4.1 调整界面

1.4.2 捕捉玩法

1.4.3 关联物体

1.4.4 大功告成

第2章 UI界面

2.1 基础UI界面

2.1.1 登录界面

2.1.2 捕捉界面

2.2 功能UI框架

2.2.1 界面层次结构

2.2.2 核心代码的实现

2.2.3 辅助代码的实现

2.3 Alpha版本

2.3.1 试运行

2.3.2 大功告成

第2篇 脚本开发篇

第3章 游戏控制

3.1 固定3D视角

3.1.1 核心代码的实现

3.1.2 设置摄像机脚本

3.2 实现专业的角色移动

3.2.1 角色移动

3.2.2 角色朝向

3.2.3 动画切换

3.3 实现摇杆控制

3.3.1 添加摇杆界面

3.3.2 添加摇杆输入代码

3.3.3 脚本关联摇杆

3.4 完善游戏功能

3.4.1 添加新场景

3.4.2 摄像机优化

第4章 角色动画

4.1 主角动画

4.1.1 游戏升级

4.1.2 主角动画实现

4.2 敌人动画

4.2.1 有限状态机的简单实现

4.2.2 爬行的蜗牛

第5章 核心玩法

5.1 游戏流程

5.1.1 战斗逻辑

5.1.2 物品掉落

5.2 游戏主角

5.2.1 能力图鉴

5.2.2 背包逻辑

5.2.3 输入控制层

5.3 PC版本

5.3.1 发布PC版本

5.3.2 测试版本功能

第3篇 发布上线篇

第6章 发布移动版

6.1 发布移动端

6.1.1 测试版本功能

6.1.2 切换到安卓发布平台

6.1.3 发布设置

6.1.4 发布流程

6.1.5 发布完成

6.2 游戏部署

6.2.1 选择安卓手机

6.2.2 安装游戏

6.2.3 运行游戏

第7章 测试与完善

7.1 完善游戏功能

7.1.1 界面最终版

7.1.2 金币系统逻辑最终版

7.2 资源优化——清理冗余资源

7.2.1 清理冗余图片

7.2.2 清理冗余模型

7.2.3 清理冗余脚本和冗余代码

第8章 游戏上线

8.1 游戏上架

8.1.1 注册账号

8.1.2 创建游戏

8.1.3 完善资料

8.2 大功告成

写作感悟

笔者很喜欢技术,可以说是非常热爱技术,于是选择了对技术要求很高的游戏行业。从业以来,笔者边工作边学习,不断地在实践中积累经验和知识,提升能力。为了精进自身的核心技术,在工作之余,笔者常研究不同类型的手游项目,这着实让笔者受益匪浅。久而久之,笔者深切地体会到了各类游戏的精妙之处。同时,不同类型的游戏之间总是存在一种共同的规律,这个发现让笔者欣喜若狂。

在欣喜之余,笔者非常希望能够把各类游戏的精妙之处以及存在于它们之间的共同规律分享给同样热爱技术的朋友们,这是本书的由来之一。

在研究了几十款游戏的核心技术之后,笔者发现了另一个问题:在学习一款游戏的技术时,手边总是缺少一本关于游戏实践类的书籍——能够依据正确而具体的路线厘清一款游戏的脉络,从而深入地实践一款游戏。想拥有这类书籍的想法一直萦绕在笔者的心底, 对于想要从零开始研究一款游戏的朋友们而言,他们对这类书籍的渴望一定和笔者一样迫切,这是本书的由来之二。

于是笔者萌生了编写本书的想法,研究了几十款游戏的实践经验,希望通过本书把它们归结为一个通用的实践规律,让大家少走弯路,在实践各类游戏的过程中不断成长。

手机游戏作为大众娱乐的重要途径,行业内聚集了大量的人才资源。使用Unity的开发者约占手游开发人群的42%,这充分说明了游戏引擎Unity的成功。为了成为一名专业的Unity手游开发者,很多人会观看大量的教学视频,甚至盲目地报各种学习班,他们迫切地想通过各种方式加入手游开发者的队伍。笔者希望通过本书提供的正确的方法论帮助大家正确地学习手游开发知识,成为一名真正的手游开发者,切身地感受到手游开发的脉搏所在!

“前路荆棘漫漫,岁月静好如初”是笔者很喜欢的一句话,也是一名手游开发者应该一直保持的心态。从做Vega Prime军事仿真项目,到在蓝港游戏开发Unity手机游戏,再到任职乐视VR(虚拟现实)的技术总监,笔者几乎接触了基于Unity 开发的所有应用形态, 比如VR、AR(增强现实)、游戏,甚至是时下流行的元宇宙。

笔者是从2013年的第一份工作开始接触Unity的,到2017年,已经是乐视VR的技术负责人了。笔者用了短短的4年时间,完成了技术工作的一个大循环,身边的朋友们经常说笔者是“开挂”了。事实上,学习绝对不是一蹴而就的事情。在这4年的学习过程中,从一个零基础的“菜鸟”到一个大企业的技术总监,笔者学会了慢慢学习,也学会了实践学习。实践学习是本书采用并且推崇的学习方法,俗话说“熟能生巧”,只要多多实践,相信大家一定能不断地成长,也一定能掌握任何一门技术。

从工作开始,笔者便时常在北京各大高校讲授与Unity相关的课程,也在知乎分享了一些学习心得。在这段分享的时光里,笔者结识了许许多多热爱游戏的朋友,也遇到了很多对Unity开发感兴趣的人。有些朋友经常问笔者是否有一种能让零基础的初学者快速精通Unity的学习方法,还有些朋友觉得游戏开发是一件遥不可及的事情,精通Unity一定需要很多年的历练才行,这跟笔者对于手游开发的感受不同。笔者认为,任何技术都可以通过实践来快速习得,手游开发也不例外。

本书的出发点和解决的痛点

笔者希望通过讲解一个真实的游戏案例的实践过程,能够让读者制作出一款真正的手机游戏,而且是一款资深游戏开发者才能搞定的开放世界类手游,以此来打破大家对手游开发的畏难心理。大家熟悉的游戏《原神》就是一款开放世界类手游,可以想象一下,实践完本书的内容,你将成为一个独立的手游开发者,并且能完整地开发出一款类似《原神》的开放世界类手游,这岂不是一件大好事?

言归正传,游戏公司开发一款开放世界类手游通常需要有一个几十人的制作团队,而这种现象往往会给初学者,甚至中级开发者一个错误的引导,让他们觉得开放世界类手游的开发很难,甚至产生畏学心理。想成为一名资深的游戏开发者到底要经过多少道门槛?一个初学者什么时候才能成为一个主程(即主要的技术开发程序员)呢?

事实上,大多数程序员需要三到五年的实践学习才能成为一个主程,才有机会接触、开发、搭建一款游戏的核心框架,这导致很多程序员根本没有机会学习到一款游戏最重要的部分,即核心框架。没有机会承担游戏的核心框架的开发工作,又怎么可能成长为一名资深的手游开发者呢?

通常,一名程序员的职业生涯中大部分的时间都花费在打磨普通的功能模块上,而没有足够的时间去认真地打磨自身的硬核能力。举个例子,如果你刚开始做的是游戏的背包系统,那么在之后的若干款游戏的制作中,公司都会让你负责做背包系统,你的工作就是做更多的普通功能模块。当然,有一部分核心主程是公司重点培养的技术骨干,只有这部分主程才有机会不断地打磨自身的硬核能力,这也是大多数程序员喜欢称自己是“搬砖工”的主要原因。

上述困难正是本书要回答并解决的问题。如果读者朋友们能够认真地跟随本书一步一步地完成实践,那么你们也将和游戏公司里的核心主程,甚至技术总监一样,具备游戏开发的硬核能力!

说到这里,大家会觉得,原来这是写给初级程序员的一本书啊,实际上绝非如此。笔者希望每一个与手游制作相关的人员在看完本书之后都能有所收获,所以本书以真实的商业化项目来做实践案例。只有真实的项目才能给初学者建立一个正确的手游开发观,才能给中级开发者指明一条深入学习硬核技术的途径,才能扩展资深游戏开发者的技术视野。

倘若你不是一名程序员,而是一名游戏建模人员,或是一名游戏策划人员,又或是一名游戏制作人,如果认真地阅读本书,你将走进一名游戏开发者的技术世界,体会真正的技术魅力。哪怕你只是一个纯粹的游戏爱好者,如果静下心来阅读本书,也将拥有一个全新的视角,去正确地看待游戏、游戏行业以及游戏人,并且能塑造一个崭新的游戏观。

对读者的建议

1.初学者

本书是零基础入门的极佳实践书籍,以一种梯度化的难易程度,实践一款简单却很高级的游戏。哪怕你是一名零基础的初学者,只要认真地实践本书的游戏案例,也一定能够成功地上线一款手机游戏。这不是一本传统的理论书籍,书中将只讲解一些C#语言和Unity的常用知识,主要梳理的是实践过程中涉及的知识点,以保证读者能够理解、学会并使用这些知识。本书是一款手游开发的全流程实践教程,这一点决定了本书将非常适合初学者。笔者对初学者学习本书的唯一要求是,慢慢实践,多多交流!

2.初中级程序员

如果你是一位初中级程序员,正在参与开发一款游戏的功能模块,并且不想一直局限于游戏开发的某一个环节,那么跟着本书一起实践吧!相信有一定基础的你,一定可以通过认真地实践本书的内容来搭建起一款手机游戏的核心框架。此外,你将对自身当前的技术状态有一个非常清晰的认识。最重要的是,本书能明确地告诉你,一个主程在一款游戏的开发工作中应该负责实现哪些功能,以及这些功能是如何实现的。

3.游戏主程

如果你是一个游戏主程,那么本书的最大魅力在于,实践本书的内容能检验你当前的硬核能力,让你可以查漏补缺,有针对性地学习缺乏的知识,补齐短板。另外,倘若你只是擅长开发某一种类型的游戏,那么一定要阅读本书,本书将极大地扩展你的技术视野,拓宽你的技术之路。

在工作中,你是否感觉到技术总监的技术视野很宽,甚至当公司需要设计一个多元化的游戏框架时,一个合格的技术总监也一定能随时随地设计一个,这是因为他们会积极主动地研究各种类型的游戏,不放过每一次实践新技术的机会。所以,大家一定要认真地阅读本书,一步一步地实践下去,通过对比不同类型的游戏技术,找到一把贯通技术之路的钥匙。

4.游戏相关人员

如果你是美术人员、策划人员或制作人,本书将带你走进程序员的技术世界。无论对团队合作还是团队搭建,本书都是你与程序员沟通合作的桥梁。如果你是一名手游爱好者,本书的最大魅力在于,你将进入一个陌生而奇妙的游戏开发世界。

京东自营购买链接:
https://item.jd.com/13557443.html

获取方式

此文章下评论:人生苦短,我爱Dream!即可参与抽取书籍活动! 评论区抽出两位小伙伴免费送出,感谢大家支持,我们评论区见!

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

来源:https://harmonyos.51cto.com/user/posts/15053213

关注回复"教程" 获得最新鸿蒙开发者教程


前言

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

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

 正文

一、创建项目

1. 选择JS模板

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

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

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

二、编写代码

1. 导入图片资源

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

2. 编写html页面

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

<!--容器-->
<div class="container">


    <!--标题-->
    <text class="title">Snake Game</text>


    <!--画布组件:贪吃蛇的移动区域-->
    <canvas style="width: 600px; height: 600px; background-color: black;"></canvas>


    <!--上按键-->
    <image src="/common/up.png"></image>
    <!--左按键-->
    <image src="/common/left.png"></image>
    <!--下按键-->
    <image src="/common/down.png"></image>
    <!--右按键-->
    <image src="/common/right.png"></image>
    
    <!--显示得分-->
    <text>
        <span>Score: </span>
    </text>


</div>

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

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

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

<!--上按键-->
<image class="backBtnup" src="/common/up.png"></image>
<!--左按键-->
<image class="backBtnleft" src="/common/left.png"></image>
<!--下按键-->
<image class="backBtncenter" src="/common/down.png"></image>
<!--右按键-->
<image class="backBtnright" src="/common/right.png"></image>


<!--显示得分-->
<text class="scoretitle">
    <span>Score: </span>
</text>

    

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

.container {
    flex-direction: column;
    justify-content: center;
    align-items: center;
    background-color: white;
}


.title {
    font-size: 100px;
    margin-bottom: 130px;
}


.scoretitle {
    font-size: 50px;
    margin-top: 30px;
}


/*
  css选择器,逗号代表并列关系
  具体可以百度噢,因为前端博大精深
*/
.backBtnup, .backBtncenter, .backBtnleft, .backBtnright {
    width: 100px;
    height: 100px;
    margin-bottom: 20px;
    margin-top: 20px;
    border-radius: 10px;
    background-color: black;
}


.backBtnup {
    margin-top: 80px;
}


.backBtncenter {
    margin-left: 40px;
    margin-right: 40px;
}

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

4. 优化按钮

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

<!--上按键-->
<image class="backBtnup" src="/common/up.png"></image>
<!--下面三个按键用同一样式,所以用同一个div包围-->
<div class="directsecond">
    <!--左按键-->
    <image src="/common/left.png" class="backBtnleft"></image>
    <!--下按键-->
    <image src="/common/down.png" class="backBtncenter"></image>
    <!--右按键-->
    <image src="/common/right.png" class="backBtnright"></image>
</div>

    css部分的新添加的代码

.directsecond {
    flex-direction: row;
    justify-content: center;
    align-items: center;
}

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

三、编写JS代码


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

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

 

1. 设计思想

  • 按钮的触发是通过点击屏幕,所以要有点击事件

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

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

 

  • 食物的生成

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

 

  • 蛇身的初始化 (由于案例比较简单,所以没有设定随机生成初始位置)

给定长度并设定一个空数组
通过for循环,把x和y的坐标push进数组,作为蛇身每格的位置

 

  • 蛇运动

移动是靠每帧重绘位置

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

  • 判定游戏结束

碰壁

相对方向移动
形成环路

 

2. 方法调用流程图

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

3. 编写代码

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

<!--容器-->
<div class="container">


    <!--标题-->
    <text class="title">Snake Game</text>


    <!--画布组件:贪吃蛇的移动区域-->
    <canvas ref="canvasref" style="width: 600px; height: 600px; background-color: black;"></canvas>


    <!--上按键-->
    <image src="/common/up.png" class="backBtnup" onclick="onStartGame(1)"></image>


    <!--下面三个按键用同一样式,所以用同一个div包围-->
    <div class="directsecond">
        <!--左按键-->
        <image src="/common/left.png" class="backBtnleft" onclick="onStartGame(2)"></image>
        <!--下按键-->
        <image src="/common/down.png" class="backBtncenter" onclick="onStartGame(3)"></image>
        <!--右按键-->
        <image src="/common/right.png" class="backBtnright" onclick="onStartGame(4)"></image>
    </div>


    <!--用if判断,如果游戏结束,则显示该模块-->
    <text if="{{gameOver}}" class="scoretitle">
        <span>Game Over!!!</span>
    </text>
    <!--用if判断,如果游戏没有结束,则显示该模块。显示得分-->
    <text if="{{!gameOver}}" class="scoretitle">
        <span>Score: {{score}}</span>
    </text>
</div>

 

  index.js文件的全部内容

export default {
    data: {
        title: "",
        snakeSize: 30,      // 蛇身格子像素大小
        w: 600,             // 背景的宽度
        h: 600,             // 背景的高度
        score: 0,           // 得分为0
        snake : [],         // 数组用来存蛇每个格子的位置
        ctx: null,          // 用来调用填充颜色的
        food: null,         // 食物位置
        direction: '',      // 按键的状态
        gameOver: false,    // 游戏状态
        tail: {             // 记录更新后蛇头的位置
            x: 0,
            y: 0
        },
        interval : null     // 获得setInterval()的返回值
    },
    onInit() {
        this.title = this.$t('strings.world');
    },
    onShow() {
        // 通过$refs得到组件,进而调用组件的变量和方法
        const canvas = this.$refs.canvasref;
        // 指定了二维绘画
        this.ctx = canvas.getContext("2d");
        // 第一次打开app时,初始化蛇的方向
        this.direction = 'down';
        // 调用初始化蛇体的方法
        this.drawSnake()
        // 创建食物的位置
        this.createFood()
        // 渲染帧画面
        this.paint()
    },
    // 画背景
    drawArea() {
        var ctx = this.ctx
        // 设置填充颜色的
        ctx.fillStyle = '#61c7e6';
        // 填充
        ctx.fillRect(0, 0, this.w, this.h);
        // 设置矩阵颜色的
        ctx.strokeStyle = '#00000';
        // 矩阵的线宽
        ctx.lineWidth = 5;
        // 绘制矩阵(不填色的)
        ctx.strokeRect(0, 0, this.w, this.h);
        this.ctx = ctx
    },
    // 创建蛇体
    drawSnake() {
        var len = 7;
        var snake = [];
        // 默认蛇的长度为7
        for (var i = len - 1; i >= 0; i--) {
            // 将x轴和y轴的坐标数据存到数组中,这些数据就是每个蛇格子的位置
            snake.push({
                x: 0,
                y: i
            });
        }
        // 更新蛇的长度
        this.snake = snake;
    },
    // 设计蛇身的颜色的
    bodySnake(x, y) {
        //single square of snake
        var ctx = this.ctx;
        // 蛇的颜色及填充的位置和大小
        ctx.fillStyle = '#e28743';
        // fillRect()指的是要填充的位置及大小 参数说明:fillRect(X轴位置, Y轴位置, 宽度, 高度)
        ctx.fillRect(x * this.snakeSize, y * this.snakeSize, this.snakeSize, this.snakeSize);
        // 蛇的内部格子边框颜色,加了才会分割
        ctx.strokeStyle = '#063970';
        ctx.strokeRect(x * this.snakeSize, y * this.snakeSize, this.snakeSize, this.snakeSize);
        this.ctx = ctx;
    },
    // 设计食物的颜色的
    cookie(x, y) {
        var ctx = this.ctx;
        // 食物的颜色及填充位置和大小
        ctx.fillStyle = '#e2d743';
        ctx.fillRect(x * this.snakeSize, y * this.snakeSize, this.snakeSize, this.snakeSize);
        this.ctx = ctx;
    },
    // 创建食物的位置
    createFood() {
        // 随机生成食物的位置
        // 这里的20是背景高度(宽度)/ 格子高度(宽度),即 600 / 30 = 20
        this.food = {
            x: Math.floor((Math.random() * 20) + 1),
            y: Math.floor((Math.random() * 20) + 1)
        }
        for (var i = 0; i > this.snake.length; i++) {
            // 获取刚创建蛇的时候,蛇上每个点的位置,再和食物的位置进行比较
            var snakeX = this.snake[i].x;
            var snakeY = this.snake[i].y;
            // 如果食物的位置出现在蛇的身上,则重新生成
            if (this.food.x === snakeX && this.food.y === snakeY || this.food.y === snakeY && this.food.x === snakeX) {
                this.food.x = Math.floor((Math.random() * 20) + 1);
                this.food.y = Math.floor((Math.random() * 20) + 1);
            }
        }
    },
    // 检查是否碰壁
    checkCollision(x, y, array) {
        for(var i = 0; i < array.length; i++) {
            if(array[i].x === x && array[i].y === y)
            return true;
        }
        return false;
    },
    // 鼠标点击绑定的事件
    onStartGame(direct){
        // 设置游戏初始状态,控制text标签的显示
        this.gameOver = false
        // 通过对应的参数,获取对应direct的字段
        if (direct == 1) {
            this.direction = 'up'
        } else if (direct == 2) {
            this.direction = 'left'
        } else if (direct == 3) {
            this.direction = 'down'
        } else if (direct == 4) {
            this.direction = 'right'
        }
        // 调用绘图方法
        this.paint()
        // 设置蛇的移动间隔时间,也可以理解为绘图的时间间隔
        if (this.interval == null) {
            // setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式
            this.interval = setInterval(this.paint, 250);
        }
    },
    // 每次移动刷新的操作,即帧画面创建和渲染的流程
    paint() {
        // 调用画背景
        this.drawArea()
        // 获得蛇头的位置的初始坐标
        var snakeX = this.snake[0].x;
        var snakeY = this.snake[0].y;
        // 移动操作,更新数据
        if (this.direction == 'right') {
            snakeX++;
        }
        else if (this.direction == 'left') {
            snakeX--;
        }
        else if (this.direction == 'up') {
            snakeY--;
        } else if (this.direction == 'down') {
            snakeY++;
        }
        // 反向移动或碰撞壁的时候,游戏失败,重启游戏
        if (snakeX == -1 || snakeX == this.w / this.snakeSize || snakeY == -1 || snakeY == this.h / this.snakeSize || this.checkCollision(snakeX, snakeY, this.snake)) {
            //ctx.clearRect(0,0,this.w,this.h); //clean up the canvas
            clearInterval(this.interval);
            this.interval = null
            this.restart()
            return;
        }
        //  判断是否吃到食物
        if(snakeX == this.food.x && snakeY == this.food.y) {
            // 吃到食物
            // 将食物的位置记录下来
            this.tail = {x: snakeX, y: snakeY};
            // 分数加5
            this.score = this.score+5;
            // 再创建食物
            this.createFood();
        } else {
            // 没吃到食物
            // 去掉数组最后的元素并返回,相当于删除蛇尾
            this.tail = this.snake.pop();
            // 将移动更新后蛇头的位置加到tail中
            this.tail.x = snakeX;
            this.tail.y = snakeY;
        }
        // unshift()方法可向数组的开头添加一个或多个元素
        // 将更新后的节点添加蛇头
        this.snake.unshift(this.tail);
        // 渲染每个蛇身格子的位置
        for(var i = 0; i < this.snake.length; i++) {
            this.bodySnake(this.snake[i].x, this.snake[i].y);
        }
        // 渲染食物的位置
        this.cookie(this.food.x, this.food.y);
    },
    // 重启操作
    restart() {
        this.drawArea()
        this.drawSnake()
        this.createFood()
        this.gameOver = true
        this.score = 0
    },
}

    运行测试ok

总结

 

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


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

比如:

  • 蛇的位置随机生成;

  • 优化按下相对按键的操作:即蛇向右走的时候,按左方向是不会影响蛇的方向,就不会被程序判定失败了;

  • 定制专门的重启游戏按键

  • 等等

--完--

关注「HarmonyOS应用开发者」,一起学习成长

后台回复"教程" 获得最新鸿蒙开发者教程,助你快速上手鸿蒙开发!

以上是关于Unity手机游戏开发:从搭建到发布上线全流程实战的主要内容,如果未能解决你的问题,请参考以下文章

结构建模设计——Solidworks软件之绘制一个手机支架模型,使用3D打印技术输出实物的全流程实战

结构建模设计——Solidworks软件之绘制一个手机支架模型,使用3D打印技术输出实物的全流程实战

Vuejs技术栈从CLI到打包上线实战全解析

一文梳理2048小游戏从开发到上云全流程

Android + Sqlite + Unity3D 踩过的那些坑 & 全流程简介

CI/CD技术专题「Jenkins实战系列」全流程介绍Jenkins环境搭建+基础部署配置(Windows->Linux)