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

从零开始独立游戏开发学习笔记(二十)-Unity学习笔记(八)M_Studio教程2D入门(一)

2021-12-31 23:39 作者:oyishyi  | 我要投稿

学完后过来提醒,整个教程是以教会你 unity 的一些基本操作,介绍各种功能为目的。并不能当做 best pratice 来用,即使是修改了 3 次版本的手感,依然非常稀烂。整个教程充斥着,这里有更好的完善的解决方案,但是偏不用,就要用一些自己写的,通过其他方式完成实现的代码。但是其实很多时候换一种实现方式其实是为了介绍某个功能。比如巡逻功能,其实目的不是教你巡逻怎么做,而是教你如何在屏幕上显示空物体的位置方便查找。

了解这个真正的目的我觉得对理解教程有很大帮助(避免踩坑吧也是为了)。这样你看到迭代了 3 次的代码的时候,就应该把错误的代码也写一遍,而不是直接用最新的。以及当你看到一个功能实现的时候,你绝对不要去想噢原来这个功能要这么去做,而是去看用了哪些 unity 组件。

不过整体来讲确实学的难受,也是比较久远的教程了。后来我看了下 brackeys 也有一个类似的教程,就流畅并且优雅多了,如果有机会建议直接看 brackeys 的 2d 教程(去他的 ytb 频道上找一个叫做 How to make a 2d game 的播放列表,国内没有搬运(如果你在 b 站搜索 brackeys 2d 是可以搜到一个教程的,但是实际上 brackeys 有两个 2d 教程,一个叫做 2d tutorial,还有一个就是刚刚那个。这两个是不一样的,b 站那个是分散的介绍各种 2d 功能,而不是一个完整的游戏流程))。

---------以下是正文--------------

开始学习著名的小狐狸了。

开头相当一部分是在 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。
名字和含义很直观,把素材放进这里,就可以在场景中用素材画画了。

  1. 在一切操作之前,首先要创建一个新的 palette,这是会弹出资源管理器页面,因为 palette 相关素材都要放在一个文件夹里保管,因此这里可以新建一个文件夹用于存放该 palette。

  2. 导入 tileset 前,记得设置 PPU。

  3. 如果直接导入,会发现整个图片都被放在一个格子里,也就是说整个图片被识别为一个素材,这样显示燃是不合格的。因此除了更改 PPU,还得切图。在更改 PPU 的那个地方,先把 Sprite Mode 改成 multiple(因为我们有多个 Sprite),然后进入 Sprite Editor。

  4. 进入之后再左上角选择 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

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

  1. 改变 z 轴位置。(不易于管理)

  2. 使用 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 放入游戏

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

  1. 直接拖。

  2. 在 hierachy 里 create 一个 sprite 的 object。然后把素材拖动到 sprite renderer 组件中的 sprite 属性里。

以上两个的效果是一样的。
如果发现角色比较小,仔细想一想原因,是之前提到过的内容。

3.2 角色逻辑

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

3.2.1 重力


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

3.2.2 碰撞


  1. 首先给小狐狸简单点添加 box collider 2d,简单地 edit 一下 collider 的范围,不要那么大一个框。

  2. 给 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。

写好的代码如下:

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

  1. 这是因为 tilemap 的 collider 不规则,人物很容易撞到。

  2. 为了解决这个问题,则去往 player 的 rigidbody 里,固定 player 的 z 轴旋转。(倒下是沿着 z 轴旋转的)。

小 tips:在游玩的过程中改变组件的属性,退出后会还原。但是有一个方法,可以在组件的右上角齿轮处选择 copy component,然后退出游戏后 paste component values。这样方便游玩的时候快速 debug

5. 角色方向(重点,包含一些重要的 C# 的语法知识)

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

上一小节提到了 GetAxisRaw 返回的正是 -1,0,1,正好可以用在这里,不用再做 if 判断。

有几点需要注意:

  1. scale 不叫 scale 叫 localScale,在 transform 里。

  2. 不能直接给 localScale.x 赋值(y,z 同理),必须给外层的 localeScale 赋值。原因可以参考这篇文章。

  3. 类似地,有一个方法是 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;

总结下来其实主要还是理解两点:

  1. Vector3 是个结构体,而结构体是值类型,无法被引用,调用方法的时候传递的只是一个副本。

  2. 执行transform.localScale = ** 的时候,调用的是 set。而当执行 transform.localScale.** 的时候(如 transform.localScale.**.x),** 是通过 get 得到的,一旦 localScale 后面跟着一个点,那就是使用了 get,如果这个属性是值类型的话就要小心了,尤其是 struct 这种值类型却还可以使用点语法的。

6. 跳跃

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

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


从零开始独立游戏开发学习笔记(二十)-Unity学习笔记(八)M_Studio教程2D入门(一)的评论 (共 条)

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