mastertravels77 发表于 2022-1-4 10:18

从零开始独立游戏开发学习笔记(二十)--Unity学习笔记(八 ...

开始学习著名的小狐狸了。
开头相当一部分是在 Brackeys 教程里已经教授过的,就简单提一下。
1. 新建项目,导入素材

新建一个 2D 项目。导入 asset store 里的素材(导入界面为 package manager)。
导入后在 asset panel 里可以看到导入后的文件。
1.1 Pixels Per Unit

在 asset panel 中点击其中一个素材(在拖进游戏画面之前)可以在 inspector 里看到许多设置,首先讲一下这个 Pixels Per Unit。




其中,Unit 指的就是中央 scene panel 里的小网格。




因此 Pixels Per Unit 的含义就很明了,就是这个小网格代表多少个像素。
默认的 100 很显然太大了,画面会非常小,不适合操作,因此在这整个项目过程中,我们统一使用 16 的 PPU。
操作之后把背景图拖进去。
1.2 TileMap

2d 场景设置中最常用的就是 TileMap。直接在 hierachy 中创建,在 2d object 子项中,选第一个矩形。新建后可以发现出现了一个网格。


进行后续工作前可以把刚导入的背景图隐藏掉。
1.2.1 Tile Palette

打开 window -> 2D -> Tile Palette。
名字和含义很直观,把素材放进这里,就可以在场景中用素材画画了。

[*]在一切操作之前,首先要创建一个新的 palette,这是会弹出资源管理器页面,因为 palette 相关素材都要放在一个文件夹里保管,因此这里可以新建一个文件夹用于存放该 palette。
[*]导入 tileset 前,记得设置 PPU。
[*]如果直接导入,会发现整个图片都被放在一个格子里,也就是说整个图片被识别为一个素材,这样显示燃是不合格的。因此除了更改 PPU,还得切图。在更改 PPU 的那个地方,先把 Sprite Mode 改成 multiple(因为我们有多个 Sprite),然后进入 Sprite Editor。
[*]进入之后再左上角选择 slice,直接切割,会发现切成如下样式:



切的很好,但是,有一个小问题,假设我们想切成下面这样子呢:


切成这样子的原因很简单,因为可以复用素材,更加灵活。 5. 方法就是在切的时候,不要选择 automatic,而是 grid by cell size,这里我们选择 16。切完后记得 apply。 6. 拖进 Tile Palette 中,导入刚创建的文件夹里。上方工具里选择笔刷,然后下方点击选择 tile,然后鼠标移动到 scene panel 中就可以看到鼠标变成 tile 了,接下来的操作就很直观了。而且因为 tile 的大小和 PPU 都是 16,tile 可以和 grid 完全贴合,非常舒适。
1.3 成果

自由操作了一番后的成果:



2. 图层

2.1 画面比例设置

进入到 game panel,这里是我们实际游玩的时候会看到的画面。这里我们把比例调成 16:9。
此外,也可以对 camera 进行调控。比如说调控 size 的大小,也会影响游戏画面的大小,也就是能看到的画面变多(少)了。
2.3 sorting layer

也许你一经发现了,之前导入的背景图永远都在画面最前方,遮挡住了主要场景。这很不好。解决方案有两种:

[*]改变 z 轴位置。(不易于管理)
[*]使用 sorting layer。
之前 Brackeys 的教程里提到过 layer,layer 可以方便选取。比如说把一些无关紧要的东西分成 environment 层,鼠标框选就无法选中这一 layer 的 object。
不过这里我们讨论的是 sorting layer。
新建方式一样,不过要在 sorting layer 里添加:





和其他设计软件不一样的地方是,在 sorting layer 的逻辑里,越靠近下方的 layer,其实是越靠近人眼的。(当然,靠 index 来判断就不容易出错了)因此我们这里新建两个图层:



我们给背景的 sorting layer 换成 layer 1(Background),tilemap 的换成 Frontground。(注意 tilemap 在 grid 的子项里,grid 没法设置 sorting layer,要点开)
此外,即使是同一个 sorting layer(比如说都是 background),也可以设置 order in layer 来改变前后位置。逻辑也是越大越靠前。



2.4 成果

成果如下,突然好看:



3. 角色建立

3.1 放入游戏

角色素材已经有了,那么如何放到游戏中呢?

[*]直接拖。
[*]在 hierachy 里 create 一个 sprite 的 object。然后把素材拖动到 sprite renderer 组件中的 sprite 属性里。
以上两个的效果是一样的。
如果发现角色比较小,仔细想一想原因,是之前提到过的内容。
3.2 角色逻辑

虽然放进去了,但是开始游戏后这个狐狸和背景没有任何区别,没有重力也没有碰撞啥的,这些都要我们来添加。
3.2.1 重力

如果之前 Brackeys 的教程比较熟悉的话,这里应该想到添加 rigidbody,不过这一次我们使用的是 rigidbody 2d。
3.2.2 碰撞


[*]首先给小狐狸简单点添加 box collider 2d,简单地 edit 一下 collider 的范围,不要那么大一个框。
[*]给 tilemap 添加专用的 tilemap collider 2d。
3.3 成果

成果如下,草我放在了另一个没有 collider 的 tilemap 上了。



4. 角色移动

首先我们可以去 Project Setting -> Input Manager(老版本叫 Input) -> Axes 里看一下,这里是一些常用按键的设置。比如说进到 Horizontal 里可以看到方向键的左右,a 和 d,以及一些重力的设置。
说看一下就真的就只是看一下,暂时不用更改这里的东西,先看第一步。
4.1 脚本

新建一个文件夹叫 Scripts,用于存放之后的脚本。
给 player 新建一个脚本,进入 unity 编辑。
比起 Brackeys 的教程,这里给了一些不一样的移动控制方式,使用了刚刚看过的 Input Manager。


以上代码为判断水平方向是否移动,如果是 0,则说明没有按 Input Manager 里设置的按键(默认是左右方向键和 a,d)。-1 到 0 表示按了向左移动的方向键,0 到 1 反之。
注意这里用的是 GetAxis,还有一个 GetAxisRaw,后者只返回 -1,0,1。前者之所以会有小数,这个其实是灵敏度,因为有些游戏用摇杆玩的时候会有轻推和重推的区分,这是用来干这个的。不需要判断轻重的时候就使用 GetAxisRaw。
写好的代码如下:


通过添加速度来移动玩家。不过现在有一个问题,那就是移动着移动着玩家突然倒下了。

[*]这是因为 tilemap 的 collider 不规则,人物很容易撞到。
[*]为了解决这个问题,则去往 player 的 rigidbody 里,固定 player 的 z 轴旋转。(倒下是沿着 z 轴旋转的)。
小 tips:在游玩的过程中改变组件的属性,退出后会还原。但是有一个方法,可以在组件的右上角齿轮处选择 copy component,然后退出游戏后 paste component values。这样方便游玩的时候快速 debug5. 角色方向(重点,包含一些重要的 C# 的语法知识)

回到 unity,调整一下 scale 的 x 值为 -1,发现人物就这么朝着左边了。(当然追求细节的话,这样做肯定是不够的的,比如说异色瞳要换颜色,发型,背包的位置等等),不过现在就这么用。
上一小节提到了 GetAxisRaw 返回的正是 -1,0,1,正好可以用在这里,不用再做 if 判断。


有几点需要注意:

[*]scale 不叫 scale 叫 localScale,在 transform 里。
[*]不能直接给 localScale.x 赋值(y,z 同理),必须给外层的 localeScale 赋值。原因可以参考这篇文章。
[*]类似地,有一个方法是 PlayerRigid.transform.localScale.Set(1,1,1)。看起来没问题,也不会报错,但是实际没有效果。因为 localScale 返回的是一个类型为 velocity 的 copy 值,更改这个 copy 值并不会影响原始值。为什么会有这种怪异的表现可以参考这篇文章,根本原因是 transform.localeScale.Set() 会先用 get 得到一个 localeScale 的副本再用 set,而不是直接使用 set。实际上和第二点是一样的,不过这里 C# 没有给你报错,因为这里是调用方法了,C# 还没有那么智能判断出方法里的逻辑错误。
如果硬要写的话可以这么写:
var localeScale = PlayerRigid.transform.localScale;
localeScale.Set(horizontalDirection, 1, 1);
PlayerRigid.transform.localScale = localeScale;总结下来其实主要还是理解两点:

[*]Vector3 是个结构体,而结构体是值类型,无法被引用,调用方法的时候传递的只是一个副本。
[*]执行transform.localScale = ** 的时候,调用的是 set。而当执行 transform.localScale.** 的时候(如 transform.localScale.**.x),** 是通过 get 得到的,一旦 localScale 后面跟着一个点,那就是使用了 get,如果这个属性是值类型的话就要小心了,尤其是 struct 这种值类型却还可以使用点语法的。
6. 跳跃

跳跃和移动差不多,只不过判断改成了 GetButtonDown。GetAxis 也是可以的,但是如果一直按着跳跃键(默认为空格键)不放,那么 GetAxis 会一直返回 1,是一个一直按住的概念。很明显跳跃是一个一次事件,因此不能使用 GetAxis。代码如下,结合了上一小节学到的知识点。



目前代码有很大问题,因为按键判断放在了 fixedUpdate 里,因此很多时候按下去了没有被 catch 到,这个问题之前 Brackeys 的学习笔记里有讲过,不再赘述。暂时只是为了方便跟着教程走这么写。

FeastSC 发表于 2022-1-4 10:27

新年快乐~

super1 发表于 2022-1-4 10:29

新年快乐呀![爱]
页: [1]
查看完整版本: 从零开始独立游戏开发学习笔记(二十)--Unity学习笔记(八 ...