搭建Unity状态机行为类框架

前言:如果了解ScriptableObject 和 StateMachineBehaviour 类的话就直接跳转到最后一章好啦

ScriptableObject Class

ScriptableObject Class 的定义

翻译:“如果要创建独立于GameObject(游戏物体)的对象,则可以从中派生的类”

那游戏物体都有哪些?

在Hierarchy视图中RMC的这些都是(注意不是Project视图,区别后面会提到)如上图。

如何理解定义中的“独立于游戏物体“?ScriptableObject类直接派生自Object类,同直接派生自Object的两大类还有Component和GameObject,可知继承自该类的脚本既不能实例化为游戏物体,也不能作为组件依赖于游戏物体。

数据资源文件

Unity的资源包括两大形式:Asset与Object(UnityEngine.Object)。实际上,Object是Asset的实例,所以我们可以说Unity中的所有资源都是以Asset形式存在的

如何体现这句话:

Project面板内RMC—Create你能看到很多资源文件,Prefab为后缀的预制体资源文件实例是游戏物体,.cs为后缀的脚本资源文件实例是脚本组件(通过在脚本中测试this即该类的实例可知)也可以是组件所需要的资源Textures(根据图片格式决定,通常文件后缀为.png因为可以透明化通道),AudioSource(根据音频格式决定,通常为.mp3)。

并非所有Asset都能实例化为Object,但Object一定是Asset

从Object派生关系图也可知Input,Time,Resources都没有继承Object类

ScriptObjectable class 的作用

从类声明中可知该类不是一个抽象类,那其一定具有实例化的能力

该类的实例其实是数据资源文件,他的图标长如下图,文件后缀名是.asset。这个后缀名意味着其可以包含Unity中所有的资源和所有基础数据类型和数据结构类型的数据(因为C#中的所有元素都继承自Object,C#的单根性,而Object是Asset的实例)

因此第一个作用:数据复用

变量(variable)复用存储有数据类型的数据

方法复用并存储代码块

数据文件是将脚本中的所有代码存储并复用。如何使用后面结合StateMachine我会给出例子

数据持续化

在运行游戏过程中变量的更改是在内存中的修改,一旦停止运行会恢复到初始值,要想在本地运行游戏的过程中更改数据并在运行结束后保留更改即将数据存储在硬盘而非内存上(好比在函数用ref/out在方法体调用结束后仍保留修改值)就需要修改.asset文件中根据定义创建的数据文件中的变量。但如果发布游戏的话,仍无法保存修改的数据,要用到数据库相关的知识,将来再讲,挖个坑

并且在创建预制体,和很多游戏物体使用同一个脚本时引用.asset数据资源文件中的变量而非在脚本中单独声明一份变量可以避免浪费内存,通常我们是给预制体挂载脚本,但这样每创建一个预制体,其脚本中定义的变量都会分配一次内存(复制一份所有脚本中的变量),如图:

但若预制体的脚本中的变量全部由继承自ScriptObjectable类的脚本中定义,所有创建的预制体都是引用.asset文件中的变量,这样只有一套变量存在于内存当中,预制体脚本中存储的是.asset文件中变量的引用。

第三个作用是关于配置文件,涉及编辑器相关的知识,挖个坑

如何实例化派生自ScriptObject类的脚本文件为数据资源文件?

方式1:最常用的是通过在脚本文件中添加CreatAssetMenu特性

PS:我怀疑该Attribute是专门为数据资源文件使用的,看文档描述就知道了。

用法:[CreateAssetMenuAttribute(fileName = "XXX“,menuName = "XXX",order = a)]

方式2:ScriptableObject类自带的静态实例化方法CreatInstance<T>()

步骤:

1.在Asset文件夹下创建任意命名为Editor的文件夹,并在该文件夹下创建脚本文件(unity会找到所有命名为Editor的文件夹无论有多少个在哪里,打包游戏时unity不会将名为Editor文件夹内的文件打包。如果没放在 Editor 文件夹下,Unity 打包时会将其一起打包,作用于游戏运行期间,与 Editor命名空间的性质相矛盾,会报错。

2.创建用于存储数据资源文件的文件夹:创建Resources文件夹,创建其子文件夹ScriptableObject

3.脚本文件中定义数据文件的模板

4.Editor文件夹内脚本完成实例化的相关逻辑

如何使用ScriptObjectable class中的数据

由于其本质也是资源文件,因此使用方式为加载资源文件通法

方式1:在脚本中公开声明派生自ScriptObjectable class类的集合或变量,在脚本外拖拽引用,注意拖拽的是脚本文件的实例即数据资源文件(.Asset为后缀的文件)不是脚本文件(.cs为后缀的文件)

方式2:将数据资源文件(不是脚本文件)放到Resources资源文件夹下,通过Resources.Load查找。

或者Addressbundle和Addressables相关的方法

StateMachineBehaviour Class

翻译:StateMachineBehaviour 是一个可添加到状态机状态的组件。它是一个基类,所有状态脚本都派生自该类。

 游戏物体添加组件需要继承Component类                                                                                     状态机中状态添加组件就需要继承StateMachineBehaviour类

该类还自带一些由unity调用的函数和消息:

如何搭建

游戏物体是由组件构成的

例如:空物体 + 摄像机组件就成为摄像机游戏物体,换言之不同的游戏物体本质就是不同组件的组合,Unity为我们封装好了许多组件,他们都有着各自不同的功能,但如果我们的需求没有对应的已定义好的组件就需要我们用脚本组件自己实现所需的组件。

但是当我们对Animator中动画片段想通过添加组件实现各种需求时发现只有自定义组件”脚本组件“一种

当我们做动作状态机,假设有奔跑跳跃,原地普通跳跃,原地蓄力条约,空中二段跳跃,及未来可能会添加的许许多多的跳跃动作,他们都有共同需要的功能:“落地检测,空中状态检测”。这些功能也都有共同需要的获取的数据:“跳跃键等各种按键的按下状态,animator、rigidbody、transform,collider的引用......"

来看看你是什么段位吧(*^_^*)

青铜:每一个跳跃片段都写一个对应的脚本(继承自StateMachineBehaviour,不懂的跳转对应的章节),把所有该片段所需的字段声明和功能都写该脚本中,脚本名就叫所挂载的动作片段名,公开需要修改的变量,替换方法内可能变化的地方。

实现了开闭原则,不错哦!但是功能多了几千行不好维护。复用性也差强人意,脚本内的许多功能其他动作片段也需要,可你毫无办法只能再写一遍


黄金:将功能拆分开,每一个功能写一个对应的脚本,按照不同功能写脚本而不是对象的不同,跳跃片段需要什么功能就添加对应的脚本组件

功能组件化,干得不错,容易维护,复用性也好。但每个组件之间都有大量重复获取的变量,不但你写起来烦,内存也觉得很不爽,相同的变量你重复存储那么多次,我快装不下啦!

每个脚本前你都会写上这几句,没错说的就是你!


钻石:声明一个InputController脚本,将所有你需要大量复用的变量存储进去并公开,其他脚本需要什么变量就通过InputController脚本对象访问获取,功能脚本中只声明自己独特需要获取的变量,比如二段条需要声明一个bool类型判断是否二次按下跳跃键的变量,这个变量其他功能脚本完全不需要,该变量就不应声明在InputController脚本中。

现在你写起来非常爽,每个功能组件所需要获取的变量从几十甚至几百骤降到几个,每个动作片段需要什么功能就把对应的脚本组件挂载上去,但是内存依旧觉得不爽,你在n个动作片段上挂载了相同的组件,内存中存储了n个相同的脚本组件中获取或声明的字段和功能代码,而其实只需要一个功能组件,在所需要的动作片段上获取其引用就可以了(不懂得先看ScriptObject的作用)


王者:我们需要额外声明2个类:派生自ScriptableObject类的抽象类StateComponent类,派生自StateMachineBehaviour的StateComponentManager类。

现在功能脚本文件继承了StateComponent类其又继承了ScriptableObject类,功能脚本文件必须实例化为数据资源文件才可以使用,好处当然也就是解决了脚本文件复制多份的问题,因为所有实例化的数据资源文件,都只是该脚本文件的引用,你无论给多少个动作片段使用该数据文件,内存中也只存在一份。数据资源文件本身不能作为组件挂载在游戏物体/动画片段上,因此需要一个载体StateComponentManager去使用这些数据文件,在之中定义一个公开的ScriptableObject类型的泛型集合。

使用方法:每个脚本组件实例化为数据资源文件,在每个需要添加功能的动画片段下先添加一个StateComponentManager组件,将需要为该动画片段添加的功能脚本对应的数据资源文件拖入StateComponentManager组件下的集合中,不同动画片段的相同脚本功能需要微调的就需要在功能脚本中公开需要更改的部分,然后创建两个数据资源文件挂载在对应的动画片段下就好啦

内存非常高兴,你写起来也非常的爽, 符合依赖倒转原则,你的合作伙伴也非常的高兴!

参考文章:

Unity进阶:ScriptableObject使用指南-CSDN博客

擅码网动作进阶收费课程

Unity 资源管理系统的理解 - 知乎 (zhihu.com)

  • 11
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值