这篇博客介绍了UE4中的Multi-Cast Delegate的使用方法以及如何使用Multi-Cast Delgate实现观察者模式。
Delegate所起的作用是把一个对象与其函数绑定起来,从而产生一个可以全局调用的函数。在C#中已经内置了这样的功能,但是在C++ 11中似乎并没有一个很官方的实现。针对于在C++ 11中实现Delegate,可以参考这两篇文章:Implementation of Delegates in C++11,The Impossibly Fast C++ Delegates
UE4针对于Delegate做了封装,目前是使用宏来实现的(可能是由于历史包袱?),可以实现高达8个变量的设定。关于UE4中的Delegate,对应的文档已经讲的很清楚了,传送门。
观察者模式是个很有用的设计模式。简短概括来说,观察者模式指的就是当被观察者改动后,会触发这个被观察者的所有观察者的一个操作。具体设计模式的介绍和实现可以参考Head First Design Patterns和Game Programming Patterns。
应该来说,Multi-Cast Delegate本来就是为了观察者模式而设计的,可以通过以下的方式来设计对应的观察者和被观察者。
首先需要声明一个对应的Multi-Cast Delegate
,用于管理观察者的事件:
DECLARE_MULTICAST_DELEGATE_TwoParams(OnSomethingChanged, float previousHP, float currentHP);
需要注意的是,UE4中的Delegate的声明是使用宏来实现的,所以OnSomethingChanged不需要引号或者TEXT("OnSomethingChanged")
之类的操作。
之后就可以在类中声明对应的Delegate变量了,以下的例子是用于当主角的生命值改变的Delegate:
class AMainPlayerState: public APlayerState
{
...
OnSomethingChanged onHPChanged;
}
此外,还需要一个入口,用于当生命值改变时候进行触发的操作:
void ChangeHP()
{
// Do something important
onHPChanged.Broadcast(previousHP, currentHP);
}
观察者Observer可以是任何东西 —— 可以是一个类,可以是全局上下文。我们可以把其绑定到某个Object的函数,也可以将其绑定到某个全局静态函数。
以下的例子是用于当玩家生命值改变时,针对血条UI的操作,首先,在BeginPlay()
方法中,将其注册为玩家血量的观察者:
void UPlayerHPUserWidget::BeginPlay()
{
Super::BeginPlay();
hpDelegateHandle = _mainPlayerState->onHPChanged.AddUObject(this, &UPlayerHPUserWidget::ReceiveHPChanged);
}
这里,ReceiveHPChanged
是一个UPlayerHPUserWidget
的函数,接受两个float
参数,类型为void
。
此外,当该UI被销毁的时候,需要将这个widget从观察者列表里删除掉:
void UPlayerHPUserWidget::Destroyed()
{
mainPlayerState->onHPChanged.Remove(hpDelegateHandle);
}
由于观察者列表采用的是链表的操作,所以Remove()
操作的时间复杂度是
O(n)
,代码注释中提到原有的Delegates的先后触发顺序可能会改变,这里需要注意一下。
设计模式这个东西虽然从大三就开始看了,但是也是到了接触商业项目之后才能真正开始体会到设计模式的神髓。C++也不像Java或者C#一样在语言层就内置了各种设计模式,很多都得自己去实现。真真是学无止境啊~~~
<全文完>