-
嗨,我叫马克。多年来,我一直想用软件制作我自己的视频游戏。
-
比如 Unity。Unity 是很多游戏背后强大的游戏引擎,包括:Cuphead、Neon White、Tunic
-
Outer Wilds,炉石传说,看火者,甚至口袋妖怪钻石重制版。
-
但我总是发现,太长的、各式各样的、弯的教程只会让我睡死过去。
-
我不可能三连收藏就假装会了,必须亲自动手啊,自己写bug才行。
-
所以去年我发明了一个实际可行的步骤,叫三步(三连?)技巧。
-
第一,只需要学习 Unity 的非常非常基础的知识。第二,用简单的方法巩固这些练习
-
然后,第三,在你前进的过程中解决剩下的bug。这绝逼有用!
-
在大约一年的时间里,我从借鉴 iPhone 游戏到开发自己的平台解谜游戏
-
游戏名:Magnets。我发布了一篇互动视频,已经有超过 10 万次播放
-
但是这些人看完总是问!第一步你怎么做?Unity这个软件太复杂了
-
你是如何学习基础知识的?
-
嗯,所以说,我就列了一份清单
-
不管我要做什么游戏,比如如何显示一个角色
-
在屏幕上移动它们。如何生成一个东西,然后再次移除它
-
然后,碰撞怎么搞,游戏结束呢,动画和声音效果等。
-
然后我通过搜索学不完的教程,还有 Unity 文档,
-
在谷歌上搜索深奥的关键词,并大量的测试和出现bug
-
这段视频的重点就是帮你省去这些麻烦。
-
这个视频是我在学习 Unity 时就希望拥有的教程。
-
所以在接下来的 40 分钟里,我们将使用引擎来制作 Flappy Bird.
-
不是因为我们想制作Flappy Bird,而是重做这款令人上瘾的 iPhone 游戏
-
我们会学习到刚才列出的所有内容,从生成对象到游戏结束。
-
本教程将涵盖从下载 Unity 到理解 UI 的每一步。
-
去写你的第一行编程代码,去构建一个你可以分享的游戏
-
你的朋友。然后,当教程结束时,我将分享一些具体的后续步骤,你可以
-
以便自己继续学习更多内容。听起来阔以吧?那就开始吧。
-
好的,让我们从网站上获取 Unity 开始。下载并安装 Unity Hub.
-
然后你需要创建一个免费帐户才能真正使用它。
-
完成后,你将被要求安装 Unity 编辑器,本教程中使用的是 2021.3 版本。
-
都2022年了,怎么还跟原始社会一样的,
-
网络慢的跟屎一样
-
还没完成,在“安装”下,点击 Unity 编辑器上的 ... 图标并选择模块。
-
你将看到 Microsoft Visual Studio 已被勾选,这是写代码的软件。
-
所以点击继续。并安装 Visual Studio.
-
在这个屏幕上,向下滚动并勾选 Unity 游戏开发,然后取消勾选 Unity Hub.
-
因为我们已经有了,我们不需要创建帐户来使用 VisualStudio,
-
所以跳过那个。不用加载了,我们稍后再打开。
-
好了,现在都搞完了。在 Unity Hub 中,选择新项目。选择所有模板。且使用 2D Core
-
这是一个空的项目,需要做些配置才比较适合 2D 游戏。
-
给你的项目命名,点击创建,让我们开始游戏制作。
-
第一步,我们将熟悉默认的 Unity 用户界面。
-
当熟悉不同的面板时,我们将顺便显示一只鸟在屏幕上。
-
好,这是 Unity 的默认屏幕布局,它分为四个面板。
-
首先,这里是项目面板。这将包含我们游戏中的所有内容。
-
-如精灵,声音效果,脚本,瓷砖,字体等。使用过程有些东西会被生成出来。
-
不过我们也可以从电脑上的其他地方拖进一些需要文件。
-
比如,我在 Photoshop 中制作了小鸟和烟斗的图片
-
就像这样将它们导入到我的项目中。我建议你别光看,最好做一下,顺便三连。
-
但如果你有毫无绘画天赋,去视频简介里拿这些资源吧。
-
下一个面板是层次结构。这包含了当前场景中的所有内容,
-
在大多数游戏中,叫关卡也可。我们将从制作鸟开始,
-
因此,右键单击并选择“创建 Empty”。创建了一个空的游戏对象..是什么鬼呢?
-
嗯,游戏对象本质上是一个看不见的容器。它在空间中有一个位置,一个旋转,
-
和缩放。然后,你可以用组件填充该容器-以添加额外的功能。
-
例如,如果我们添加一个精灵渲染器(SpriteRenderer)组件,
我们可以将鸟的图像放到游戏对象上。
-
当然,我们关卡中的东西都是一个带有组件的游戏对象,
-
不管是鸟啊,管道啊,甚至用户界面和摄像头,都是这种一个对象有多个组件。
-
最后在第三个面板中,即检查器(Inspector)——用于查看和配置游戏对象的属性。
-
所以,当我们选中了一个新的对象,空的游戏对象,我们就可以在顶部修改名称-比如:Bird
-
我们可以看到 Transform 这里可以改变游戏对象的位置,旋转和缩放。
-
现在,我们可以按“添加组件”、“拾取渲染”和“拾取精灵渲染器”。为了实现这一点,
-
我们需要填充 sprite 字段-因此只需将鸟图像从
-
项目面板把它拖进来这里,这样就有图片显示出来了!
-
当然,这将在第四个也是最后一个面板中显示,
-
场景视图。在这里,我们可以看到当前场景中的内容,
-
如果你愿意,你可以使用这些工具来移动东西,缩放它,等等。
-
这个部分有一个额外的游戏视图标签,它向我们展示了游戏的外观。
-
运行时的主摄像头。此外,从这个下拉菜单中,我们可以设置分辨率或屏幕比例
-
为了更好地了解它在播放时的样子,所以我将选择 1920 乘以 1080。
-
哦,这只鸟占据了太多的空间。我们可以缩小规模,但实际上
-
把镜头拉远。就像我之前说的,摄影机本身就是层次结构中的一个游戏对象。
-
它有一个摄像头组件,里面有我们可以调整的属性。通过改变大小,我们可以缩小。
-
我也要改变背景颜色,搞个可爱的。
-
我们现在可以按下上面的播放按钮开始..
-
世界上最无聊的游戏诞生了。好吧,让我们把它变得更刺激一点。
-
快速回顾一下。默认情况下,Unity 有四个面板。项目包含我们的所有游戏内容
-
层次列出了当前级别中的所有游戏对象。检视器(Inspector)让我们看看。
-
并更改这些游戏对象。我们可以在场景视图中看到级别。
-
游戏对象是一个不可见的容器,我们可以用组件填充它,就像精灵渲染器一样。
-
在第二步中,我们将使用更多的组件将鸟变成
-
受重力影响的物理对象。然后我们要写一些编程代码,
-
让小鸟在我们按下空格键的时候飞起来。
-
所以让我们给我们的鸟添加另一个组件:一个2D刚体(Rigidbody2D) .
-
这把我们的鸟变成了物理物体,有重力。所以当我们点击播放时,鸟就会掉下来,从屏幕上掉下来,凉凉了。
-
我们还希望这只鸟能够与其他物体互动,所以让我们添加一个对撞机(Collider2D)。
-
2D圆形碰撞器 CircleCollider.
-
回到场景视图中,我们可以看到碰撞器有一个绿色的轮廓。对我来说有点偏离中心,
-
所以我将使用偏移(offset)来移动它。一个小游戏设计技巧,
-
如果我们使对撞机比图像小一点,它会让玩家容易通过管道,即使他们看起来摸到了边缘
-
它给了游戏一点宽容,让玩家觉得赚到了。
-
现在要添加的最后一项内容是:脚本(Script)。这本质上让我们自定义自己的组件。
-
但我们必须使用编程代码自己编写。组件列表选择: New Script
-
就叫它 Bird 脚本。加载后,双击脚本打开它
-
这将在我们之前安装的 Visual Studio 中打开该文件。
-
所以,欢迎来到编程!别慌,我保证不难,我们会慢慢来
-
我们用 C Sharp 写的,那是编程语言。
-
现在唯一需要担心的是这两个部分:启动和更新。
-
Start 用于在启用此脚本后将立即运行一次。
-
启用脚本时,Update 将持续运行,会不停的允许。
-
每一行代码,每一帧。一遍又一遍。
-
所以我们现在要用代码做的主要事情是-好吧,如果我们回去到 Unity
-
查看组件中的这些数字和文本字段,只要我们能在Unity中编辑它们
-
基本上,我们就可以编写代码来在游戏运行时更改这些属性。
-
举个愚蠢的例子,我们将在一秒钟内删除它。在开始阶段,
-
我们可以输入 gameObject 它指的是这里。然后输入一个 "."。你将看到出现一个列表,
-
许多项目引用了 Inspector 中的内容,如 IsStatic,标记、图层和名称
-
所以让我们选个 name。然后写一个等号,然后在引号中,给我们的鸟起个名字。
-
最后,我们必须始终使用分号(;)来标记一行代码的结束。
-
在我们回到Unity之前,我们必须保存一下脚本。
-
现在,当我们运行游戏..游戏对象的名称已更改,怎么样牛不牛。
-
好的,删除那个代码。那只是给傻子看的。
-
不过我们学会了使用代码与游戏交互了。我们可以通过写代码来操作某个东西。
-
比如游戏对象,然后名称,然后命令
-
将其更改为 Bob Birdington. 会经常写这种东西。
-
其实,我们真正想做的是..在刚体 2D 的组件中,在“信息”(Info)下,
-
我们会看到一个灰色的速度场。
-
想写一些代码来给鸟增加向上的速度,使它飞到空中。
-
问题是…。最初,脚本只能与游戏对象的顶部进行操作。
-
现在,这个脚本完全不知道其他组件。
-
所以我们需要先解决这个问题。我们需要在这个脚本上做一个特殊的插槽
-
一个 RigidBody2D-这样我们就可以与操作它并向它发送命令。这称为引用。
-
我们将在这里创建引用,在类名和 Start方法。
-
我们将编写 public RigidBody2D myRigidbody.
-
所以我们现在有一个插槽来存储 RigidBody2D.我们有一个可以参考的名字-
-
以确定我们需要的是这个特定的刚体 2D. 因为我们公开了它,我们从脚本外部访问此插槽。
-
所以如果我们回到 Unity,我们会看到脚本组件现在有一个用于 RigidBody2D 的字段。
-
我们可以把组件拖到那个槽里,放下。
-
我们已经在脚本和刚体之间建立了一条通信线路。
-
好的,回到 Visual Studio. 在 Update 中,我们可以输入 myRigidbody. 然后是点。
-
现在看看我们可以调用的所有东西。比如:角阻力,重力比例,质量
-
这些都是组件上的属性,我们需要的是速度(velocity)。
-
我们想把它设置为一个新的数字值,所以,就像前面的名字一样,我们将写一个等号。
-
现在我们在这里写的实际上是一个向量,它是两个数字,表示一个二维空间。
-
在这种情况下,它用来表示鸟飞行的方向。
-
我们想要鸟直接上升,所以是 (0, 1) 是可以的。我将使用 Vector2.up,
-
这是(0, 1)的内置简写。为了给它更多的能量,
-
我要将该矢量乘以一个数字。说,10,这应该可以把鸟送上西天。
-
现在,就像我之前说的,Update 中的任何代码都会每一帧反复运行,
-
所以如果我们保存脚本,并在 Unity 中点击播放..我们的鸟飞了。祈祷!
-
这样不行,最好只在玩家按空格键的时候。那么
-
是时候使用最基本的编程代码了:if 语句。
-
if 语句就像一个门。
-
你可以用大括号包围一些代码,代码的每一帧都将完全不执行
-
除非,游戏满足一些特定的条件
-
在门上-在这种情况下,门是打开的,代码被读取并执行。
-
所以我们想说“如果玩家按下空格键,然后加上向上的速度”。
-
为了做这个..我们可以写 if,然后在括号里写条件。
-
这次我们不是在讨论一个组件,而是在讨论 Unity 本身——特别是它的输入系统。
-
那么我们将编写输入。然后我们可以选择 GetKeyDown,并在括号中选择 KeyCode.space.
-
如果在该帧上按下了空格键。然后我们输入 “== true”。
-
关于等号的简要说明——我们用一个等号来使左边的东西等于右边一个值。
-
我们用两个如果我们只是在检查左边的东西和右边的一样
-
懂吗?
-
无论如何。所以这个代码说..如果空格键刚被按下,那么..然后我们会
-
将代码放在大括号里。
-
所以,现在在 Update 中-游戏的每一帧都会走到门口,然后被问到嘿,
-
空格键是否已按下?如果是,代码将运行,鸟将扇翅膀。如果没有,
-
它将跳过花括号中的代码,并在下一帧中重试。
-
所以-保存脚本,并返回到 Unity. 我们现在可以点击播放和 Tada:
-
当我们按空格键的时候,鸟就会飞起来。
-
我们现在已经创建了一个角色,并使用键盘控制它。这就是一个电子游戏。牛!
-
然而,它就像垃圾。这翅膀扇得不对,感觉不像原作。
-
所以我们可以改变这个数字。保存。回Unity,运行游戏。不太对。
-
停。更改一下。保存。不过好傻啊,我们搞点更聪明的办法吧
-
首先,我们要创建一个变量。让我们回到脚本的顶部和底部
-
我们参考刚体,让我们制作一个名为 flapStrength 的公共插槽。
-
一个浮点数(float)-是一个可以有小数位的数字。
-
然后回到我们的更新代码中,我们将 Vector2.up 乘以 flapStrength,而不是 10。
-
现在,回到 Unity 中,你将看到脚本组件有一个新字段:flapStrength
-
我们可以随时改变这一点,让游戏手感更有感觉。
-
我们甚至可以在游戏中更改它,但请注意,你更改的任何内容
-
当你按下“停止”时,正在运行的游戏不会保存。这意味着你可以玩
-
你可以尽情享受你的更改,而不用担心搞砸你的游戏。
-
所以,如果我们弄乱了flapStrength,还有刚体重力缩放
-
我们希望能得到一些感觉良好的东西。
-
来回变换数字:观众老爷,这就是游戏设计!
-
接下来复习一下。
-
在游戏运行时,我们可以使用代码来更改组件的属性。
-
默认情况下,脚本不能与游戏对象上的其他组件对话。
-
你必须通过存储对该特定组件的引用来建立通信线路。
-
我们在代码中创建引用,然后通过拖放在 Unity 中填充它。
-
当脚本出现时,Start 中的代码只运行一次。Update 中代码每一帧连续运行,
-
但是,我们可以使用 if 语句跳过一些代码,除非满足条件。
-
我们可以使用公共变量来改变某些Unity 的 Inspector 中的值
-
即使在游戏运行时。
-
好的,Flappy Bird 的秘密是,虽然它看起来像一只鸟
-
在一个充满管道的世界里飘动,但实际上是
-
鸟保持完全静止,管道在屏幕上移动。所以在第三步中我们
-
让管道在世界上生成,在屏幕上移动,然后出去屏幕后移除它们。
-
我们将从制作我们想要生成的对象开始。
-
这将是从左到右穿过屏幕的两根管道。
-
让我们创建另一个名为“管道”的游戏对象。现在把它准确地移动到鸟中间,
-
以获得正确的相对位置。然后我们将在这个中创建另一个对象,称为顶管。
-
这是第一个和父对象和子对象层级有关的游戏对象。
-
这边我们可以嵌套多个游戏对象,并通过移动父对象一次移动所有子对象
-
所以让我们还是重复像制作鸟的过程一样,为管道图像添加精灵渲染器。
-
并添加一个碰撞器-这次是一个 2D 的盒子碰撞器(BoxCollider)。我们不需要刚体,
-
因为它不会受到物理学的影响。然后我们可以把它移到鸟的上方,但保持 X 的位置为零。
-
最后,我们可以复制整个顶部管道对象。然后叫它底管。然后翻转一下
-
通过将 Y 刻度更改为-1,将其颠倒。然后把它移到鸟的下面。
-
如你所见,如果我们改变管道父游戏对象,两个管道都会以父对象作为轴点
-
移动,缩放,并与其一起旋转。
204
00:17:35,400 --> 00:17:38,940
将脚本添加到此父对象,使其在屏幕上移动。
-
我们首先为 moveSpeed 创建一个变量。如果我们在这里给它一个数字,
-
它将填充此值作为 Unity 中的默认值。但我们以后可以随时改变它。
-
然后,我们将在更新中编写代码来移动对象。如果我们能
-
键入 transform.position.x,然后直接更改此数字,但其实不是,Boo,
-
你必须一次完成整个Vector的更改。哦,这次我们要用Vector3,而不是矢量 Vector2,
-
因为 position 有三个数字。尽管我们在制作 2D 游戏,但从根本上说
-
Unity 仍然是一个 3D 引擎,因此它通过 Z 值来跟踪对象的深度。
-
所以,我们要做的是。我们将transform.position, 然后等于。
-
我们想要添加到它的位置,所以再次编写
-
transform.position = transform.position + (Vector3.left * moveSpeed);
-
返回 Unity,按“播放”。卧槽,太快了。
-
现在,你可能认为你可以把这个 moveSpeed 变量改成
-
像 0.001 这样的小数字。这会起作用——但这实际上不是问题所在。
-
你可以看到,Update 中的代码只是尽可能频繁地运行。事实上,如果我们在游戏视图中查看属性,
-
我们将看到游戏以每秒超过 1000 帧的速度运行。嘿,
-
对不起,PlayStation 5,每秒 120 帧?呸,这和《Flappy Bird》没什么关系。
-
真正的问题是游戏可能在不同的计算机上以不同的速度运行,
-
我们不希望管道移动得更快或更慢,这取决于你的电脑。
-
真实游戏其实也犯过这个错误——在《黑暗之魂 2》中,武器耐久度随每帧耐久度下降
-
所以你的剑在每秒 60 帧时会比每秒 30 帧快两倍。那真是太棒了。
-
幸运的是,这是一个很容易解决的问题。我们只需乘以 Time.deltaTime.
-
这确保了无论帧速率如何,乘法都会计算出相同的值。
-
不过在velocity的代码上则不需要它,因为物理运行在它自己的小时钟上,
-
如果你想知道更多-关于这件事,或者任何事,
-
Unity 文档是一个好地方。你会找到更多和示例代码。
-
好的,现在有了这个解决方案,我们的管道可以在屏幕上平滑地移动。可爱的。
-
接下来,我们要创建一个将不断生成新管道的系统。首先,
-
从层次结构中获取父游戏对象并将其拖动到项目中。这将创建一个预制游戏对象(Prefab)。
-
或叫预制。这就像一个游戏对象的蓝图,我们通过这个蓝图
-
创建新的整个游戏对象的另外版本,会包括所有子对象、组件和属性。
-
在我们继续之前,我们现在可以删除层次结构中的原始内容。拜拜。
-
让我们创建一个名为“管道产生成器”的新游戏对象。
-
我们会把它放在摄像机的右边。我们会为它做一个脚本。
-
脚本将每隔几秒生成管道预置的新版本。因为管道已经
-
有向左移动的代码,管道将在生成后立即自动在屏幕上移动。
-
我们将编写一些代码来生成我们刚刚制作的预置。那么
-
我们将从引用预制件开始。
-
在这里,我们将输入公共游戏对象管道。
-
然后在 Unity 中,我们将使用相同的拖放方法来填充插槽,但这一次,
-
我们将从“项目”面板中拖动预置,而不是组件。
-
现在,Unity 有一个很好的内置生成对象的方法。
-
新游戏对象。我们将输入 Instantiate,然后打开括号。
-
在这里,需要填一些额外的细节。我们实际上可以通过翻阅这些找到不同的,怎么说呢,食谱吧?
-
我猜?这个看起来不错,它将在指定的位置和旋转创建一个对象。
-
因此,对于游戏对象,我们可以输入 pipe. 对于位置
-
我们可以只输入 transform.position 来获取对象的位置。
-
意思是说,会把管道生成在生成器所在的位置。
-
并且对于旋转,让我们只使用 transform.rotation 和生成器也相同。
-
让我们运行它,天啊,这不是我们想要的。生成效果很好,
-
但他们每一帧都在出现,我们希望他们出现。
-
在一个我们可以控制的好时间里。所以,回到 Visual Studio.
-
我们现在要做的是写一些代码来制作一个计时器。这将计入
-
指定的秒数,运行一些代码,然后再次开始计数。要做到这一点,
-
我们需要做几个变量。生成器应该指它应该有多少秒生产一个管道
-
然后计时器是计数的数字。我们可以做这个。
-
一个私人的,因为我们不会在编辑器或其他任何地方更改它。
-
在Update中,我们将执行另一个 if 语句。此时,如果计时器小于 spawnRate,
-
然后我们让计时器加一。因此,我们将采用当前的计时器,
-
并将 time.deltatime 添加到其中。这将创建一个计数每一帧的数字,
-
无论你的计算机的帧速率是多少,它的工作方式都是相同的。
-
实际上,我们可以通过将其更改为 += 来缩短它,但是,不要觉得你需要将代码设置为尽可能短
-
只是为了避免大佬们不屑的评论。
-
如果:"timer = timer + xxx" 更容易阅读和掌握,那么这绝对没问题。
-
你熟练了,可以随便使用。
-
现在,在我说 if 语句之前,它就像一扇门。
-
我们可以在它的旁边添加另一个门,否则。这意味着,
-
如果不满足条件,则跳过该代码,并在 else 中执行该代码。
-
因此,我们将在这里放入生成代码,并将计时器重置为零。所以现在,每一帧,
-
询问计时器是否小于生成率。如果是的话,那就把计时器算起来。如果不是
-
即计时器实际上已经达到或超过了生成速率,然后生成一个管道并再次启动计时器。
-
把这个放在 Unity 里-很好。我对此很满意。唯一的问题是。
-
我们必须等待第一个管道生成的时间很长。如果这个马上出来就好了,对吧?
-
现在,我们可以将代码复制并粘贴到 Start 中,这样它就会在 Start 中发生一次。
-
然后在更新中一遍又一遍地发生。但这是个坏主意。你通常应该试着
-
避免在多个位置使用相同甚至相似的代码。
-
如果我们想要改变生产的工作方式?我们必须各处寻找并修改它,不是很好。
-
相反,我们可以将代码放在一个新函数中,然后运行它
-
所以在这里,下面的Update,但在最后的花括号上面-我们将
-
创建一个名为 void SpawnPipe()的函数。然后将实例化代码剪切并粘贴到其中。
-
现在,我们只需在 Update 和 Start 中编写带有空括号的 SpawnPipe.这将运行
-
执行这些行时该函数中的所有代码。做完这件事,
-
它会让游戏一开始就有一个管道,并且每次计时器达到最大值时都会产生新的管道。牛了。
-
然而,这是一个相当无聊的游戏,对不对?管子总是从中间出来。我们想要他们
-
以随机的高度出现。请记住,当我们编写实例化代码时,
-
我们必须选择一个物体出现的位置。我们将更改该值。
-
现在,管道总是在与生产器相同的位置生产。我们想要 X 值
-
是一样的..但对于 Y,我们想要在生产器上方或下方的某个位置随机选择一个点。
-
因此,让我们为高度偏移创建一个公共变量,可能是 10。
-
然后我们将创建一个名为 lowestPoint 的浮点数。因为我们在函数,
-
而不是在脚本的顶部,这意味着它只能在函数中使用。但是,
-
这也意味着我们可以通过计算来设置它。
-
因此,我们将执行 = transform.Position.y - heightOffset. 然后我们再做一个。
-
一个是最高点,但这次是加上高度偏移。这样我们就得到了这两个数字。
-
然后,我们将替换实例化代码中的 transform.position.
-
我们要写一个新的Vector3,每当我们指定我们自己的数字时,我们必须用 Vector3
-
然后在括号中,我们将 x、y 和 Z 值指定为三个不同的浮点数。对于 X,
-
我们希望这与生成器相同,所以我们将执行 transform.position.x.
-
但是对于 y,我们可以做随机范围。在括号里,我们可以提供一个最小值和最大值。
-
从中挑选。这是最低点和最高点。然后用 0 表示 Z,并关闭括号。
-
回到Unity..好的!管道将在这两个数字之间的任何位置生成。
-
哦最后一件事。每次这些管道生产时,它们都会出现并向左移动..永远。
-
这不是很好的方式——他们在屏幕外什么也不做,
-
然而,它们仍然在内存中,每一帧都在运行代码。如果太多,它们就会
-
把你的电脑内存全吃掉,然后崩溃。所以让我们解决这个问题。
-
现在,我们可以制作一个计时器,并在几秒钟后删除管道。但是相反,
-
我们将检查管道的 X 位置,如果它超过了某个点,则将其删除。我们会借的
-
这只鸟找出了屏幕左边的 X 坐标。看起来大约是零下 45 度
-
在管道移动脚本中,我们将为死区添加一个浮动。-45. 然后是一个简单的 if 语句-if
-
如果 Transform.Position.X 小于 DeadZone,则销毁保存此脚本的游戏对象。
-
在 Unity 中运行它,然后,砰,他们死了。
-
让我们再做一件事,像个学生一样。就在销毁之前,
-
让我们编写 Debug.Log,并在括号中删除管道。然后,回到 Unity 中,
-
你将看到另一个面板我在 UI 演示过程中跳过了-它是 Project 旁边的一个选项卡,称为 Console.
-
然后当我们运行游戏..每次删除管道时,我们的消息都会发送到控制台。
-
这是一种非常有用的调试代码的方法,因为我们可以准确地找出代码的目的。
-
回顾时间!
-
游戏对象可以转换为预制件,
-
通过将它们从层次结构中拖放到项目中。
-
然后你可以把它们拖到场景中, 我在我的益智游戏中使用预制件来创建关卡。
-
比如说。或者你可以在游戏中创建一个生产器来实例化新的生产器。
-
计时器是一种使代码在特定时间间隔内发生的好方法,
-
但始终使用 time.deltatime 来保持不同计算机之间的一致性。
-
if 语句可以有一个 else 的门,以使代码在条件未满足。
-
你也可以有其他的 if,来制作更复杂的门。
-
如果不再需要游戏对象,你应该尝试删除它们,以释放内存。
-
好的,我们下一步是记录玩家的得分,
-
并在用户界面上将其显示给玩家。然后,
-
我们希望每次鸟穿过管道时,分数都能上升一分。
-
因此,请记住,游戏对象不一定是游戏中的物理对象
-
世界就像一个角色或一个敌人-它可以是一个完全看不见的管理者
-
这只是跟踪关键数据,如HP、时间或分数。然后,
-
我们可以使用用户界面让玩家看到这些信息。
-
所以让我们开始制作 UI。像其他东西一样,在层级面板,它是一个游戏对象。
-
这一次,向下到 UI 并选择文本,它可能在Legacy
-
我们需要将场景视图放大到非常远的地方,才能真正看到 UI.
-
为了确保 UI 在每个设备上看起来都一样,我们将选择这个新的画布游戏对象
-
将画布缩放器组件的 UI 比例设置为随屏幕大小缩放,并选择合理的
-
参考分辨率-我将再次使用 1080p.然后,我们可以移动文本。你会注意到
-
该 UI 具有 Rect Transform,而不是正常 Transform。最重要的是要注意
-
你真的先不要使用 scale改变大小,而是用宽度和高度。
-
然后,我将增加字体大小并设置默认值。
-
文本设置 0. 然后检查它在游戏视图上看起来都很好。
-
好的,现在我们要做一个脚本来存储玩家的分数,
-
并将 UI 上的数字更改为该分数。
-
我们将制作一个名为逻辑管理器(Logic Manager)的游戏对象。
-
我们会给它一个脚本。此脚本将跟踪高级信息
-
比如玩家得分。它将具有我们可以运行的各种基本功能。
-
因此,我们将删除 “Start”和“Update”,我们在此脚本中不需要它们。我们可以随时添加
-
如果我们后面还要,可以加回来。我们想为玩家的分数存储一个数字。这一次,
-
我们不想要浮点数,因为我们只想要整数。所以让我们做一个整数,
-
这是个整数。没有小数位。
-
因为我们想要更新我们刚刚创建的 UI 文本,所以我们将一如既往地
-
必须做一个引用。但是,好像没找到合适的。
-
啊,好吧。默认情况下,脚本只加载Unity所需的基础内容。
-
但如果我们在顶部输入 UnityEngine.UI;,我们现在可以
-
访问更多的功能-在这种情况下,是用户界面的东西。现在我们可以参考文本。
-
我们需要将文本组件拖回 Unity 中的这个字段。因为我们正在引用一个组件
-
在另一个游戏对象(UI 上的文本)上,最好的方法就是
-
拖动整个游戏对象进入我们的插槽。这将自动为我们找到文本组件。方便。
-
所以现在我们要做一个函数。我们称之为 AddScore.
-
还有因为我们将从其他脚本运行此函数,并将其设置为 public void.
-
这个函数需要做两件事。在玩家的分数上加一。很容易,
-
我们现在知道该怎么做了。并将 UI 上的文本更改为此数字。哦,文本框是
-
寻找一个字符串-字符序列-我们的分数是一个整数。他们看起来一模一样
-
对我们人类来说,但程序是挑剔的。请注意,通过将.ToString()添加到游戏分数中,很容易修复。
-
为了确保这一点,让我们自己来运行这个函数。
-
从Unity本身。我们需要做的就是在函数上面写上 ContextMenu 和一个名称。
-
现在,在 Unity 中,当游戏运行时,点击这个脚本上的小点并选择函数。
-
好看的!这类东西在测试中非常方便。
-
好的,现在我们知道函数运行了,我们特别想运行它。
-
当鸟在管道之间穿行时。而做到这一点的方法是碰撞。
-
现在,如果两个物体有碰撞器,它们会互相撞击-事实上,在我们的游戏中,
-
这只鸟已经撞进了管道,因为我们在两个管道上都增加了对撞机。然而-你
-
也可以有不可见的碰撞器,称为触发器(Trigger)。它们不会造成真正的碰撞,
-
但是它们确实让你知道两个对象已经接触-并且你可以在此时运行代码。
-
所以我们要在管道之间放一个触发器,
-
所以我们知道这只鸟穿过了它们。然后在那个时候,我们将运行 addScore.
-
让我们打开管道的预置。我们将制作另一个名为“middle”的游戏对象
-
它需要一个盒子对撞机。让我们把它做成这种形状。这次我们要
-
在 Is Trigger 框中打勾。最后,让我们为这个新的中间游戏对象添加一个脚本。
-
在 Update 下面,输入 OnTrig,按Tab,自动将帮助我们输入 OnTriggerEnter2D.
-
此函数意思是,任何内容进入碰撞器的第一次时都将运行
-
还有 OnTriggerExit 和 OnTriggerStay,供将来参考。
-
我们要运行之前编写的 addScore 函数..除了。啊哈。再一次,
-
这个脚本不知道游戏中的任何其他脚本,直到我们引用它。
-
所以我们可以编写公共逻辑脚本逻辑。但回到 Unity,你很快就会意识到
-
你不能把脚本拖到这个槽中。你不能把它从项目中拖出来
-
我们只能与游戏对象上的脚本实例来操作。
-
但我们也无法从场景拖动到预置中。这是因为场景中还不存在管道,
-
只有当游戏运行时,生产器开始制作管道时,它才会存在。
-
因此,我们需要使用代码来填充此引用。
-
这需要在管道首次生成时进行。
-
为此,我们需要帮助代码找到逻辑脚本。
-
要做到这一点,以游戏逻辑对象为例,查看检查器的顶部:
-
你会看到标签。从下拉列表中选择“添加标记”。创建一个新的标签,
-
并确保你回到游戏对象并实际设置这个新标签。你不会忘记的
-
在你的Unity职业生涯中,这样做大约 8000 次,所以期待吧。
-
现在,回到 PipeMiddleScript 中,
-
在 Start 下,我们可以写 logic = Gameobject.FindGameObjectWithTag("logic")。
-
这将在层次结构中寻找第一个带有logic标签的游戏对象,
-
在我们的例子中,场景中永远只有一个,
-
所以我们知道它总会找到合适的——但一定要注意这一点。
-
然后我们可以添加.GetComponent();
-
所以,一旦一个新的管道产生,它就会通过层次结构来寻找一个有logic标签的游戏对象。
-
然后,它将查找该对象的组件以找到脚本LogicScript。
-
如果它找到一个,它会把它放在我们的参考槽中。
-
它完成了与拖放组件到插槽完全相同的操作。
-
当然,区别是通过代码在运行时做到的。
-
所以现在,管道的中间脚本可以找到逻辑脚本并与之操作。
-
如果我们编写 logic.addScore,它将运行该代码。回到Unity,
-
点击播放,如果我们一切都做得很好,当我们通过管道之间时,分数将上升一分。
-
哦,不过为了后面更好的做其他事情,让我们确保通过管道的确实是这只鸟。
-
我们将通过把鸟放在一个层(Layer)上,并设置碰撞是否有效。
-
转到鸟的游戏对象,这一次,而不是标签,我们将改变鸟的层
-
添加一个新的,记得回来设置一下,并记下数字。
-
现在,在管道的中间脚本上,我们可以在 addScore 周围添加一个 if 语句,
-
并检查刚刚发生的碰撞是否与属于 Bird 层上的游戏对象。
-
当我们谈到这个话题的时候,还有一点需要做的。回到Logic Script。
-
让我们以 addScore 函数为例,在这些空括号中,
-
我们将写下 int scoreToAdd. 然后,我们将添加 soreToAdd,而不是添加一个 1。
-
然后,在管道中间脚本中,我们可以在 addScore 后面的括号中写入 1.
-
对的,现在,这和我们以前做的事情完全一样。但是,你肯定能猜到,
-
稍后,你可以在游戏中添加一些其他目标,例如,在你的得分上添加 5。
-
这使我们可以使函数更通用,因为它可以使用
-
以不同的方式,从不同的地方。我认为成为一名优秀程序员的一部分,
-
是让东西变得不那么死板,并为未来的想法保持开放。
-
这使得对设计进行迭代变得更加容易和快速。
-
对的!回顾!
-
UI 只是另一个游戏对象,但如果我们想在这些组件中引用
-
我们需要将 UnityEngine.UI 添加到脚本的顶部。
-
游戏对象可以是完全看不见的东西,
-
只是在那里管理规则、逻辑、分数等等。
-
如果我们想在一个游戏对象不在场景中的时候引用一个组件,
-
我们需要在运行时找到该组件。
-
一种方法是使用Tag、FindGameObject 和 GetComponent.
-
只要有引用,就可以从另一个脚本运行公共函数。
-
对那个脚本。我们甚至可以在该函数运行时传入变量。
-
当两个物体接触时,碰撞和触发可以用来使事情发生。
-
说到碰撞,让我们进入下一步……
-
最后一步是添加失败状态。当鸟撞到管道时,游戏就结束了。
-
我们将通过在屏幕上制作一个游戏结束界面来做到这一点,并让它在鸟碰到管道坠毁时出现。
-
游戏结束界面将有一个按钮,我们可以用它来重置游戏。
-
首先,让我们在屏幕上制作游戏结束界面。在画布游戏对象上,
-
添加一个新的空的游戏结束界面。然后,在该父项中,添加“游戏结束”文本。
-
还有一个按钮-也在 Legacy 下。调整它的大小。
-
和改变按钮上的文本,该文本可以作为按钮本身的子项找到。
-
所以回到按钮游戏对象上,在按钮组件上,你会看到
-
点击时显示的这一点。这是一个事件,它允许我们调用
-
游戏对象上的公共函数。所以让我们做一个重启关卡的函数。
-
我们可以将这段代码放在逻辑脚本中,
-
在我们的 addScore 函数下面。如果你愿意,你可以做一个单独的脚本,
-
但我觉得这很好。让我们创建另一个名为 RestartGame 的公共函数,
-
在这里,我们将编写代码来重新启动场景。就像以前的用户界面一样管理场景
-
然后,我们需要在顶部添加一行-这一次,使用 UnityEngine.SceneManagment.
-
现在,在我们的函数中,我们将调用 SceneManager.LoadScene.
-
这需要一个场景的名称。其实就是场景的文件名。但是因为我们想要当前的场景,我们
-
可以简单地输入 SceneManager.getActiveScene,方括号,.name。
-
现在回到 Unity 中,向此按钮添加一个事件。
-
然后拖入逻辑游戏对象。并找到 RestartGame 函数。
-
简单测试一下..。好的,每次我们按下按钮,游戏就重新开始。
-
现在很明显,我们不想让它一直出现在屏幕上——只是在我们失败的时候。那么,
-
我们可以将整个游戏置于屏幕游戏对象之上,并使用此复选标记将其禁用。
-
那我们就让它在鸟撞进管道的时候出现。
-
我们先写函数。同样,在逻辑脚本中,让我们为 GameOver 创建一个公共函数。
-
我们需要对屏幕上的游戏对象进行引用。
-
并用Unity来填充它。
-
然后我们可以简单地在这个函数中输入 gameoverscreen.setActive true.
-
所以我们想让这个函数在鸟撞进管道时触发。
-
回到 Bird 脚本,让我们重用之前的代码来访问逻辑脚本。
-
是的,我们可以在 Unity 中拖放引用,但是,
-
我们现在已经编写了这段代码。然后我们将对触发器代码执行类似的操作,但这次
-
我们将使用 OnCollisionEnter2D,因为管道是实体对象,而不是设置为触发器。
-
当冲突发生时,使用 Logic.GameOver 触发游戏结束脚本。
-
回到Unity..这是可行的,但我们仍然可以在屏幕上玩游戏。不是很理想。
-
我已经讨论了几个关键的变量类型。浮子
-
整数是数字。字符串通常用于文本。另一个重要的是布尔,
-
布尔值的缩写。这是一个非常简单的类型,要么为真,要么为假。在上方,
-
或者关掉。是,或者不是。这是一种简单地检查或更改某物状态的好方法。
-
所以让我们有一个名为 birdIsLive 的布尔,并确保它开始为真。
-
然后,当碰撞发生时,我们将 birdIsLive 设置为 false.
-
最后,我们将在第一个 if 语句中添加一个额外的条件。
-
比如说,如果空格键刚刚被按下……由两个“”符号组成。而 birdIsLive 是等于真。
-
实际上,我们不需要加上这个 == true 的东西。是可以的。
-
没有它也一样。但是,再一次,这取决于你-也许写出完整的代码更容易阅读。
-
无论如何,现在,如果鸟死了,它就不会拍打翅膀,这对我来说似乎很合乎逻辑。
-
最后要做的是构建游戏。这真的很容易。选择文件、生成设置和生成。选择一个文件夹
-
你的硬盘。让Unity发挥作用。然后你可以打开这个文件来玩你的游戏!令人惊讶的。
-
在很短的时间内,我们制作了一个非常实用的游戏。
-
更重要的是,我们学到了很多关于Unity的基本知识。
-
我们创造了一个角色,它会根据我们的输入而移动。
-
我们有在计时器上生成新对象。我们已经创建了一个显示分数的 UI,
-
并在条件满足时使分数打勾。我们有能力结束一场比赛,
-
然后重新开始。
-
现在,我应该注意到,有不同的,也许更好的方法来做几乎所有的事情。
-
在本教程中。例如,我使用了 Unity 的传统方法来检查输入
-
Unity他们后来开发了一个非常非常好的输入系统。但要复杂得多
-
使用-所以这个简单的方法现在很好,你可以稍后再看看新的输入系统。
-
当你感觉更自信的时候。对我来说就是这样。还有 TextMeshPro,
-
已经取代了旧的Legacy用户界面系统-所以你会想要升级,在某些时候。
-
无论如何,这些都是对制作各种游戏有用的课程。
-
但是..游戏还没有完全结束。还有一些事情要解决。尽管,
-
我不想告诉你怎么做每件事。所以我会给你一些建议完成游戏,
-
我想让你自己试着搞定这些。
-
首先,如果小鸟从屏幕上消失,我们也需要让游戏结束。
-
这应该不会太难。还有一个错误,分数会一直上升,
-
即使在比赛结束后。也试着解决这个问题。
-
我们也想要声音效果。我想让你在逻辑管理器中添加一个音频源组件。
-
用音效文件填充它。在脚本上引用它。当分数上升时让它播放。
-
然后,我想让你玩一下粒子系统,让云出现在游戏中。
-
接下来,打开动画窗口,为小鸟添加一些拍动的翅膀。
-
然后我想让你添加另一个场景来制作一个标题菜单界面,
-
所以游戏不会立即开始行动。这里有一个提示:
-
你需要将此新场景添加到“构建设置”(Build Settings)窗口。
-
最后,如果你想要一个真正的挑战-使用 PlayerPrefs
-
将玩家的高分保存到硬盘上,并在用户界面上绘制。
-
对于其中的每一个,你可能会想要谷歌相关的术语,阅读 Unity 文档,
-
观看一些快速教程视频,或在下面的评论中寻求帮助。
-
接下来,你可以扩展一下《Flappy Bird》。发挥创意
-
并添加原始 iPhone 游戏中没有的想法或设计。
-
举个例子,我给了它发射导弹的能力,
-
然后我在管道上添加了目标。你现在必须用导弹击中目标才能打开。
-
一个你可以穿过的缝隙。它非常酷,为简单的游戏增加了更多的深度。
-
事实上,我很想看看你如何扩展原来的游戏。如果
-
你做一些有趣的东西,录一些片段,放到 YouTube 上,
-
并在评论中放置一个链接。将来我可能会介绍其中的一些。
-
最后,我建议你
-
做另一个简单的游戏,并尝试在 Unity 中重新制作它,就像我们现在所做的那样。
-
这是一个伟大的技术,因为你不必担心艺术或设计。
-
只是代码。你将面临的解决问题的难题就是一个很好的例子。
-
真正的游戏开发会是什么样子。
-
这方面的好候选人包括乒乓,太空入侵者,突破,弹出锁,愤怒的小鸟,
-
各种各样的 WarioWare 迷你游戏,如果你的互联网坏了,还可以在 Chrome 上玩恐龙游戏。
-
所以,在这个视频中,我想教你
-
Unity背后的基本概念-但是,剩下的就看你的了。
-
幸运的是,我认为这种亲自动手,自我指导,从错误中学习的风格
-
学习是最有趣、最有效的方法,可以让你坚持下去。但我们会看到的!
-
在下面的评论中让我知道你的进展。如果你
-
想看我的游戏开发故事吗-它仍在进行中,
-
承诺-然后点击这里,发展的第一集。
-
非常感谢我的赞助人——他们是让你不回吃土的爹地。
-
像这样的长视频中的广告。你可以在 patreon.com 上帮助支持 GMTK.