找回密码
 立即注册
查看: 390|回复: 11

[简易教程] 从零开始独立游戏开发学习笔记(三十一)--Unity学习笔记 ...

[复制链接]
发表于 2022-5-5 13:01 | 显示全部楼层 |阅读模式
哎,说真的,我真的很不想连续两篇都戛然而止,搞得好像我很容易放弃一样。但是不得不说这教程真的不适合我。(我其实想说的更狠点,但还是算了)。学的时候真的一锅子火,太强行了。一个功能有他自己适合的用处,而不是用在毫不相关的地方告诉你还可以这么用,这样是能做,可是有什么意义。
举一反三,举一反三,一都没举就直接给你反三,还把这三当成一来用,太难受了。我中间查阅了一大堆手册。每次手册上的官方例子都在打视频的脸,我真的看不下去了。真想把当初找教程的我打一嘴巴子。
算了算了,也不是说教程多不好。只是不适合我,既有优点也有缺点,我下面也分析了一下。大家看着办吧。
-------以下是正文---------
学着学着逐渐开始意识到 M_Studio 的教程并不算很好。优点在于有一套完整的流程,并给你介绍每一个功能,尽量面面俱到。缺点在于功能介绍的很浅显,并且经常为了介绍而强行做某个功能,代码写的也不行。
相比之下,国外教程的优点在于功能介绍会附带理论比较深入一些,代码质量也不错。但是缺点在于要么就是完全分散的知识点,要么就是非常短的一个流程。
或者再极端点,官方手册,真的完全分散,但是绝对正确。
1. 鼠标控制人物移动

1.1 mouseManager

鼠标作为之后将要经常用到的操作(鼠标控制移动,控制攻击等等),作者选择在这里使用 mouseManager 来控制。
mouseManager 并不是 unity 自带的某个东西和定义,而是完全由我们自己创建的一个控制器:创建脚本,再创建一个物体,将脚本挂载到物体上。所谓的 mouseManager 只是作者自己的一个命名而已,完全可命名为 mouseController 或者 cameraController 等,只要看得懂,随你喜欢。
这里作者没有讲,因此我试图自己理解:没有选择直接挂在 player 上,而是选择挂载在一个单独的空物体上,是因为鼠标并不只是控制对人物的移动,还包括其他和人物不相关的操作。
1.1.1 事件

假设我们要做鼠标移动,那么可以想象成这么一个过程。鼠标点击触发事件 -> 人物移动。还记得之前做 UI 的时候使用的 UI button 里面自带的 on click 事件吗,那里需要你绑定一个 object,然后选一个这个 object 里的方法,到时候触发就会调用这个方法。事件就是这么个东西。
那么现在我们没有 UI button 配置好的事件属性,而是一个空物体,我们要如何使用事件呢?
可以想象,应该会有一个类似的一个有参数的属性或者方法,暴露出来后,在 unity 编辑器里看就会像是 UI button 显示的那个样子。

  • 首先我们要引入事件类的 namespace,叫做 UnityEngine.Events。
  • 新建一个继承 UnityEvent 的类,因为我们要使用参数。


3. 代码解释:UnityEvent 后面跟着的是之后这个监听函数要使用的参数,参数最多可以有 4 个,比如说UnityEvent<int, Vecctor2, string, Vecctor3>这样。然后创建的类继承这个类,像是例子中的 EventVector 是一个很好的名字,这样更容易区分类型。(注意,不要起类似 EventClick 这种代表动作的名字,因为这只是个类,以后所有只接收一个 Vector3 参数的事件都可以用这个类,并不一定是 click 相关。只有之后新建变量的时候才会起和动作相关的名字,而且命名方式也不是什么 eventClick,也应该是 onMouseClick 这种命名习惯。)(至于 eventMove 这种命名就更偏了,正确命名和用法应该是:继承了 EventVector3 的 onMouseClick 事件变量绑定一个叫做 move 的方法)。
4. 既然创建了这么一个类,接下来就是在 MouseManager 类里新建这个类型的变量,这样我们就可以在 inspector 里看到了。


5. 上面说的和真的一样,但是其实并没有看到。
6. 原因是数据格式问题,像是一些 int,string,gameObject 类型等确实可以被 unity 编辑器序列化并显示在 inspector 中。但是很可惜,unity 无法序列化泛型。而我们通过继承创建的类型又是高度自定义的(几乎有无限种参数组合的可能性)。因此需要我们手动序列化使其起作用。
7. 解决方法是在前面加上 [System.Serializable]





1.1.2 绑定鼠标点击动作

虽然我们起了个名字叫做 onMouseClick,但这只是个名字,我们要真正的把它和鼠标点击的动作联系起来。
1.1.2.1 Camera.ScreenPointToRay

顾名思义,这个方法的作用就是把屏幕点转化成射线。
具体含义就是,把屏幕上的某个点的位置(也就是一个 2 维的坐标),转化成从摄像机射到这个点的位置的射线。具体到游戏中就是,你点了某个地方,然后 unity 会自动计算你在屏幕上点的这个地方,对应在游戏中的三维坐标系的位置,然后从摄像机发射一条射线到这个位置,把射线返回给你。
可以在 unity 手册里找到这个方法,不过基本上是不说人话的。但理解了大概意思后可以在手册里找到一些细节,比如说它是怎么判断你点的是哪个物体的
1.1.2.1.1 global rotation vs local rotation4

题外话,去 scene 里看一下,选取 camera,调整左上角的 global 为 local。意思是工具调整的坐标系的角度是什么样的,下面两张图可以直观看到区别:




上图人都埋进土里了,平移工具还是一如既往保持和世界坐标一致。下图则是会根据人物的旋转改变角度。
1.1.2.1.2 继续之前的话题

这个方法虽然好,不过依然要你去提供屏幕点,并不能读取鼠标点击的位置。所以我们要通过 Input 给参数。
看文档可以知道这个方法返回一个 Ray 类型,因此我们创建一个变量来储存。




  • 私有变量命名使用 _ 前缀更容易区分。
  • Camera.main 获取当前场景中被 tag 为 MainCamera 的摄像机。
  • 虽然老师说直接在 update 里使用 MainCamera 的性能在 2020 版本后被优化过,但在 tarodev(ytb 上的一个频道) 的测试下缓存的速度依然要比不缓存快很多。
  • 使用 Input.mousePosition 获取鼠标位置。
1.1.2.2 Phsics.Raycast

不过射线只是射线,这条射线只是单纯从摄像机到你点的地方之间的一条线。但是实际上我们还需要知道这条线上有没有什么障碍物。想要获取碰撞物体的信息,就要使用物理方法。
那么这里使用 Phsics.Raycast 方法(看文档记得往下翻,有重载): 找到一种一个重载接受 ray 参数,并且有一个 out 修饰的 RaycastHit 类型的参数(RaycastHit 查看文档可以看到包含很多碰撞相关的信息),我们就使用这个:




  • out 修饰符作用可以查文档,总之就是让其变成引用传递,可以被修改。感觉是一个类似指针的概念。
  • 需要提前创建一个 Raycast 变量,作为参数来储存信息。
  • 方法调用的时候也要使用 out 修饰。




1.1.2.3 鼠标点击

刚刚虽然在 if 里没有放代码,但是 Phsics.Raycast 执行了,就会更改 _hitInfo 的信息。
此外,刚刚做的事情,是在每帧的时候,读取鼠标位置信息并拿到碰撞体的信息。不过我们其实没必要每一帧的信息都要。我们只是想在鼠标点击的时候需要。那么为什么刚刚要这么做呢,其实这是为了后面改变鼠标样式的时候需要(也就是在鼠标移到不同物体上,会变成不同的样子来告诉你这个物体是可以互动的)。
这就是为什么我们把 _hitinfo 放在外面,因为这样我们鼠标点击的时候就可以直接读取 _hitinfo 了。 新建一个 函数,用于处理之后的鼠标操作。





  • GetMouseButtonDown 的参数,0 指的是鼠标左键。
  • 如果射线没有碰撞,则 this._hitInfo.collider 返回 null。
  • 这里检测如果碰撞到的是地面,就会 invoke OnMouseClick 事件。
  • ?. 这个语法表明,只有前面不为空的时候才会进行后面的调用或者取值,这样避免了事先判断一次空值。可以参考文档。
  • 这里代码的含义是,每当点击并且碰撞体为地面的时候,执行一次 OnMouseClick,参数为碰撞点。
然后我们回到 Unity Editor,添加一个事件,属性设置为 NavMeshAgent.destination,NavMeshAgent 是下载素材自带的一个组件,也可以手动添加。看到 Nav 就大概明白这个和上一篇文章中的 Navigation 自动导航相关。而 destination 也顾名思义就是目的点。这里我们设置后,每次鼠标点击的时候,就会执行 NavMeshAgent.destination,不过由于 destination 是一个属性不是方法,所以此时 unity 执行的操作为将 destination 设置成 this._hitInfo.point 的值,然后自动导航系统就会把人物移动到对应位置。
2. 换教程

见前言。我是真的不理解为什么要用事件来做点击。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2022-5-5 13:09 | 显示全部楼层
不过仔细想想也还好,也不该奢求每次都能找到最适合的教程。在各种踩坑后找到适合自己的教程也算是正常的路线。
发表于 2022-5-5 13:19 | 显示全部楼层
同感啊同感[捂脸]
发表于 2022-5-5 13:23 | 显示全部楼层
过于吹毛求疵了,用法讲清楚就达成课程的作用了。像MouseManager之类的概念,哪怕稍微有点开发经验的人员也不至于搞不清楚。代码写出来,引用的命名空间,点进去就能很清楚的看出来是不是系统的类函数。语言基本功不合格,怪不到课程上。
发表于 2022-5-5 13:29 | 显示全部楼层
1. 我从来就没有怪罪过什么没告诉我 MouseManager 是不是系统的类函数。这个我一开始就看出来了,我写在文章里并不是因为我没看出来,是因为我假设一个观众正在看我的文章,并提醒一下他。所以并不是我的语言不合格,从而迁怒怪课程,希望这一点你可以理解。(也就是说这里我没有怪罪作者的意思)
2. 而且,就算我真没看懂。这也不是我不满的地方。我不满的地方一开始就说了,是因为强行使用某个特性来实现某个功能。因为我是一个 Unity 初学者,而这个教程也是初学者教程。这个教程每次用奇怪的方式来实现功能的时候也不说这合不合理,全靠我自己想。我很害怕什么时候不小心,以为教程教的这个是正常的方法,然后就走错路了。(下面的第6点的第①个理由里详细说了是怎么引起误会的)
3. 我比较相信一句话:“教一个什么都不懂的学生,比教一个走错了路的学生更简单”这句话我想你应该可以理解吧。我大概就是这个意思。当然我不是说教程故意教错人,我只是说有这种风险。
4. 我在前言的第三段话,以及正文的前两段话里也说了,并没有说课程多不好,只是不适合我,并且为了证明自己不是口嗨,我还仔细分析了优点和缺点。这几段话希望你能理解一下。
5. MouseManager 是最开始写的内容了。这个时候我对教程完全没有意见。我有意见的是后面开始讲事件的时候。你可以看一下文章的最后一句话。
发表于 2022-5-5 13:30 | 显示全部楼层
6. 当然,只看最后一句话看不出来啥,这整个后面使用事件来完成移动功能的这一节,我一直都处于不满的状态。主要由以下两点引起的:①为什么要用事件来做人物移动啊,我花了很长时间去想这一步的理由是什么,后来看了手册才发现根本不用这么做,当时就非常难受。你可以去看一下视频的1分45秒,老师是这么说的“因为移动和鼠标点击有关,因此这里要用事件”(我相信这句话听起来就像是在告诉你移动就是要用事件吧,这个绝对有足够引起误会的理由吧)(我希望教程能够告诉我这么做的理由,如果你要反驳我教程没必要告诉我理由的话,你可以回去看一下我的第 4 点,我只是说教程不适合我)②讲事件的时候,老师说事件这里绑定好物体后就可以使用组件上挂载的方法了。然后说“这里有一个 position 属性,我们可以xxx”。所以说好的方法呢?position 明明是属性啊。然后我又去搜文档,更懵逼了。有一个 SetPosition 方法,还有一个 Position 属性。调用前者或者改变后者都可以达到相同的效果。可是 Editor 里为什么只出现了后者,而且老师说了这里应该是方法,你一个属性怎么出现了。最应该出现的 SetPosition 怎么反而不出现了。百思不得其解。搜索也搜不出来。所以更加烦躁了。
发表于 2022-5-5 13:34 | 显示全部楼层
以及,老哥,如果你看完并能够理解我的话。希望你能告诉我我在第 6 点里提到的 position 和 SetPosition 的问题。我到现在也不知道,只能简单归纳为:①SetPosition 只能在代码里使用,无法出现在 editor 的事件绑定里。② editor 里也可以给事件绑定上属性而不是方法,只要类型一致。然后绑定属性的时候,事件触发的时候,就会把这个属性更改为参数值。
发表于 2022-5-5 13:44 | 显示全部楼层
woc,不好意思,打错了。以上所有的 position 换成 destination。不好意思啊。不过问题还是那个问题没有影响。
发表于 2022-5-5 13:44 | 显示全部楼层
具体截图我私信给你了,如果觉得麻烦也不用回。我自己再搜一下。
发表于 2022-5-5 13:47 | 显示全部楼层
哦对了,我好像忘了说是什么组件,是 NavMeshAgent 组件的 destination 和 SetDestination。
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-4-29 16:56 , Processed in 0.103443 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表