1 00:00:00,000 --> 00:00:05,940 嗨,我叫马克。多年来,我一直想用软件制作我自己的视频游戏。 2 00:00:05,940 --> 00:00:13,320 比如 Unity。Unity 是很多游戏背后强大的游戏引擎,包括:Cuphead、Neon White、Tunic 3 00:00:13,320 --> 00:00:18,600 Outer Wilds,炉石传说,看火者,甚至口袋妖怪钻石重制版。 4 00:00:18,600 --> 00:00:26,280 但我总是发现,太长的、各式各样的、弯的教程只会让我睡死过去。 5 00:00:26,280 --> 00:00:31,140 我不可能三连收藏就假装会了,必须亲自动手啊,自己写bug才行。 6 00:00:31,140 --> 00:00:37,500 所以去年我发明了一个实际可行的步骤,叫三步(三连?)技巧。 7 00:00:37,500 --> 00:00:44,820 第一,只需要学习 Unity 的非常非常基础的知识。第二,用简单的方法巩固这些练习 8 00:00:44,820 --> 00:00:51,180 然后,第三,在你前进的过程中解决剩下的bug。这绝逼有用! 9 00:00:51,180 --> 00:00:57,000 在大约一年的时间里,我从借鉴 iPhone 游戏到开发自己的平台解谜游戏 10 00:00:57,000 --> 00:01:03,480 游戏名:Magnets。我发布了一篇互动视频,已经有超过 10 万次播放 11 00:01:03,480 --> 00:01:08,580 但是这些人看完总是问!第一步你怎么做?Unity这个软件太复杂了 12 00:01:08,580 --> 00:01:11,580 你是如何学习基础知识的? 13 00:01:11,580 --> 00:01:15,360 嗯,所以说,我就列了一份清单 14 00:01:15,360 --> 00:01:19,980 不管我要做什么游戏,比如如何显示一个角色 15 00:01:19,980 --> 00:01:24,060 在屏幕上移动它们。如何生成一个东西,然后再次移除它 16 00:01:24,060 --> 00:01:28,680 然后,碰撞怎么搞,游戏结束呢,动画和声音效果等。 17 00:01:28,680 --> 00:01:33,540 然后我通过搜索学不完的教程,还有 Unity 文档, 18 00:01:33,540 --> 00:01:36,780 在谷歌上搜索深奥的关键词,并大量的测试和出现bug 19 00:01:36,780 --> 00:01:41,820 这段视频的重点就是帮你省去这些麻烦。 20 00:01:41,820 --> 00:01:46,380 这个视频是我在学习 Unity 时就希望拥有的教程。 21 00:01:46,380 --> 00:01:52,080 所以在接下来的 40 分钟里,我们将使用引擎来制作 Flappy Bird. 22 00:01:52,080 --> 00:01:57,060 不是因为我们想制作Flappy Bird,而是重做这款令人上瘾的 iPhone 游戏 23 00:01:57,060 --> 00:02:02,220 我们会学习到刚才列出的所有内容,从生成对象到游戏结束。 24 00:02:02,220 --> 00:02:08,460 本教程将涵盖从下载 Unity 到理解 UI 的每一步。 25 00:02:08,460 --> 00:02:13,680 去写你的第一行编程代码,去构建一个你可以分享的游戏 26 00:02:13,680 --> 00:02:19,080 你的朋友。然后,当教程结束时,我将分享一些具体的后续步骤,你可以 27 00:02:19,080 --> 00:02:25,920 以便自己继续学习更多内容。听起来阔以吧?那就开始吧。 28 00:02:26,880 --> 00:02:34,200 好的,让我们从网站上获取 Unity 开始。下载并安装 Unity Hub. 29 00:02:34,800 --> 00:02:39,660 然后你需要创建一个免费帐户才能真正使用它。 30 00:02:39,660 --> 00:02:45,840 完成后,你将被要求安装 Unity 编辑器,本教程中使用的是 2021.3 版本。 31 00:02:45,840 --> 00:02:49,020 都2022年了,怎么还跟原始社会一样的, 32 00:02:49,020 --> 00:02:52,380 网络慢的跟屎一样 33 00:02:52,380 --> 00:03:00,300 还没完成,在“安装”下,点击 Unity 编辑器上的 ... 图标并选择模块。 34 00:03:00,300 --> 00:03:04,980 你将看到 Microsoft Visual Studio 已被勾选,这是写代码的软件。 35 00:03:04,980 --> 00:03:10,620 所以点击继续。并安装 Visual Studio. 36 00:03:10,620 --> 00:03:15,420 在这个屏幕上,向下滚动并勾选 Unity 游戏开发,然后取消勾选 Unity Hub. 37 00:03:15,420 --> 00:03:20,340 因为我们已经有了,我们不需要创建帐户来使用 VisualStudio, 38 00:03:20,340 --> 00:03:23,580 所以跳过那个。不用加载了,我们稍后再打开。 39 00:03:23,580 --> 00:03:31,140 好了,现在都搞完了。在 Unity Hub 中,选择新项目。选择所有模板。且使用 2D Core 40 00:03:31,140 --> 00:03:35,940 这是一个空的项目,需要做些配置才比较适合 2D 游戏。 41 00:03:35,940 --> 00:03:41,220 给你的项目命名,点击创建,让我们开始游戏制作。 42 00:03:43,260 --> 00:03:48,720 第一步,我们将熟悉默认的 Unity 用户界面。 43 00:03:48,720 --> 00:03:52,500 当熟悉不同的面板时,我们将顺便显示一只鸟在屏幕上。 44 00:03:52,500 --> 00:03:58,800 好,这是 Unity 的默认屏幕布局,它分为四个面板。 45 00:03:59,880 --> 00:04:05,340 首先,这里是项目面板。这将包含我们游戏中的所有内容。 46 00:04:05,340 --> 00:04:12,660 -如精灵,声音效果,脚本,瓷砖,字体等。使用过程有些东西会被生成出来。 47 00:04:12,660 --> 00:04:17,940 不过我们也可以从电脑上的其他地方拖进一些需要文件。 48 00:04:17,940 --> 00:04:22,800 比如,我在 Photoshop 中制作了小鸟和烟斗的图片 49 00:04:22,800 --> 00:04:27,540 就像这样将它们导入到我的项目中。我建议你别光看,最好做一下,顺便三连。 50 00:04:27,540 --> 00:04:32,400 但如果你有毫无绘画天赋,去视频简介里拿这些资源吧。 51 00:04:32,400 --> 00:04:38,940 下一个面板是层次结构。这包含了当前场景中的所有内容, 52 00:04:38,940 --> 00:04:42,960 在大多数游戏中,叫关卡也可。我们将从制作鸟开始, 53 00:04:42,960 --> 00:04:50,280 因此,右键单击并选择“创建 Empty”。创建了一个空的游戏对象..是什么鬼呢? 54 00:04:50,280 --> 00:04:56,700 嗯,游戏对象本质上是一个看不见的容器。它在空间中有一个位置,一个旋转, 55 00:04:56,700 --> 00:05:03,300 和缩放。然后,你可以用组件填充该容器-以添加额外的功能。 56 00:05:03,300 --> 00:05:08,220 例如,如果我们添加一个精灵渲染器(SpriteRenderer)组件, 我们可以将鸟的图像放到游戏对象上。 57 00:05:08,220 --> 00:05:13,260 当然,我们关卡中的东西都是一个带有组件的游戏对象, 58 00:05:13,260 --> 00:05:16,440 不管是鸟啊,管道啊,甚至用户界面和摄像头,都是这种一个对象有多个组件。 59 00:05:16,440 --> 00:05:22,680 最后在第三个面板中,即检查器(Inspector)——用于查看和配置游戏对象的属性。 60 00:05:22,680 --> 00:05:28,440 所以,当我们选中了一个新的对象,空的游戏对象,我们就可以在顶部修改名称-比如:Bird 61 00:05:28,440 --> 00:05:34,860 我们可以看到 Transform 这里可以改变游戏对象的位置,旋转和缩放。 62 00:05:34,860 --> 00:05:41,520 现在,我们可以按“添加组件”、“拾取渲染”和“拾取精灵渲染器”。为了实现这一点, 63 00:05:41,520 --> 00:05:45,540 我们需要填充 sprite 字段-因此只需将鸟图像从 64 00:05:45,540 --> 00:05:49,620 项目面板把它拖进来这里,这样就有图片显示出来了! 65 00:05:49,620 --> 00:05:53,280 当然,这将在第四个也是最后一个面板中显示, 66 00:05:53,280 --> 00:05:57,240 场景视图。在这里,我们可以看到当前场景中的内容, 67 00:05:57,240 --> 00:06:01,560 如果你愿意,你可以使用这些工具来移动东西,缩放它,等等。 68 00:06:01,560 --> 00:06:06,900 这个部分有一个额外的游戏视图标签,它向我们展示了游戏的外观。 69 00:06:06,900 --> 00:06:13,440 运行时的主摄像头。此外,从这个下拉菜单中,我们可以设置分辨率或屏幕比例 70 00:06:13,440 --> 00:06:19,080 为了更好地了解它在播放时的样子,所以我将选择 1920 乘以 1080。 71 00:06:19,080 --> 00:06:23,940 哦,这只鸟占据了太多的空间。我们可以缩小规模,但实际上 72 00:06:23,940 --> 00:06:29,700 把镜头拉远。就像我之前说的,摄影机本身就是层次结构中的一个游戏对象。 73 00:06:29,700 --> 00:06:35,880 它有一个摄像头组件,里面有我们可以调整的属性。通过改变大小,我们可以缩小。 74 00:06:36,420 --> 00:06:39,420 我也要改变背景颜色,搞个可爱的。 75 00:06:39,420 --> 00:06:44,340 我们现在可以按下上面的播放按钮开始.. 76 00:06:44,340 --> 00:06:47,760 世界上最无聊的游戏诞生了。好吧,让我们把它变得更刺激一点。 77 00:06:49,320 --> 00:06:55,440 快速回顾一下。默认情况下,Unity 有四个面板。项目包含我们的所有游戏内容 78 00:06:55,440 --> 00:07:00,900 层次列出了当前级别中的所有游戏对象。检视器(Inspector)让我们看看。 79 00:07:00,900 --> 00:07:04,740 并更改这些游戏对象。我们可以在场景视图中看到级别。 80 00:07:04,740 --> 00:07:10,800 游戏对象是一个不可见的容器,我们可以用组件填充它,就像精灵渲染器一样。 81 00:07:11,580 --> 00:07:15,900 在第二步中,我们将使用更多的组件将鸟变成 82 00:07:15,900 --> 00:07:19,320 受重力影响的物理对象。然后我们要写一些编程代码, 83 00:07:19,320 --> 00:07:23,400 让小鸟在我们按下空格键的时候飞起来。 84 00:07:23,400 --> 00:07:29,760 所以让我们给我们的鸟添加另一个组件:一个2D刚体(Rigidbody2D) . 85 00:07:29,760 --> 00:07:36,600 这把我们的鸟变成了物理物体,有重力。所以当我们点击播放时,鸟就会掉下来,从屏幕上掉下来,凉凉了。 86 00:07:36,600 --> 00:07:42,279 我们还希望这只鸟能够与其他物体互动,所以让我们添加一个对撞机(Collider2D)。 87 00:07:42,279 --> 00:07:43,920 2D圆形碰撞器 CircleCollider. 88 00:07:43,920 --> 00:07:49,920 回到场景视图中,我们可以看到碰撞器有一个绿色的轮廓。对我来说有点偏离中心, 89 00:07:49,920 --> 00:07:55,260 所以我将使用偏移(offset)来移动它。一个小游戏设计技巧, 90 00:07:55,260 --> 00:08:00,720 如果我们使对撞机比图像小一点,它会让玩家容易通过管道,即使他们看起来摸到了边缘 91 00:08:00,720 --> 00:08:05,220 它给了游戏一点宽容,让玩家觉得赚到了。 92 00:08:05,220 --> 00:08:10,920 现在要添加的最后一项内容是:脚本(Script)。这本质上让我们自定义自己的组件。 93 00:08:10,920 --> 00:08:16,680 但我们必须使用编程代码自己编写。组件列表选择: New Script 94 00:08:16,680 --> 00:08:21,780 就叫它 Bird 脚本。加载后,双击脚本打开它 95 00:08:21,780 --> 00:08:27,000 这将在我们之前安装的 Visual Studio 中打开该文件。 96 00:08:27,000 --> 00:08:31,020 所以,欢迎来到编程!别慌,我保证不难,我们会慢慢来 97 00:08:31,020 --> 00:08:35,460 我们用 C Sharp 写的,那是编程语言。 98 00:08:35,460 --> 00:08:41,340 现在唯一需要担心的是这两个部分:启动和更新。 99 00:08:41,340 --> 00:08:46,620 Start 用于在启用此脚本后将立即运行一次。 100 00:08:46,620 --> 00:08:52,620 启用脚本时,Update 将持续运行,会不停的允许。 101 00:08:52,620 --> 00:08:57,360 每一行代码,每一帧。一遍又一遍。 102 00:08:58,080 --> 00:09:02,820 所以我们现在要用代码做的主要事情是-好吧,如果我们回去到 Unity 103 00:09:02,820 --> 00:09:08,340 查看组件中的这些数字和文本字段,只要我们能在Unity中编辑它们 104 00:09:08,340 --> 00:09:13,320 基本上,我们就可以编写代码来在游戏运行时更改这些属性。 105 00:09:13,320 --> 00:09:17,880 举个愚蠢的例子,我们将在一秒钟内删除它。在开始阶段, 106 00:09:17,880 --> 00:09:25,920 我们可以输入 gameObject 它指的是这里。然后输入一个 "."。你将看到出现一个列表, 107 00:09:25,920 --> 00:09:30,720 许多项目引用了 Inspector 中的内容,如 IsStatic,标记、图层和名称 108 00:09:30,720 --> 00:09:38,940 所以让我们选个 name。然后写一个等号,然后在引号中,给我们的鸟起个名字。 109 00:09:38,940 --> 00:09:44,640 最后,我们必须始终使用分号(;)来标记一行代码的结束。 110 00:09:45,433 --> 00:09:49,573 在我们回到Unity之前,我们必须保存一下脚本。 111 00:09:49,573 --> 00:09:54,360 现在,当我们运行游戏..游戏对象的名称已更改,怎么样牛不牛。 112 00:09:55,080 --> 00:09:58,560 好的,删除那个代码。那只是给傻子看的。 113 00:09:58,560 --> 00:10:03,120 不过我们学会了使用代码与游戏交互了。我们可以通过写代码来操作某个东西。 114 00:10:03,120 --> 00:10:08,280 比如游戏对象,然后名称,然后命令 115 00:10:08,280 --> 00:10:14,160 将其更改为 Bob Birdington. 会经常写这种东西。 116 00:10:14,880 --> 00:10:19,740 其实,我们真正想做的是..在刚体 2D 的组件中,在“信息”(Info)下, 117 00:10:19,740 --> 00:10:23,400 我们会看到一个灰色的速度场。 118 00:10:23,400 --> 00:10:27,120 想写一些代码来给鸟增加向上的速度,使它飞到空中。 119 00:10:27,120 --> 00:10:32,640 问题是…。最初,脚本只能与游戏对象的顶部进行操作。 120 00:10:32,640 --> 00:10:37,200 现在,这个脚本完全不知道其他组件。 121 00:10:37,200 --> 00:10:42,480 所以我们需要先解决这个问题。我们需要在这个脚本上做一个特殊的插槽 122 00:10:42,480 --> 00:10:48,360 一个 RigidBody2D-这样我们就可以与操作它并向它发送命令。这称为引用。 123 00:10:48,360 --> 00:10:52,380 我们将在这里创建引用,在类名和 Start方法。 124 00:10:52,380 --> 00:10:57,840 我们将编写 public RigidBody2D myRigidbody. 125 00:11:00,540 --> 00:11:06,480 所以我们现在有一个插槽来存储 RigidBody2D.我们有一个可以参考的名字- 126 00:11:06,480 --> 00:11:12,060 以确定我们需要的是这个特定的刚体 2D. 因为我们公开了它,我们从脚本外部访问此插槽。 127 00:11:12,060 --> 00:11:18,240 所以如果我们回到 Unity,我们会看到脚本组件现在有一个用于 RigidBody2D 的字段。 128 00:11:18,240 --> 00:11:24,780 我们可以把组件拖到那个槽里,放下。 129 00:11:24,780 --> 00:11:28,740 我们已经在脚本和刚体之间建立了一条通信线路。 130 00:11:29,340 --> 00:11:36,300 好的,回到 Visual Studio. 在 Update 中,我们可以输入 myRigidbody. 然后是点。 131 00:11:36,300 --> 00:11:40,440 现在看看我们可以调用的所有东西。比如:角阻力,重力比例,质量 132 00:11:40,440 --> 00:11:45,540 这些都是组件上的属性,我们需要的是速度(velocity)。 133 00:11:45,540 --> 00:11:51,060 我们想把它设置为一个新的数字值,所以,就像前面的名字一样,我们将写一个等号。 134 00:11:51,060 --> 00:11:57,180 现在我们在这里写的实际上是一个向量,它是两个数字,表示一个二维空间。 135 00:11:57,180 --> 00:12:02,400 在这种情况下,它用来表示鸟飞行的方向。 136 00:12:02,400 --> 00:12:08,940 我们想要鸟直接上升,所以是 (0, 1) 是可以的。我将使用 Vector2.up, 137 00:12:08,940 --> 00:12:14,400 这是(0, 1)的内置简写。为了给它更多的能量, 138 00:12:14,400 --> 00:12:19,920 我要将该矢量乘以一个数字。说,10,这应该可以把鸟送上西天。 139 00:12:19,920 --> 00:12:24,720 现在,就像我之前说的,Update 中的任何代码都会每一帧反复运行, 140 00:12:24,720 --> 00:12:31,560 所以如果我们保存脚本,并在 Unity 中点击播放..我们的鸟飞了。祈祷! 141 00:12:31,560 --> 00:12:36,360 这样不行,最好只在玩家按空格键的时候。那么 142 00:12:36,360 --> 00:12:41,040 是时候使用最基本的编程代码了:if 语句。 143 00:12:41,040 --> 00:12:43,380 if 语句就像一个门。 144 00:12:43,380 --> 00:12:47,640 你可以用大括号包围一些代码,代码的每一帧都将完全不执行 145 00:12:47,640 --> 00:12:52,020 除非,游戏满足一些特定的条件 146 00:12:52,020 --> 00:12:56,700 在门上-在这种情况下,门是打开的,代码被读取并执行。 147 00:12:56,700 --> 00:13:00,960 所以我们想说“如果玩家按下空格键,然后加上向上的速度”。 148 00:13:00,960 --> 00:13:06,300 为了做这个..我们可以写 if,然后在括号里写条件。 149 00:13:06,300 --> 00:13:12,360 这次我们不是在讨论一个组件,而是在讨论 Unity 本身——特别是它的输入系统。 150 00:13:12,360 --> 00:13:20,160 那么我们将编写输入。然后我们可以选择 GetKeyDown,并在括号中选择 KeyCode.space. 151 00:13:20,160 --> 00:13:25,200 如果在该帧上按下了空格键。然后我们输入 “== true”。 152 00:13:25,200 --> 00:13:31,020 关于等号的简要说明——我们用一个等号来使左边的东西等于右边一个值。 153 00:13:31,020 --> 00:13:35,280 我们用两个如果我们只是在检查左边的东西和右边的一样 154 00:13:35,280 --> 00:13:38,520 懂吗? 155 00:13:38,520 --> 00:13:43,740 无论如何。所以这个代码说..如果空格键刚被按下,那么..然后我们会 156 00:13:43,740 --> 00:13:48,960 将代码放在大括号里。 157 00:13:50,100 --> 00:13:54,240 所以,现在在 Update 中-游戏的每一帧都会走到门口,然后被问到嘿, 158 00:13:54,240 --> 00:13:59,340 空格键是否已按下?如果是,代码将运行,鸟将扇翅膀。如果没有, 159 00:13:59,340 --> 00:14:02,760 它将跳过花括号中的代码,并在下一帧中重试。 160 00:14:02,760 --> 00:14:08,160 所以-保存脚本,并返回到 Unity. 我们现在可以点击播放和 Tada: 161 00:14:08,160 --> 00:14:11,460 当我们按空格键的时候,鸟就会飞起来。 162 00:14:11,460 --> 00:14:17,340 我们现在已经创建了一个角色,并使用键盘控制它。这就是一个电子游戏。牛! 163 00:14:17,340 --> 00:14:23,100 然而,它就像垃圾。这翅膀扇得不对,感觉不像原作。 164 00:14:23,100 --> 00:14:31,860 所以我们可以改变这个数字。保存。回Unity,运行游戏。不太对。 165 00:14:31,860 --> 00:14:41,402 停。更改一下。保存。不过好傻啊,我们搞点更聪明的办法吧 166 00:14:36,540 --> 00:14:41,520 首先,我们要创建一个变量。让我们回到脚本的顶部和底部 167 00:14:41,520 --> 00:14:47,160 我们参考刚体,让我们制作一个名为 flapStrength 的公共插槽。 168 00:14:47,160 --> 00:14:51,960 一个浮点数(float)-是一个可以有小数位的数字。 169 00:14:51,960 --> 00:14:57,720 然后回到我们的更新代码中,我们将 Vector2.up 乘以 flapStrength,而不是 10。 170 00:14:57,720 --> 00:15:02,040 现在,回到 Unity 中,你将看到脚本组件有一个新字段:flapStrength 171 00:15:02,040 --> 00:15:06,900 我们可以随时改变这一点,让游戏手感更有感觉。 172 00:15:06,900 --> 00:15:11,340 我们甚至可以在游戏中更改它,但请注意,你更改的任何内容 173 00:15:11,340 --> 00:15:15,360 当你按下“停止”时,正在运行的游戏不会保存。这意味着你可以玩 174 00:15:15,360 --> 00:15:18,660 你可以尽情享受你的更改,而不用担心搞砸你的游戏。 175 00:15:18,660 --> 00:15:22,680 所以,如果我们弄乱了flapStrength,还有刚体重力缩放 176 00:15:22,680 --> 00:15:26,700 我们希望能得到一些感觉良好的东西。 177 00:15:26,700 --> 00:15:29,640 来回变换数字:观众老爷,这就是游戏设计! 178 00:15:30,300 --> 00:15:31,440 接下来复习一下。 179 00:15:31,440 --> 00:15:36,000 在游戏运行时,我们可以使用代码来更改组件的属性。 180 00:15:36,000 --> 00:15:40,860 默认情况下,脚本不能与游戏对象上的其他组件对话。 181 00:15:40,860 --> 00:15:45,000 你必须通过存储对该特定组件的引用来建立通信线路。 182 00:15:45,000 --> 00:15:49,860 我们在代码中创建引用,然后通过拖放在 Unity 中填充它。 183 00:15:50,400 --> 00:15:56,280 当脚本出现时,Start 中的代码只运行一次。Update 中代码每一帧连续运行, 184 00:15:56,280 --> 00:16:01,920 但是,我们可以使用 if 语句跳过一些代码,除非满足条件。 185 00:16:01,920 --> 00:16:04,740 我们可以使用公共变量来改变某些Unity 的 Inspector 中的值 186 00:16:04,740 --> 00:16:07,860 即使在游戏运行时。 187 00:16:09,480 --> 00:16:13,680 好的,Flappy Bird 的秘密是,虽然它看起来像一只鸟 188 00:16:13,680 --> 00:16:18,180 在一个充满管道的世界里飘动,但实际上是 189 00:16:18,180 --> 00:16:23,640 鸟保持完全静止,管道在屏幕上移动。所以在第三步中我们 190 00:16:23,640 --> 00:16:28,620 让管道在世界上生成,在屏幕上移动,然后出去屏幕后移除它们。 191 00:16:28,620 --> 00:16:32,280 我们将从制作我们想要生成的对象开始。 192 00:16:32,280 --> 00:16:35,100 这将是从左到右穿过屏幕的两根管道。 193 00:16:35,100 --> 00:16:40,500 让我们创建另一个名为“管道”的游戏对象。现在把它准确地移动到鸟中间, 194 00:16:40,500 --> 00:16:44,520 以获得正确的相对位置。然后我们将在这个中创建另一个对象,称为顶管。 195 00:16:44,520 --> 00:16:50,700 这是第一个和父对象和子对象层级有关的游戏对象。 196 00:16:50,700 --> 00:16:55,080 这边我们可以嵌套多个游戏对象,并通过移动父对象一次移动所有子对象 197 00:16:55,980 --> 00:17:00,000 所以让我们还是重复像制作鸟的过程一样,为管道图像添加精灵渲染器。 198 00:17:00,900 --> 00:17:06,839 并添加一个碰撞器-这次是一个 2D 的盒子碰撞器(BoxCollider)。我们不需要刚体, 199 00:17:06,839 --> 00:17:13,439 因为它不会受到物理学的影响。然后我们可以把它移到鸟的上方,但保持 X 的位置为零。 200 00:17:13,440 --> 00:17:19,020 最后,我们可以复制整个顶部管道对象。然后叫它底管。然后翻转一下 201 00:17:19,020 --> 00:17:24,420 通过将 Y 刻度更改为-1,将其颠倒。然后把它移到鸟的下面。 202 00:17:24,420 --> 00:17:31,020 如你所见,如果我们改变管道父游戏对象,两个管道都会以父对象作为轴点 203 00:17:31,020 --> 00:17:35,400 移动,缩放,并与其一起旋转。 204 00:17:35,400 --> 00:17:38,940 将脚本添加到此父对象,使其在屏幕上移动。 204 00:17:40,680 --> 00:17:46,080 我们首先为 moveSpeed 创建一个变量。如果我们在这里给它一个数字, 205 00:17:46,080 --> 00:17:50,700 它将填充此值作为 Unity 中的默认值。但我们以后可以随时改变它。 206 00:17:50,700 --> 00:17:56,100 然后,我们将在更新中编写代码来移动对象。如果我们能 207 00:17:56,100 --> 00:18:03,360 键入 transform.position.x,然后直接更改此数字,但其实不是,Boo, 208 00:18:03,360 --> 00:18:08,580 你必须一次完成整个Vector的更改。哦,这次我们要用Vector3,而不是矢量 Vector2, 209 00:18:08,580 --> 00:18:14,760 因为 position 有三个数字。尽管我们在制作 2D 游戏,但从根本上说 210 00:18:14,760 --> 00:18:19,980 Unity 仍然是一个 3D 引擎,因此它通过 Z 值来跟踪对象的深度。 211 00:18:20,580 --> 00:18:26,160 所以,我们要做的是。我们将transform.position, 然后等于。 212 00:18:26,160 --> 00:18:31,200 我们想要添加到它的位置,所以再次编写 213 00:18:31,200 --> 00:18:37,320 transform.position = transform.position + (Vector3.left * moveSpeed); 214 00:18:39,000 --> 00:18:43,500 返回 Unity,按“播放”。卧槽,太快了。 215 00:18:43,500 --> 00:18:47,520 现在,你可能认为你可以把这个 moveSpeed 变量改成 216 00:18:47,520 --> 00:18:53,280 像 0.001 这样的小数字。这会起作用——但这实际上不是问题所在。 217 00:18:53,280 --> 00:18:58,860 你可以看到,Update 中的代码只是尽可能频繁地运行。事实上,如果我们在游戏视图中查看属性, 218 00:18:58,860 --> 00:19:03,060 我们将看到游戏以每秒超过 1000 帧的速度运行。嘿, 219 00:19:03,060 --> 00:19:07,680 对不起,PlayStation 5,每秒 120 帧?呸,这和《Flappy Bird》没什么关系。 220 00:19:07,680 --> 00:19:11,880 真正的问题是游戏可能在不同的计算机上以不同的速度运行, 221 00:19:11,880 --> 00:19:16,440 我们不希望管道移动得更快或更慢,这取决于你的电脑。 222 00:19:16,440 --> 00:19:22,680 真实游戏其实也犯过这个错误——在《黑暗之魂 2》中,武器耐久度随每帧耐久度下降 223 00:19:22,680 --> 00:19:30,660 所以你的剑在每秒 60 帧时会比每秒 30 帧快两倍。那真是太棒了。 224 00:19:30,660 --> 00:19:36,420 幸运的是,这是一个很容易解决的问题。我们只需乘以 Time.deltaTime. 225 00:19:36,420 --> 00:19:41,280 这确保了无论帧速率如何,乘法都会计算出相同的值。 226 00:19:41,280 --> 00:19:44,820 不过在velocity的代码上则不需要它,因为物理运行在它自己的小时钟上, 227 00:19:44,820 --> 00:19:49,320 如果你想知道更多-关于这件事,或者任何事, 228 00:19:49,320 --> 00:19:53,580 Unity 文档是一个好地方。你会找到更多和示例代码。 229 00:19:53,580 --> 00:19:59,400 好的,现在有了这个解决方案,我们的管道可以在屏幕上平滑地移动。可爱的。 230 00:19:59,400 --> 00:20:04,560 接下来,我们要创建一个将不断生成新管道的系统。首先, 231 00:20:04,560 --> 00:20:10,740 从层次结构中获取父游戏对象并将其拖动到项目中。这将创建一个预制游戏对象(Prefab)。 232 00:20:10,740 --> 00:20:16,980 或叫预制。这就像一个游戏对象的蓝图,我们通过这个蓝图 233 00:20:16,980 --> 00:20:22,500 创建新的整个游戏对象的另外版本,会包括所有子对象、组件和属性。 234 00:20:22,500 --> 00:20:26,460 在我们继续之前,我们现在可以删除层次结构中的原始内容。拜拜。 235 00:20:26,460 --> 00:20:29,460 让我们创建一个名为“管道产生成器”的新游戏对象。 236 00:20:30,720 --> 00:20:36,240 我们会把它放在摄像机的右边。我们会为它做一个脚本。 237 00:20:36,240 --> 00:20:41,820 脚本将每隔几秒生成管道预置的新版本。因为管道已经 238 00:20:41,820 --> 00:20:46,800 有向左移动的代码,管道将在生成后立即自动在屏幕上移动。 239 00:20:47,400 --> 00:20:51,360 我们将编写一些代码来生成我们刚刚制作的预置。那么 240 00:20:51,360 --> 00:20:54,276 我们将从引用预制件开始。 241 00:20:54,276 --> 00:20:58,680 在这里,我们将输入公共游戏对象管道。 242 00:20:58,680 --> 00:21:03,840 然后在 Unity 中,我们将使用相同的拖放方法来填充插槽,但这一次, 243 00:21:03,840 --> 00:21:07,920 我们将从“项目”面板中拖动预置,而不是组件。 244 00:21:07,920 --> 00:21:11,580 现在,Unity 有一个很好的内置生成对象的方法。 245 00:21:11,580 --> 00:21:17,880 新游戏对象。我们将输入 Instantiate,然后打开括号。 246 00:21:17,880 --> 00:21:22,980 在这里,需要填一些额外的细节。我们实际上可以通过翻阅这些找到不同的,怎么说呢,食谱吧? 247 00:21:22,980 --> 00:21:28,500 我猜?这个看起来不错,它将在指定的位置和旋转创建一个对象。 248 00:21:28,500 --> 00:21:32,760 因此,对于游戏对象,我们可以输入 pipe. 对于位置 249 00:21:32,760 --> 00:21:37,560 我们可以只输入 transform.position 来获取对象的位置。 250 00:21:37,560 --> 00:21:42,180 意思是说,会把管道生成在生成器所在的位置。 251 00:21:42,180 --> 00:21:46,800 并且对于旋转,让我们只使用 transform.rotation 和生成器也相同。 252 00:21:48,300 --> 00:21:52,740 让我们运行它,天啊,这不是我们想要的。生成效果很好, 253 00:21:52,740 --> 00:21:55,560 但他们每一帧都在出现,我们希望他们出现。 254 00:21:55,560 --> 00:21:59,760 在一个我们可以控制的好时间里。所以,回到 Visual Studio. 255 00:21:59,760 --> 00:22:05,100 我们现在要做的是写一些代码来制作一个计时器。这将计入 256 00:22:05,100 --> 00:22:10,140 指定的秒数,运行一些代码,然后再次开始计数。要做到这一点, 257 00:22:10,140 --> 00:22:14,520 我们需要做几个变量。生成器应该指它应该有多少秒生产一个管道 258 00:22:14,520 --> 00:22:19,560 然后计时器是计数的数字。我们可以做这个。 259 00:22:19,560 --> 00:22:22,980 一个私人的,因为我们不会在编辑器或其他任何地方更改它。 260 00:22:22,980 --> 00:22:29,400 在Update中,我们将执行另一个 if 语句。此时,如果计时器小于 spawnRate, 261 00:22:29,400 --> 00:22:34,980 然后我们让计时器加一。因此,我们将采用当前的计时器, 262 00:22:34,980 --> 00:22:39,780 并将 time.deltatime 添加到其中。这将创建一个计数每一帧的数字, 263 00:22:39,780 --> 00:22:42,600 无论你的计算机的帧速率是多少,它的工作方式都是相同的。 264 00:22:42,600 --> 00:22:48,480 实际上,我们可以通过将其更改为 += 来缩短它,但是,不要觉得你需要将代码设置为尽可能短 265 00:22:48,480 --> 00:22:53,040 只是为了避免大佬们不屑的评论。 266 00:22:53,040 --> 00:22:58,440 如果:"timer = timer + xxx" 更容易阅读和掌握,那么这绝对没问题。 267 00:22:58,440 --> 00:23:01,740 你熟练了,可以随便使用。 268 00:23:01,740 --> 00:23:05,220 现在,在我说 if 语句之前,它就像一扇门。 269 00:23:05,220 --> 00:23:09,540 我们可以在它的旁边添加另一个门,否则。这意味着, 270 00:23:09,540 --> 00:23:14,520 如果不满足条件,则跳过该代码,并在 else 中执行该代码。 271 00:23:14,520 --> 00:23:22,560 因此,我们将在这里放入生成代码,并将计时器重置为零。所以现在,每一帧, 272 00:23:22,560 --> 00:23:28,500 询问计时器是否小于生成率。如果是的话,那就把计时器算起来。如果不是 273 00:23:28,500 --> 00:23:33,420 即计时器实际上已经达到或超过了生成速率,然后生成一个管道并再次启动计时器。 274 00:23:33,420 --> 00:23:38,820 把这个放在 Unity 里-很好。我对此很满意。唯一的问题是。 275 00:23:38,820 --> 00:23:44,100 我们必须等待第一个管道生成的时间很长。如果这个马上出来就好了,对吧? 276 00:23:44,100 --> 00:23:49,560 现在,我们可以将代码复制并粘贴到 Start 中,这样它就会在 Start 中发生一次。 277 00:23:49,560 --> 00:23:55,320 然后在更新中一遍又一遍地发生。但这是个坏主意。你通常应该试着 278 00:23:55,320 --> 00:24:00,900 避免在多个位置使用相同甚至相似的代码。 279 00:24:00,900 --> 00:24:06,120 如果我们想要改变生产的工作方式?我们必须各处寻找并修改它,不是很好。 280 00:24:06,120 --> 00:24:10,260 相反,我们可以将代码放在一个新函数中,然后运行它 281 00:24:10,260 --> 00:24:15,180 所以在这里,下面的Update,但在最后的花括号上面-我们将 282 00:24:15,180 --> 00:24:21,420 创建一个名为 void SpawnPipe()的函数。然后将实例化代码剪切并粘贴到其中。 283 00:24:21,420 --> 00:24:27,720 现在,我们只需在 Update 和 Start 中编写带有空括号的 SpawnPipe.这将运行 284 00:24:27,720 --> 00:24:32,340 执行这些行时该函数中的所有代码。做完这件事, 285 00:24:32,340 --> 00:24:37,800 它会让游戏一开始就有一个管道,并且每次计时器达到最大值时都会产生新的管道。牛了。 286 00:24:38,760 --> 00:24:44,700 然而,这是一个相当无聊的游戏,对不对?管子总是从中间出来。我们想要他们 287 00:24:44,700 --> 00:24:49,080 以随机的高度出现。请记住,当我们编写实例化代码时, 288 00:24:49,080 --> 00:24:52,980 我们必须选择一个物体出现的位置。我们将更改该值。 289 00:24:52,980 --> 00:24:58,140 现在,管道总是在与生产器相同的位置生产。我们想要 X 值 290 00:24:58,140 --> 00:25:03,960 是一样的..但对于 Y,我们想要在生产器上方或下方的某个位置随机选择一个点。 291 00:25:03,960 --> 00:25:08,520 因此,让我们为高度偏移创建一个公共变量,可能是 10。 292 00:25:08,520 --> 00:25:14,220 然后我们将创建一个名为 lowestPoint 的浮点数。因为我们在函数, 293 00:25:14,220 --> 00:25:19,800 而不是在脚本的顶部,这意味着它只能在函数中使用。但是, 294 00:25:19,800 --> 00:25:22,200 这也意味着我们可以通过计算来设置它。 295 00:25:22,200 --> 00:25:29,820 因此,我们将执行 = transform.Position.y - heightOffset. 然后我们再做一个。 296 00:25:29,820 --> 00:25:36,600 一个是最高点,但这次是加上高度偏移。这样我们就得到了这两个数字。 297 00:25:37,560 --> 00:25:41,400 然后,我们将替换实例化代码中的 transform.position. 298 00:25:41,400 --> 00:25:47,400 我们要写一个新的Vector3,每当我们指定我们自己的数字时,我们必须用 Vector3 299 00:25:47,400 --> 00:25:53,700 然后在括号中,我们将 x、y 和 Z 值指定为三个不同的浮点数。对于 X, 300 00:25:53,700 --> 00:25:59,160 我们希望这与生成器相同,所以我们将执行 transform.position.x. 301 00:25:59,160 --> 00:26:05,160 但是对于 y,我们可以做随机范围。在括号里,我们可以提供一个最小值和最大值。 302 00:26:05,160 --> 00:26:12,300 从中挑选。这是最低点和最高点。然后用 0 表示 Z,并关闭括号。 303 00:26:14,220 --> 00:26:19,560 回到Unity..好的!管道将在这两个数字之间的任何位置生成。 304 00:26:19,560 --> 00:26:24,780 哦最后一件事。每次这些管道生产时,它们都会出现并向左移动..永远。 305 00:26:24,780 --> 00:26:30,120 这不是很好的方式——他们在屏幕外什么也不做, 306 00:26:30,120 --> 00:26:35,100 然而,它们仍然在内存中,每一帧都在运行代码。如果太多,它们就会 307 00:26:35,100 --> 00:26:40,140 把你的电脑内存全吃掉,然后崩溃。所以让我们解决这个问题。 308 00:26:40,140 --> 00:26:45,060 现在,我们可以制作一个计时器,并在几秒钟后删除管道。但是相反, 309 00:26:45,060 --> 00:26:50,700 我们将检查管道的 X 位置,如果它超过了某个点,则将其删除。我们会借的 310 00:26:50,700 --> 00:26:58,260 这只鸟找出了屏幕左边的 X 坐标。看起来大约是零下 45 度 311 00:26:58,260 --> 00:27:06,120 在管道移动脚本中,我们将为死区添加一个浮动。-45. 然后是一个简单的 if 语句-if 312 00:27:06,120 --> 00:27:13,020 如果 Transform.Position.X 小于 DeadZone,则销毁保存此脚本的游戏对象。 313 00:27:15,720 --> 00:27:18,840 在 Unity 中运行它,然后,砰,他们死了。 314 00:27:18,840 --> 00:27:24,420 让我们再做一件事,像个学生一样。就在销毁之前, 315 00:27:24,420 --> 00:27:32,880 让我们编写 Debug.Log,并在括号中删除管道。然后,回到 Unity 中, 316 00:27:32,880 --> 00:27:39,420 你将看到另一个面板我在 UI 演示过程中跳过了-它是 Project 旁边的一个选项卡,称为 Console. 317 00:27:39,420 --> 00:27:44,820 然后当我们运行游戏..每次删除管道时,我们的消息都会发送到控制台。 318 00:27:44,820 --> 00:27:50,280 这是一种非常有用的调试代码的方法,因为我们可以准确地找出代码的目的。 319 00:27:51,900 --> 00:27:52,800 回顾时间! 320 00:27:52,800 --> 00:27:55,560 游戏对象可以转换为预制件, 321 00:27:55,560 --> 00:27:58,980 通过将它们从层次结构中拖放到项目中。 322 00:27:58,980 --> 00:28:04,020 然后你可以把它们拖到场景中, 我在我的益智游戏中使用预制件来创建关卡。 323 00:28:04,020 --> 00:28:08,940 比如说。或者你可以在游戏中创建一个生产器来实例化新的生产器。 324 00:28:08,940 --> 00:28:12,660 计时器是一种使代码在特定时间间隔内发生的好方法, 325 00:28:12,660 --> 00:28:17,400 但始终使用 time.deltatime 来保持不同计算机之间的一致性。 326 00:28:17,400 --> 00:28:21,540 if 语句可以有一个 else 的门,以使代码在条件未满足。 327 00:28:21,540 --> 00:28:26,520 你也可以有其他的 if,来制作更复杂的门。 328 00:28:26,520 --> 00:28:30,960 如果不再需要游戏对象,你应该尝试删除它们,以释放内存。 329 00:28:32,400 --> 00:28:35,820 好的,我们下一步是记录玩家的得分, 330 00:28:35,820 --> 00:28:39,360 并在用户界面上将其显示给玩家。然后, 331 00:28:39,360 --> 00:28:43,380 我们希望每次鸟穿过管道时,分数都能上升一分。 332 00:28:43,380 --> 00:28:47,460 因此,请记住,游戏对象不一定是游戏中的物理对象 333 00:28:47,460 --> 00:28:52,560 世界就像一个角色或一个敌人-它可以是一个完全看不见的管理者 334 00:28:52,560 --> 00:28:57,540 这只是跟踪关键数据,如HP、时间或分数。然后, 335 00:28:57,540 --> 00:29:01,260 我们可以使用用户界面让玩家看到这些信息。 336 00:29:01,260 --> 00:29:06,600 所以让我们开始制作 UI。像其他东西一样,在层级面板,它是一个游戏对象。 337 00:29:06,600 --> 00:29:11,520 这一次,向下到 UI 并选择文本,它可能在Legacy 338 00:29:11,520 --> 00:29:16,680 我们需要将场景视图放大到非常远的地方,才能真正看到 UI. 339 00:29:16,680 --> 00:29:21,660 为了确保 UI 在每个设备上看起来都一样,我们将选择这个新的画布游戏对象 340 00:29:21,660 --> 00:29:28,260 将画布缩放器组件的 UI 比例设置为随屏幕大小缩放,并选择合理的 341 00:29:28,260 --> 00:29:34,200 参考分辨率-我将再次使用 1080p.然后,我们可以移动文本。你会注意到 342 00:29:34,200 --> 00:29:40,140 该 UI 具有 Rect Transform,而不是正常 Transform。最重要的是要注意 343 00:29:40,140 --> 00:29:44,700 你真的先不要使用 scale改变大小,而是用宽度和高度。 344 00:29:46,620 --> 00:29:49,080 然后,我将增加字体大小并设置默认值。 345 00:29:49,080 --> 00:29:53,280 文本设置 0. 然后检查它在游戏视图上看起来都很好。 346 00:29:53,280 --> 00:29:56,640 好的,现在我们要做一个脚本来存储玩家的分数, 347 00:29:56,640 --> 00:29:59,820 并将 UI 上的数字更改为该分数。 348 00:29:59,820 --> 00:30:02,940 我们将制作一个名为逻辑管理器(Logic Manager)的游戏对象。 349 00:30:03,960 --> 00:30:08,820 我们会给它一个脚本。此脚本将跟踪高级信息 350 00:30:08,820 --> 00:30:13,860 比如玩家得分。它将具有我们可以运行的各种基本功能。 351 00:30:13,860 --> 00:30:18,180 因此,我们将删除 “Start”和“Update”,我们在此脚本中不需要它们。我们可以随时添加 352 00:30:18,180 --> 00:30:22,740 如果我们后面还要,可以加回来。我们想为玩家的分数存储一个数字。这一次, 353 00:30:22,740 --> 00:30:27,600 我们不想要浮点数,因为我们只想要整数。所以让我们做一个整数, 354 00:30:27,600 --> 00:30:30,360 这是个整数。没有小数位。 355 00:30:30,360 --> 00:30:34,980 因为我们想要更新我们刚刚创建的 UI 文本,所以我们将一如既往地 356 00:30:34,980 --> 00:30:40,680 必须做一个引用。但是,好像没找到合适的。 357 00:30:40,680 --> 00:30:46,320 啊,好吧。默认情况下,脚本只加载Unity所需的基础内容。 358 00:30:46,320 --> 00:30:52,500 但如果我们在顶部输入 UnityEngine.UI;,我们现在可以 359 00:30:52,500 --> 00:30:58,680 访问更多的功能-在这种情况下,是用户界面的东西。现在我们可以参考文本。 360 00:30:59,280 --> 00:31:04,680 我们需要将文本组件拖回 Unity 中的这个字段。因为我们正在引用一个组件 361 00:31:04,680 --> 00:31:09,540 在另一个游戏对象(UI 上的文本)上,最好的方法就是 362 00:31:09,540 --> 00:31:16,020 拖动整个游戏对象进入我们的插槽。这将自动为我们找到文本组件。方便。 363 00:31:16,020 --> 00:31:20,460 所以现在我们要做一个函数。我们称之为 AddScore. 364 00:31:20,460 --> 00:31:25,009 还有因为我们将从其他脚本运行此函数,并将其设置为 public void. 365 00:31:25,740 --> 00:31:30,840 这个函数需要做两件事。在玩家的分数上加一。很容易, 366 00:31:30,840 --> 00:31:39,240 我们现在知道该怎么做了。并将 UI 上的文本更改为此数字。哦,文本框是 367 00:31:39,240 --> 00:31:45,720 寻找一个字符串-字符序列-我们的分数是一个整数。他们看起来一模一样 368 00:31:45,720 --> 00:31:51,720 对我们人类来说,但程序是挑剔的。请注意,通过将.ToString()添加到游戏分数中,很容易修复。 369 00:31:52,800 --> 00:31:56,580 为了确保这一点,让我们自己来运行这个函数。 370 00:31:56,580 --> 00:32:02,820 从Unity本身。我们需要做的就是在函数上面写上 ContextMenu 和一个名称。 371 00:32:05,340 --> 00:32:10,140 现在,在 Unity 中,当游戏运行时,点击这个脚本上的小点并选择函数。 372 00:32:10,800 --> 00:32:14,460 好看的!这类东西在测试中非常方便。 373 00:32:15,060 --> 00:32:18,660 好的,现在我们知道函数运行了,我们特别想运行它。 374 00:32:18,660 --> 00:32:23,220 当鸟在管道之间穿行时。而做到这一点的方法是碰撞。 375 00:32:23,220 --> 00:32:28,380 现在,如果两个物体有碰撞器,它们会互相撞击-事实上,在我们的游戏中, 376 00:32:28,380 --> 00:32:33,060 这只鸟已经撞进了管道,因为我们在两个管道上都增加了对撞机。然而-你 377 00:32:33,060 --> 00:32:37,740 也可以有不可见的碰撞器,称为触发器(Trigger)。它们不会造成真正的碰撞, 378 00:32:37,740 --> 00:32:42,000 但是它们确实让你知道两个对象已经接触-并且你可以在此时运行代码。 379 00:32:42,000 --> 00:32:45,180 所以我们要在管道之间放一个触发器, 380 00:32:45,180 --> 00:32:50,700 所以我们知道这只鸟穿过了它们。然后在那个时候,我们将运行 addScore. 381 00:32:50,700 --> 00:32:55,920 让我们打开管道的预置。我们将制作另一个名为“middle”的游戏对象 382 00:32:55,920 --> 00:33:00,900 它需要一个盒子对撞机。让我们把它做成这种形状。这次我们要 383 00:33:00,900 --> 00:33:06,420 在 Is Trigger 框中打勾。最后,让我们为这个新的中间游戏对象添加一个脚本。 384 00:33:06,420 --> 00:33:13,920 在 Update 下面,输入 OnTrig,按Tab,自动将帮助我们输入 OnTriggerEnter2D. 385 00:33:13,920 --> 00:33:18,960 此函数意思是,任何内容进入碰撞器的第一次时都将运行 386 00:33:18,960 --> 00:33:25,620 还有 OnTriggerExit 和 OnTriggerStay,供将来参考。 387 00:33:25,620 --> 00:33:30,060 我们要运行之前编写的 addScore 函数..除了。啊哈。再一次, 388 00:33:30,060 --> 00:33:34,440 这个脚本不知道游戏中的任何其他脚本,直到我们引用它。 389 00:33:34,440 --> 00:33:41,340 所以我们可以编写公共逻辑脚本逻辑。但回到 Unity,你很快就会意识到 390 00:33:41,340 --> 00:33:45,840 你不能把脚本拖到这个槽中。你不能把它从项目中拖出来 391 00:33:45,840 --> 00:33:51,060 我们只能与游戏对象上的脚本实例来操作。 392 00:33:51,060 --> 00:33:56,880 但我们也无法从场景拖动到预置中。这是因为场景中还不存在管道, 393 00:33:56,880 --> 00:34:01,320 只有当游戏运行时,生产器开始制作管道时,它才会存在。 394 00:34:01,320 --> 00:34:05,520 因此,我们需要使用代码来填充此引用。 395 00:34:05,520 --> 00:34:08,340 这需要在管道首次生成时进行。 396 00:34:08,340 --> 00:34:12,719 为此,我们需要帮助代码找到逻辑脚本。 397 00:34:12,719 --> 00:34:16,799 要做到这一点,以游戏逻辑对象为例,查看检查器的顶部: 398 00:34:16,800 --> 00:34:23,820 你会看到标签。从下拉列表中选择“添加标记”。创建一个新的标签, 399 00:34:23,820 --> 00:34:30,060 并确保你回到游戏对象并实际设置这个新标签。你不会忘记的 400 00:34:30,060 --> 00:34:34,679 在你的Unity职业生涯中,这样做大约 8000 次,所以期待吧。 401 00:34:34,679 --> 00:34:37,379 现在,回到 PipeMiddleScript 中, 402 00:34:37,380 --> 00:34:46,739 在 Start 下,我们可以写 logic = Gameobject.FindGameObjectWithTag("logic")。 403 00:34:46,739 --> 00:34:50,819 这将在层次结构中寻找第一个带有logic标签的游戏对象, 404 00:34:50,820 --> 00:34:54,420 在我们的例子中,场景中永远只有一个, 405 00:34:54,420 --> 00:34:57,900 所以我们知道它总会找到合适的——但一定要注意这一点。 406 00:34:57,900 --> 00:35:02,280 然后我们可以添加.GetComponent(); 407 00:35:03,480 --> 00:35:09,300 所以,一旦一个新的管道产生,它就会通过层次结构来寻找一个有logic标签的游戏对象。 408 00:35:09,300 --> 00:35:13,800 然后,它将查找该对象的组件以找到脚本LogicScript。 409 00:35:13,800 --> 00:35:19,020 如果它找到一个,它会把它放在我们的参考槽中。 410 00:35:19,020 --> 00:35:22,740 它完成了与拖放组件到插槽完全相同的操作。 411 00:35:22,740 --> 00:35:28,200 当然,区别是通过代码在运行时做到的。 412 00:35:28,860 --> 00:35:33,960 所以现在,管道的中间脚本可以找到逻辑脚本并与之操作。 413 00:35:35,400 --> 00:35:41,340 如果我们编写 logic.addScore,它将运行该代码。回到Unity, 414 00:35:41,340 --> 00:35:47,340 点击播放,如果我们一切都做得很好,当我们通过管道之间时,分数将上升一分。 415 00:35:47,340 --> 00:35:52,860 哦,不过为了后面更好的做其他事情,让我们确保通过管道的确实是这只鸟。 416 00:35:52,860 --> 00:35:57,660 我们将通过把鸟放在一个层(Layer)上,并设置碰撞是否有效。 417 00:35:57,660 --> 00:36:03,900 转到鸟的游戏对象,这一次,而不是标签,我们将改变鸟的层 418 00:36:03,900 --> 00:36:10,620 添加一个新的,记得回来设置一下,并记下数字。 419 00:36:10,620 --> 00:36:15,480 现在,在管道的中间脚本上,我们可以在 addScore 周围添加一个 if 语句, 420 00:36:15,480 --> 00:36:20,400 并检查刚刚发生的碰撞是否与属于 Bird 层上的游戏对象。 421 00:36:22,560 --> 00:36:27,494 当我们谈到这个话题的时候,还有一点需要做的。回到Logic Script。 422 00:36:27,494 --> 00:36:33,420 让我们以 addScore 函数为例,在这些空括号中, 423 00:36:33,420 --> 00:36:38,520 我们将写下 int scoreToAdd. 然后,我们将添加 soreToAdd,而不是添加一个 1。 424 00:36:39,240 --> 00:36:45,360 然后,在管道中间脚本中,我们可以在 addScore 后面的括号中写入 1. 425 00:36:45,360 --> 00:36:50,160 对的,现在,这和我们以前做的事情完全一样。但是,你肯定能猜到, 426 00:36:50,160 --> 00:36:54,900 稍后,你可以在游戏中添加一些其他目标,例如,在你的得分上添加 5。 427 00:36:54,900 --> 00:36:58,980 这使我们可以使函数更通用,因为它可以使用 428 00:36:58,980 --> 00:37:03,120 以不同的方式,从不同的地方。我认为成为一名优秀程序员的一部分, 429 00:37:03,120 --> 00:37:07,440 是让东西变得不那么死板,并为未来的想法保持开放。 430 00:37:07,440 --> 00:37:12,660 这使得对设计进行迭代变得更加容易和快速。 431 00:37:12,660 --> 00:37:14,040 对的!回顾! 432 00:37:14,040 --> 00:37:17,880 UI 只是另一个游戏对象,但如果我们想在这些组件中引用 433 00:37:17,880 --> 00:37:22,920 我们需要将 UnityEngine.UI 添加到脚本的顶部。 434 00:37:22,920 --> 00:37:25,680 游戏对象可以是完全看不见的东西, 435 00:37:25,680 --> 00:37:29,760 只是在那里管理规则、逻辑、分数等等。 436 00:37:29,760 --> 00:37:34,080 如果我们想在一个游戏对象不在场景中的时候引用一个组件, 437 00:37:34,080 --> 00:37:36,660 我们需要在运行时找到该组件。 438 00:37:36,660 --> 00:37:42,300 一种方法是使用Tag、FindGameObject 和 GetComponent. 439 00:37:42,300 --> 00:37:46,740 只要有引用,就可以从另一个脚本运行公共函数。 440 00:37:46,740 --> 00:37:50,940 对那个脚本。我们甚至可以在该函数运行时传入变量。 441 00:37:50,940 --> 00:37:56,580 当两个物体接触时,碰撞和触发可以用来使事情发生。 442 00:37:56,580 --> 00:37:59,400 说到碰撞,让我们进入下一步…… 443 00:38:01,020 --> 00:38:06,900 最后一步是添加失败状态。当鸟撞到管道时,游戏就结束了。 444 00:38:06,900 --> 00:38:11,340 我们将通过在屏幕上制作一个游戏结束界面来做到这一点,并让它在鸟碰到管道坠毁时出现。 445 00:38:11,340 --> 00:38:16,320 游戏结束界面将有一个按钮,我们可以用它来重置游戏。 446 00:38:16,980 --> 00:38:21,060 首先,让我们在屏幕上制作游戏结束界面。在画布游戏对象上, 447 00:38:21,060 --> 00:38:28,440 添加一个新的空的游戏结束界面。然后,在该父项中,添加“游戏结束”文本。 448 00:38:30,960 --> 00:38:36,900 还有一个按钮-也在 Legacy 下。调整它的大小。 449 00:38:36,900 --> 00:38:41,580 和改变按钮上的文本,该文本可以作为按钮本身的子项找到。 450 00:38:43,440 --> 00:38:48,000 所以回到按钮游戏对象上,在按钮组件上,你会看到 451 00:38:48,000 --> 00:38:53,520 点击时显示的这一点。这是一个事件,它允许我们调用 452 00:38:53,520 --> 00:38:58,680 游戏对象上的公共函数。所以让我们做一个重启关卡的函数。 453 00:38:59,520 --> 00:39:02,100 我们可以将这段代码放在逻辑脚本中, 454 00:39:02,100 --> 00:39:07,080 在我们的 addScore 函数下面。如果你愿意,你可以做一个单独的脚本, 455 00:39:07,080 --> 00:39:12,060 但我觉得这很好。让我们创建另一个名为 RestartGame 的公共函数, 456 00:39:12,060 --> 00:39:17,580 在这里,我们将编写代码来重新启动场景。就像以前的用户界面一样管理场景 457 00:39:17,580 --> 00:39:22,560 然后,我们需要在顶部添加一行-这一次,使用 UnityEngine.SceneManagment. 458 00:39:23,940 --> 00:39:30,180 现在,在我们的函数中,我们将调用 SceneManager.LoadScene. 459 00:39:30,180 --> 00:39:35,460 这需要一个场景的名称。其实就是场景的文件名。但是因为我们想要当前的场景,我们 460 00:39:35,460 --> 00:39:42,720 可以简单地输入 SceneManager.getActiveScene,方括号,.name。 461 00:39:44,580 --> 00:39:48,000 现在回到 Unity 中,向此按钮添加一个事件。 462 00:39:48,960 --> 00:39:53,340 然后拖入逻辑游戏对象。并找到 RestartGame 函数。 463 00:39:57,300 --> 00:40:02,580 简单测试一下..。好的,每次我们按下按钮,游戏就重新开始。 464 00:40:02,580 --> 00:40:07,560 现在很明显,我们不想让它一直出现在屏幕上——只是在我们失败的时候。那么, 465 00:40:07,560 --> 00:40:12,000 我们可以将整个游戏置于屏幕游戏对象之上,并使用此复选标记将其禁用。 466 00:40:13,380 --> 00:40:16,260 那我们就让它在鸟撞进管道的时候出现。 467 00:40:16,260 --> 00:40:22,620 我们先写函数。同样,在逻辑脚本中,让我们为 GameOver 创建一个公共函数。 468 00:40:22,620 --> 00:40:27,750 我们需要对屏幕上的游戏对象进行引用。 469 00:40:27,900 --> 00:40:30,116 并用Unity来填充它。 470 00:40:31,467 --> 00:40:38,611 然后我们可以简单地在这个函数中输入 gameoverscreen.setActive true. 471 00:40:38,820 --> 00:40:42,300 所以我们想让这个函数在鸟撞进管道时触发。 472 00:40:42,300 --> 00:40:48,180 回到 Bird 脚本,让我们重用之前的代码来访问逻辑脚本。 473 00:40:48,180 --> 00:40:52,680 是的,我们可以在 Unity 中拖放引用,但是, 474 00:40:52,680 --> 00:40:57,720 我们现在已经编写了这段代码。然后我们将对触发器代码执行类似的操作,但这次 475 00:40:57,720 --> 00:41:03,360 我们将使用 OnCollisionEnter2D,因为管道是实体对象,而不是设置为触发器。 476 00:41:03,360 --> 00:41:10,277 当冲突发生时,使用 Logic.GameOver 触发游戏结束脚本。 477 00:41:10,277 --> 00:41:16,440 回到Unity..这是可行的,但我们仍然可以在屏幕上玩游戏。不是很理想。 478 00:41:16,440 --> 00:41:21,420 我已经讨论了几个关键的变量类型。浮子 479 00:41:21,420 --> 00:41:28,380 整数是数字。字符串通常用于文本。另一个重要的是布尔, 480 00:41:28,380 --> 00:41:34,320 布尔值的缩写。这是一个非常简单的类型,要么为真,要么为假。在上方, 481 00:41:34,320 --> 00:41:38,340 或者关掉。是,或者不是。这是一种简单地检查或更改某物状态的好方法。 482 00:41:38,940 --> 00:41:44,160 所以让我们有一个名为 birdIsLive 的布尔,并确保它开始为真。 483 00:41:44,160 --> 00:41:49,080 然后,当碰撞发生时,我们将 birdIsLive 设置为 false. 484 00:41:49,080 --> 00:41:54,480 最后,我们将在第一个 if 语句中添加一个额外的条件。 485 00:41:54,480 --> 00:42:01,200 比如说,如果空格键刚刚被按下……由两个“”符号组成。而 birdIsLive 是等于真。 486 00:42:01,200 --> 00:42:06,360 实际上,我们不需要加上这个 == true 的东西。是可以的。 487 00:42:06,360 --> 00:42:11,400 没有它也一样。但是,再一次,这取决于你-也许写出完整的代码更容易阅读。 488 00:42:11,400 --> 00:42:17,040 无论如何,现在,如果鸟死了,它就不会拍打翅膀,这对我来说似乎很合乎逻辑。 489 00:42:17,040 --> 00:42:24,360 最后要做的是构建游戏。这真的很容易。选择文件、生成设置和生成。选择一个文件夹 490 00:42:24,360 --> 00:42:31,500 你的硬盘。让Unity发挥作用。然后你可以打开这个文件来玩你的游戏!令人惊讶的。 491 00:42:31,500 --> 00:42:35,400 在很短的时间内,我们制作了一个非常实用的游戏。 492 00:42:35,400 --> 00:42:39,960 更重要的是,我们学到了很多关于Unity的基本知识。 493 00:42:39,960 --> 00:42:43,980 我们创造了一个角色,它会根据我们的输入而移动。 494 00:42:43,980 --> 00:42:48,660 我们有在计时器上生成新对象。我们已经创建了一个显示分数的 UI, 495 00:42:48,660 --> 00:42:53,700 并在条件满足时使分数打勾。我们有能力结束一场比赛, 496 00:42:53,700 --> 00:42:55,573 然后重新开始。 497 00:42:55,573 --> 00:43:00,060 现在,我应该注意到,有不同的,也许更好的方法来做几乎所有的事情。 498 00:43:00,060 --> 00:43:05,220 在本教程中。例如,我使用了 Unity 的传统方法来检查输入 499 00:43:05,220 --> 00:43:10,440 Unity他们后来开发了一个非常非常好的输入系统。但要复杂得多 500 00:43:10,440 --> 00:43:15,660 使用-所以这个简单的方法现在很好,你可以稍后再看看新的输入系统。 501 00:43:15,660 --> 00:43:21,420 当你感觉更自信的时候。对我来说就是这样。还有 TextMeshPro, 502 00:43:21,420 --> 00:43:27,559 已经取代了旧的Legacy用户界面系统-所以你会想要升级,在某些时候。 503 00:43:27,559 --> 00:43:31,674 无论如何,这些都是对制作各种游戏有用的课程。 504 00:43:31,674 --> 00:43:37,260 但是..游戏还没有完全结束。还有一些事情要解决。尽管, 505 00:43:37,260 --> 00:43:41,160 我不想告诉你怎么做每件事。所以我会给你一些建议完成游戏, 506 00:43:41,160 --> 00:43:43,860 我想让你自己试着搞定这些。 507 00:43:43,860 --> 00:43:48,360 首先,如果小鸟从屏幕上消失,我们也需要让游戏结束。 508 00:43:48,360 --> 00:43:51,900 这应该不会太难。还有一个错误,分数会一直上升, 509 00:43:51,900 --> 00:43:55,020 即使在比赛结束后。也试着解决这个问题。 510 00:43:55,020 --> 00:44:00,300 我们也想要声音效果。我想让你在逻辑管理器中添加一个音频源组件。 511 00:44:00,300 --> 00:44:05,700 用音效文件填充它。在脚本上引用它。当分数上升时让它播放。 512 00:44:05,700 --> 00:44:11,220 然后,我想让你玩一下粒子系统,让云出现在游戏中。 513 00:44:11,220 --> 00:44:15,600 接下来,打开动画窗口,为小鸟添加一些拍动的翅膀。 514 00:44:15,600 --> 00:44:19,320 然后我想让你添加另一个场景来制作一个标题菜单界面, 515 00:44:19,320 --> 00:44:22,800 所以游戏不会立即开始行动。这里有一个提示: 516 00:44:22,800 --> 00:44:25,740 你需要将此新场景添加到“构建设置”(Build Settings)窗口。 517 00:44:25,740 --> 00:44:30,540 最后,如果你想要一个真正的挑战-使用 PlayerPrefs 518 00:44:30,540 --> 00:44:35,400 将玩家的高分保存到硬盘上,并在用户界面上绘制。 519 00:44:35,400 --> 00:44:40,620 对于其中的每一个,你可能会想要谷歌相关的术语,阅读 Unity 文档, 520 00:44:40,620 --> 00:44:45,120 观看一些快速教程视频,或在下面的评论中寻求帮助。 521 00:44:45,120 --> 00:44:48,840 接下来,你可以扩展一下《Flappy Bird》。发挥创意 522 00:44:48,840 --> 00:44:53,280 并添加原始 iPhone 游戏中没有的想法或设计。 523 00:44:53,280 --> 00:44:58,320 举个例子,我给了它发射导弹的能力, 524 00:44:58,320 --> 00:45:03,480 然后我在管道上添加了目标。你现在必须用导弹击中目标才能打开。 525 00:45:03,480 --> 00:45:07,500 一个你可以穿过的缝隙。它非常酷,为简单的游戏增加了更多的深度。 526 00:45:07,500 --> 00:45:11,580 事实上,我很想看看你如何扩展原来的游戏。如果 527 00:45:11,580 --> 00:45:15,060 你做一些有趣的东西,录一些片段,放到 YouTube 上, 528 00:45:15,060 --> 00:45:19,200 并在评论中放置一个链接。将来我可能会介绍其中的一些。 529 00:45:19,200 --> 00:45:20,940 最后,我建议你 530 00:45:20,940 --> 00:45:25,680 做另一个简单的游戏,并尝试在 Unity 中重新制作它,就像我们现在所做的那样。 531 00:45:25,680 --> 00:45:30,600 这是一个伟大的技术,因为你不必担心艺术或设计。 532 00:45:30,600 --> 00:45:35,223 只是代码。你将面临的解决问题的难题就是一个很好的例子。 533 00:45:35,223 --> 00:45:38,879 真正的游戏开发会是什么样子。 534 00:45:38,879 --> 00:45:46,366 这方面的好候选人包括乒乓,太空入侵者,突破,弹出锁,愤怒的小鸟, 535 00:45:46,366 --> 00:45:52,279 各种各样的 WarioWare 迷你游戏,如果你的互联网坏了,还可以在 Chrome 上玩恐龙游戏。 536 00:45:52,279 --> 00:45:54,540 所以,在这个视频中,我想教你 537 00:45:54,540 --> 00:45:58,680 Unity背后的基本概念-但是,剩下的就看你的了。 538 00:45:58,680 --> 00:46:04,380 幸运的是,我认为这种亲自动手,自我指导,从错误中学习的风格 539 00:46:04,380 --> 00:46:10,380 学习是最有趣、最有效的方法,可以让你坚持下去。但我们会看到的! 540 00:46:10,380 --> 00:46:14,040 在下面的评论中让我知道你的进展。如果你 541 00:46:14,040 --> 00:46:17,460 想看我的游戏开发故事吗-它仍在进行中, 542 00:46:17,460 --> 00:46:22,140 承诺-然后点击这里,发展的第一集。 543 00:46:22,140 --> 00:46:25,440 非常感谢我的赞助人——他们是让你不回吃土的爹地。 544 00:46:25,440 --> 00:46:30,540 像这样的长视频中的广告。你可以在 patreon.com 上帮助支持 GMTK.