欢迎光临散文网 会员登陆 & 注册

从零开始独立游戏开发学习笔记(五)--Unity学习笔记(二)--Unity基础(二)

2021-10-03 02:28 作者:oyishyi  | 我要投稿

边听岚少直播,边继续 Unity 的学习。

1. Physic Material

上一次做 cube 的时候。cube 接触地面后会向前滚。这是因为有摩擦力。为了让物体平滑地滚动,我们可以去掉摩擦力。

  1. 在 project panel 里新建一个 Pyysic Material。用法和 Material 一致。直接拖到 Object 上即可。2. 然后可以在 Box Collider 里看到这个 asset 文件。双击该文件或者 project panel 里的文件可以打开关于这个 physic material 的 Inspector。可以改变其属性。

  2. 将两个摩擦力参数都改成 0,运行游戏,发现 cube 不会再滚动了。

2. 变量

为什么要专门提到变量。因为 Unity 可以让你在 Inspector 里看到并改动 C# Script 里变量的值,只需要声明变量为 public。然后变量就可以出现在 Script 组件的属性里了。

其实之前声明的 RigidBody 类型的 rb,也是一个变量。然后后续连接的时候我们把 cube 的 RigidBody 赋值给这个变量。

顺带一提,Unity 里的数值变量/属性,可以把鼠标移到这行属性左边的属性名上,可以看到鼠标箭头变了。表示可以左右拖拽改变数值。因为没有明显的提示所以提一下。

3. 按键

在 Update 里可以监听按键事件。使用 Input.GetKey("w") 即可。 此外 input 事件在 Update 里监听更好,而不是 FixedUpdate。比如说在 Update 里监听按键。然后赋值给一个布尔变量,再在 FixedUpdate 里根据这个变量来判断。

4. FixedUpdate 和 Update 的区别

Update 的调用是每帧调用一次。但是每一帧的处理时间很可能不一样。
而 FixedUpdate 是按照固定时间间隔调用的,而且 FixedUpdate 调用后会立刻进行物理计算。因此任何对 RIgidBody 的物理运算都应该在 FixedUpdate 里进行,因为你当然希望你的代码运行后能够立刻改变物理状态。

  • 所以如果用 FixedUpdate 就可以不需要用 Time.deltaTime 来 normalize 了。 (不过有自己的 FixedDeltaTime,是一个固定值。当然你也可以在游戏运行时改变它以调整 FixedUpdate 的运行速率)

  • 即使在 Update 里用 Time.deltaTime 也不如用 FixedUpdate。因为 Time.deltaTime 毕竟是上一帧花费的时间,而不是这一帧。也许一秒内移动的距离是固定住了,但是物理运行速度不稳定。

但是像是按键输入这种行为,则一定要放在 update 里。这是为什么呢?

  • 举个例子,你按了一下跳跃键,因为是跳跃键,所以你只按一次。然后你按键的这一刹那,刚好没赶上 FixedUpdate 执行,于是你的按键没有被监听到,因此你跳不起来,这甚至连延迟都不是,直接就是没监听到。

  • 但是你放在 update 里,则是一定会被监听到的。

5. 相机跟随

如果想让相机跟随物体,可以直接把相机拖到物体上,成为其 child。但是这样一来让物体旋转的时候相机也跟着旋转了,这样不好。所以我们用脚本来实现:

我们让 camera 的 position 等于 cube 的 position 加上一个偏移值。

  • Vector3 储存 3 个 float 值。

6. 碰撞

创建一个障碍物,当碰撞的时候,让 player 无法行动,代码如下:


其中 PlayerMovement 其实就是之前我们创建的 Sript 的名字(因为会按照 Script 名字自动创建一个类,然后创建的就是那个类的类型变量),然后在 Unity 里将其链接。最后在 c# 里将其 enabled 关掉即可停止 player 的行动。

OnCollisionEnter 会在物体碰撞发生的时候调用。可接受一个参数,就是这个碰撞事件。这个碰撞事件里的 collider 就是撞到的物体。

tag 可以在 Unity 里添加。Inspector 的右上角,有一些内置的 tag,但我们这里自己添加一个新的 Obstacle tag。

6. Prefabs

当需要创建一群相同的 Objects 的时候,一个一个复制过去虽然也可以,但是当你需要对这些 Objects 进行统一修改的时候那就麻烦了。于是出现了 Prefab 这么一个概念。
创建方式是从 Hierachy panel 里把想要变成 Prefab 的 Object 直接拖到下方 Project Panel 里。就变成了一个 Prefab,然后可以直接把这个 Prefab 拖到中央视图。就可以创建一个个相同的 Objects 了。

  • 此时如果在 Prefab 的 Inspector 里改动,所有改动都会影响到所有通过这个 Prefab 创建的 Objects。

  • 也可以改动单独的 Object,不会对其他的有影响。如果想把这个改动映射到其它 Objects 上,可以选择 Object 的 Inspector 里上方的 Override 属性里选择把哪些改动映射到其它 Objects 上。

7. 透视

Scene view 里右上角的那个显示 x,y,z 轴的东西,可以点击 x,y,z 轴来跳转到那个轴的视角上。
而点击中央则可以切换是否透视。

  1. 这是 isometric 的(我也不知道怎么翻,等比例?):

    • 可以看到地面是平行的,前后所有线都是平行,不符合透视规则


2. 这是 perspective 的(透视:) - 可以看出透视的规律(图中绿线)

除了画面右上角,选择物体的时候也有 x,y,z 轴显示(Which 你已经看到过也用过很多次了),拖动 x,y,z 其中任意两轴之间的那个小方块,可以同时移动这两个轴。按住中间的那个方块则是所有轴一起改动。

8. Layer

当你的项目比较复杂,有些时候场景东西实在是太多。当你想鼠标左键拉框选取其中几个 Objects 的时候,总是会把一些诸如地面,草地 等你不想选中的东西选到。而如果在 hierachy panel 里选又太麻烦了,因为这些东西不一定在一起。你也不一定能在 hierachy 里区分出它们。

那么这个时候就可以用 layer 来将你的 object 分层。(或者理解为 group 也可以)我们把这些环境的东西专门放到一个 layer 叫做 environment,然后让这个 layer 里的东西无法通过鼠标被选中就可以了。

我们点击地面,然后在 Inspector 上方的 layer 里,可以看到其实 unity 已经有一些预制 layer 名字了,并且可以看到我们之前生成的 Object 都已经被放在一个叫做 Default 的 layer 里了。

  • 这里我们选择 Add Layer 创建一个新的层,取名叫做 Environment。(注意虽然你是在选择地面后里加的 Add Layer,但是 Unity 并不会给你自动绑定上去,所以还要你点到地面,再在 layer 里选择 Environment 才行)

  • 然后我们点击屏幕右上角的 Layer(调整布局左边的那个),然后将 Environment 设置成无法选中(截图标红的那个):

  • 之后中央视图里鼠标左键拉框的或者直接点到地面的时候,就不会选中地面了。只能在 hierachy 里选择。

9. snap

中央视图上方一排工具里,可以选择显示网格。然后当按住 control 键,再移动物体的时候,就会附着在网格上。然后网格工具右边的一个按钮可以设置 snap 的大小,比如说设置为 1 的时候,按住 control 后,物体就只能 1 格 1 格地移动。也可以设置为 1/3(可以直接打1/3,Unity 会自动计算),这样物体就只能 1/3 格地移动。对齐的时候很有效。

10. fog

远方的东西如果都能看到,会很影响视野。和近处的东西混在一起。这个时候可以加 fog。在上方工具栏里选择 Window->Rendering->Lighting。可以把它拖到 Inspector 右边粘附成一个 tab 页。其下的一个叫做 Environment 的 sub-tab 里,勾选 Fog。可以调整雾的颜色(比如说吸色成 skybox 的颜色),雾的浓度等等

11. 高速物体的碰撞检测失效

如果物体速度过快,碰撞检测是有可能失效的。

解决方案是在 Obstacle Prefab 的 RigidBody 组件里将 Collision Detection 改成 continuous。cube 的改成 continuous dynamic。

然后在 Project Setting->Time 里减少 Fixed Timestep。其实这个就是 Time.fixedDeltaTime,减少了之后,FixedUpdate 运行频率就会更快,也就是说物理更新也会更快。

12. UI

我们想创建一个在屏幕中央显示已获得分数的 UI。

  1. 在 hierarchy 中创建一个 UI->Text object。hierarchy 会新建一个 canvas->text Object。canvas 就是用来放置你的 UI Object 的。

  2. toggle 中央视图为 2d,然后按 f 自动 focus 到刚创建的 text 上。

  3. 在 Inspector 里把 vertical overflow 设置为 overflow,不然高度太高的话,文本直接全部消失。

  4. 调整文本大小(font size)。选文本对齐方式(Alignment)(中央居中,垂直居中等)。

  5. 下一个字体,直接拖到 Asset 里。然后换成这个字体。text 里可以更改文本字符串。

  6. 还有个问题,此时你把屏幕放大缩小,字体大小不变。但我们想根据 window size 而变化。那么我们需要进入 canvas 的 Inspector,将 Canvas Scaler->UI Scale Mode 改成 Scale With Screen Size,然后将 Canvas Scaler->Match 拖到最右边(和 Height 匹配)。

  7. 这样我们 UI 的样式就调整好了。

注:Asset 文件夹太乱了,可以在里面建立 folder 整理一下。Asset 文件夹本身也是对应你项目文件夹里的 Asset 文件夹的。你导入文件其实就是往那个文件夹里复制文件罢了。

接下来我们想实时更新这个 text 的文本(有点语病哈,但是前者 text 指的是 text 这个 UI,后者“文本”指的是这个 text 的文本内容),那么就需要 Script 了:

  1. 创建一个 Script。

  2. 在 Script 里引用 player 的 transform 信息。之前讲过了就不说了。

  3. 代码如下:

13. Game Over

我们想让游戏结束后可以重来,UI 的展示等等。我们可以用一个 Game Manager 来做这件事。

  1. 我们在 Hierarchy 里创建一个空物体,添加脚本,命名为 GameManager。可以看到 Unity 会自动识别这个名字,然后 asset 处显示的 icon 就变成了一个齿轮。我们在里面重置游戏,如下代码:

  1. 然后我们在之前写的 PlayerCollision 里运行这段脚本就好了。

    • 这里有一个问题,怎么引用这个脚本,像之前那样,因为脚本本身会新建一个 class,我们建一个变量储存?

    • 可惜这样不行。因为当我们重置游戏后,player 被销毁再重建,就会忘记销毁前引用的变量(当然,引用自己的或者非引用类型的不会),同理其他 Object 上引用这个 object 的变量也会消失。

  2. 因此我们要实时地去寻找,代码如下:

  1. 不只是碰撞发生的时候需要重置游戏,当 cube 掉出去的时候也需要:

    • 当然以上不需要用 GetComponent,直接用 rb.position.y 就可以,只是演示一下。

  1. 以上还是有 bug,当重置游戏的时候,会发现光照都没有了。因为我们改变了光照设置,却没有生成。然后现在 auto 是关掉的。所以我们去之前打开过的 Lighting 面板,在 Workflow Setting 里 Generate Lighting 一下即可。

  2. 然后我们需要加一个延迟,当游戏结束后等几秒再重置,代码如下:

14. 游戏胜利

14.1 添加 Trigger

  1. 创建一个 cube,拉长放在你想结束游戏的位置,当 player 碰到它时胜利。不过我们要把它的 Mesh Renderer 关掉,这样就看不见它了。

    • 但是问题来了,我们又选不中它了,所以要加一个 icon 以便选中。在 Inspector 里面左上方可以选择 icon。不过可能显示的比较小或者不透明度太低。我们在 scene view 上方的 gizmos 列表里最上方找到 3d icons,把拖拽条往右边拖就可以让 icon 显示地更明显。

  2. 把 isTrigger 打开,这样就会被物理引擎忽视,player 碰到它会无视掉直接穿过。

  3. 我们新建一个 Script,里面写一个 OnTriggerEnter 函数,当有 Object 碰到这个 trigger 时这个函数就会被调用。(所以不要让 trigger 和其它 Object 重合,以免游戏刚开始就触发了)(当然也可以在里面判断一下碰撞的是哪个 Object 就是了)

14.2 添加 panel

我们希望胜利的时候,画面变成通关提示。

  1. 在 hierarchy 上右键,新建一个 UI->panel,这样就会在 canvas 下新出现一个 panel。

  2. 进入 panel inspector,将 source image 设置成 None,意思是我们用纯色当背景。设置一下 color,如果你发现颜色半透明,可能是 color 部分不透明度较低(准确来说是 Alpha 值)。

  3. 在 panel 上而不是空白处右键新建 text,这样 text 就会在 panel 下作为 child,而不是平级。

  4. 调整一下想要的样式。

  5. Inspector 最左上方,将勾选去掉,可以看到屏幕中已经不显示这个 panel 了,我们要在 Script 中启用它。

14.3 胜利脚本

  1. 虽然可以在 OnTriggerEnter 里操作,但是游戏胜利这种应该在 GameManager 里操作。所以我们在 OnTriggerEnter 所在脚本中引入 GameManager。这里我们不需要用 FindObjectOfType,因为我们不会销毁再重新生成 trigger。直接用变量引用即可。

  2. 然后在 GameManager 里 enable 我们刚建好的 panel。

// GameObject 引用整个 Objectpublic GameObject WinningPanel;public void Win(){    // 启用    WinningPanel.SetActive(true); }

14.4 动画

直接显示 pane了没什么意思,加动画比较有趣,所以我们开始加动画。

  1. 首先把动画窗口打开,上方工具栏里 window->Animation->Animation。(拖到下方附着比较好)

  2. 创建一个 animation,会打开资源管理器,选择放置的位置。我们新建一个文件夹叫做 Animation,然后在里面创建即可。

  3. 然后打开 project panel 可以看到里面有两个文件。一个是我们创建的 Animation,关掉 loop 因为我们不想要。第二个是 Animation Controller,控制 Animation 的各个状态等。

  4. 回到 Animation 视窗(选中 UI panel 的情况下),可以看到 timeline。点击左边红色的 recording 按钮,timeline 变成红色,就可以记录所有的改动了。Aniamtion 的创造和 AE(视频后期软件那个 AE) 一样,靠关键帧。进入 recording 状态后,所有的改动都会变成 keyframe。然后就该怎么做怎么做吧。


从零开始独立游戏开发学习笔记(五)--Unity学习笔记(二)--Unity基础(二)的评论 (共 条)

分享到微博请遵守国家法律