管理GameObject中组件之间的状态和事件
链接到项目众所周知,或多或少熟悉Unity平台,每个
GameObject游戏对象
均由组件(内置或自定义,通常称为“脚本”)组成。 组件从基类
MonoBehavior继承 。

通常,经常或经常或直接建立链接以绑定组件。

即 在一个组件中,要从另一个组件获取数据,我们可以使用
GetComponent <...>()方法来获取另一个组件,例如:

在此示例中,对
SomeComponent类型的组件的
引用将放置在变量
someComponent中 。
使用这种“紧密耦合”的方法,尤其是在存在大量组件的情况下,很容易混淆并保持这种连接的完整性。 例如,如果一个组件中的属性或方法的名称发生了变化,那么您将必须在使用该组件的所有组件中对其进行修复。 这就是出血。
下切了很多照片根据组件的“强连通性”创建解决方案
当我们有某些组件并且每个组件相互引用时,我们将创建一个空项目来重现通常的情况,以接收数据或进行控制。

我添加了两个脚本
FirstComponent和
SecondComponent ,它们将用作游戏对象中的组件:

现在,我将为实验所需的每个组件定义一个简单的结构。


现在想象一下一种情况,我们需要从
FirstComponent组件获取
state1字段的值,并在
SecondComponent组件中调用其
ChangeState (...)方法。 为此,您需要获取到该组件的链接并在
SecondComponent组件中请求必要的数据:

在控制台中启动游戏后,将看到我们从
SecondComponent从
FisrtComponent接收了数据并更改了第一个的状态

现在,以完全相同的方式,我们可以从
FirstComponent组件
以相反的方向获取数据,
以获取
SecondComponent组件的数据。

开始游戏后,我们将可以接收数据,并且可以从
FirstComponent控制
SecondComponent组件。

这是一个非常简单的示例,要了解我要描述的问题类型,有必要使所有组件的结构和关系大大复杂化,但是含义很明确。 现在,组件之间的连接如下:


如果新组件需要与现有组件进行交互,则甚至用一个新组件扩展一个游戏对象也将是常规操作。 特别是,例如,如果
FirstComponent组件中的
state1字段的名称更改为例如
state_1,并且您必须更改在所有组件中使用的名称,则尤其如此。 或者,当组件具有太多字段时,导航它们将变得非常困难。
根据组件之间的“一般状态”创建解决方案
现在想象我们不需要链接到每个感兴趣的组件并从中获取数据,而是会有一个特定的对象包含游戏对象中所有组件的状态和数据。 在图中,它看起来像这样:

常规状态或常规状态对象(SharedState)也是一个组件,它将充当服务组件的角色并存储游戏对象所有组件的状态。
我将创建一个新组件并将其命名为SharedState:

然后,我将确定该通用组件的代码。 它将存储一个封闭的字典和索引器,以更方便地使用组件字典,它也将被封装,并且不能直接与其他组件的字典一起使用。

现在,需要将此组件放置在游戏对象上,以便其他组件可以访问它:

接下来,您需要对
FirstComponent和
SecondComponent组件进行一些更改,以便它们使用
SharedState组件存储其状态或数据:


从组件代码中可以看到,我们不再存储字段,而是使用常规状态,并可以使用键“ state1”或“ counter”访问其数据。 现在,此数据不再与任何组件相关联,并且如果出现第三个组件,则可以访问SharedState,他将能够访问所有这些数据。
现在,为了演示此方案的操作,您需要更改两个组件中的Update方法。 在
FisrtComponent中 :

在
SecondComponent组件中:

现在,这些组件不知道这些值的来源,也就是说,在它们转向某个特定组件以获取它们之前,现在它们仅存储在公共空间中,任何组件都可以访问它们。
开始游戏后,您可以看到组件获得了所需的值:

现在我们知道了它是如何工作的,我们可以派生用于访问基类中的常规状态的基本基础结构,从而不必在每个组件中分别完成所有操作:

并且我将其抽象化,以免意外创建它的实例...并且建议添加一个属性,指示该基本组件需要
SharedState组件:

现在,您需要更改
FirstComponent和
SecondComponent组件,以便它们从
SharedStateComponent继承并删除所有不必要的组件:


好啦 调用方法呢? 建议不要直接执行此操作,而应通过发布者-订阅者模式执行此操作。 简化了。
要实现这一点,您需要添加另一个通用组件,类似于包含数据的组件,除了该组件将仅包含订阅并将被称为
SharedEvents之外 :

原理如下。 一个想要在另一个组件上调用某种方法的组件将不会直接执行此操作,而是通过调用事件(仅按名称),因为我们从常规状态获取数据。
每个组件都订阅一些可以跟踪的事件。 并且,如果他捕获到此事件,则将执行在组件本身中定义的处理程序。
创建
SharedEvents组件:

我们将定义管理订阅和发布所需的结构。

为了在订阅,发布之间交换数据,定义了一个基类,将为每个事件的作者独立确定一个特定的基类,然后定义几个示例:

现在,您需要向游戏对象添加一个新组件:

并
稍微扩展
SharedStateComponent基类,并添加对象包含
SharedEvents的要求

除了一般状态对象之外,还必须从游戏对象中获取一般订阅对象:


现在,我们定义一个事件订阅,将在
FisrtComponent中处理该事件,并定义一个用于通过此类事件传输数据的类,并更改
SecondComponent以便发布该订阅的事件:


现在,我们已经订阅了
FirstComponent组件中称为“ writesomedata”的任何事件,并在发生时简单地
将消息
打印到控制台。 在本示例中,它是通过在
SecondComponent组件中调用名称为“ writesomedata”的事件的发布并传输一些可在该组件中使用该名称捕获事件的信息来产生的。
在5秒钟内开始游戏后,我们将在
FirstComponent中看到处理事件的
结果 :

总结
现在,如果您需要扩展此游戏对象的组件(还将使用常规状态和常规事件),则需要添加一个类,并简单地从
SharedStateComponent继承:
主题继续