zoukankan      html  css  js  c++  java
  • [UE4]事件处理(Handling Events)和委托(Delegate)代码示例(二)【C++】

    3. 创建带参数的委托

    我们可以通过修改委托的签名来使其接受参数

    比如我们需要接受一个参数的话,可以在 GameMode 中这样声明:

    DECLARE_DELEGATE_OneParam(FParamDelegateSignature, FLinearColor)  

    注意:这个宏与之前稍有不同,后缀多出了一个 _OneParam ,而且我们还需要指定接受参数的类型——本例为 FLinearColor

    接着再添加一个 FParamDelegateSignature 成员 

    FParamDelegateSignature MyParameterDelegate;     

    这和之前一样,创建一个委托实例作为 GameMode 成员


    然后创建一个 Actor 类,取名为 ParamDelegateListener,

    在头文件中添加以下声明

    UFUNCTION()  
    void SetLightColor(FLinearColor LightColor);  
      
    UPROPERTY()  
    UPointLightComponent* PointLight;  

    ParamDelegateListener.cpp 

    #include "Test.h"  
    #include "UE4TestGameMode.h"  
    #include "ParamDelegateListener.h"  
      
      
    // Sets default values  
    AParamDelegateListener::AParamDelegateListener()  
    {  
        // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.  
        PrimaryActorTick.bCanEverTick = true;  
        PointLight = CreateDefaultSubobject<UPointLightComponent>("PointLight");  
        RootComponent = PointLight;  
      
    }  
      
    // Called when the game starts or when spawned  
    void AParamDelegateListener::BeginPlay()  
    {  
        Super::BeginPlay();  
        UWorld* TheWorld = GetWorld();  
        if (TheWorld != nullptr)  
        {  
            AGameMode* GameMode = Cast<AGameMode>(UGameplayStatics::GetGameMode(TheWorld));  
            AUE4TestGameMode * MyGameMode = Cast<AUE4TestGameMode>(GameMode);  
            if (MyGameMode != nullptr)  
            {  
                // Binds a UObject-based member function delegate. UObject delegates keep a weak reference to your object. You can use ExecuteIfBound() to call them.(注意绑定的还是 UFUNCTION)  
                MyGameMode->MyParameterDelegate.BindUObject(this, &AParamDelegateListener::SetLightColor);  
            }  
        }  
      
    }  
      
    // Called every frame  
    void AParamDelegateListener::Tick( float DeltaTime )  
    {  
        Super::Tick( DeltaTime );  
      
    }  
    // 1个参数  
    void AParamDelegateListener::SetLightColor(FLinearColor LightColor)  
    {  
        PointLight->SetLightColor(LightColor);  
    }  

    回到 MyTriggerVollume.cpp,在 NotifyActorBeginOverlap 函数中添加以下代码:

    MyGameMode->MyParameterDelegate.ExecuteIfBound(FLinearColor(1, 0, 0, 1));    // 带一个参数  

    与之前不同的是,我们需要多指定一个参数,参数类型和我们之前的委托声明一致。

    显然,MyTriggerVolume 压根就无需知道 ParamDelegateListener 的存在,却通过 GameMode 就可以调用 ParamDelegateListener 的函数了,很大程度上降低了类间的耦合度。

    解绑委托方式与之前相同,不再赘述。

    4.通过委托绑定传递负载数据(Payload Data)

    稍加修改,我们就可以在委托被调用时传递额外创建时的参数(additional creation-time parameter),即我们在 MyTriggerVolume 中的调用方式不变,仍然是 ExecuteIfBound(FLinearColor(1, 0, 0, 1)),但可以额外添加一些负载数据,在 ParamDelegateListener 中的 BindUObject 上添加。

    首先修改 AParamDelegateListener::BeginPlay 中的 BindUObject,为其添加一个 bool 负载数据

    MyGameMode->MyParameterDelegate.BindUObject(this, &AParamDelegateListener::SetLightColor, false);  

    并修改 SetLightColor 的定义

    UFUNCTION()  
    void SetLightColor(FLinearColor LightColor, bool EnableLight);  
    // 2个参数  
    void AParamDelegateListener::SetLightColor(FLinearColor LightColor, bool EnableLight)  
    {  
        PointLight->SetLightColor(LightColor);  
        PointLight->SetVisibility(EnableLight);  
    }  

    注意:负载数据并不局限于带参数的委托,其他的委托形式也可以使用

    5. 多播委托(Multicast Delegate)

    之前说的委托,都是只绑定了一个函数指针,而多播委托绑定的是一个函数指针集合,每个函数指针都有对应的一个委托句柄,当广播(Broadcast)委托的时候,他们将会被激活。

    首先在 GameMode 中添加多播的委托声明

    需要明确声明为多播

    DECLARE_MULTICAST_DELEGATE(FMulticastDelegateSignature)  

    接着在类中声明一个 FMulticastDelegateSignature 成员

    FMulticastDelegateSignature MyMulticastDelegate;      

    其次,创建一个新 Actor 类,命名为 MulticastDelegateListener

    在其头文件中添加以下声明:

    FDelegateHandle MyDelegateHandle;  
      
    UPROPERTY()  
    UPointLightComponent* PointLight;  
      
    UFUNCTION()  
    void ToggleLight();  
      
    virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;  

    大部分和之前的 Listener 类很相似,但是多一个 委托句柄实例,将用它来存储委托实例的引用,我们的添加(AddUObject)和移除(Remove)都需要它作为参数

     源文件的代码如下:

    // Sets default values  
    AMulticastDelegateListener::AMulticastDelegateListener()  
    {  
        // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.  
        PrimaryActorTick.bCanEverTick = true;  
        PointLight = CreateDefaultSubobject<UPointLightComponent>("PointLight");  
        RootComponent = PointLight;  
      
    }  
      
    // Called when the game starts or when spawned  
    void AMulticastDelegateListener::BeginPlay()  
    {  
        Super::BeginPlay();  
        UWorld* TheWorld = GetWorld();  
        if (TheWorld != nullptr)  
        {  
            AGameMode* GameMode = Cast<AGameMode>(UGameplayStatics::GetGameMode(TheWorld));  
            AUE4TestGameMode * MyGameMode = Cast<AUE4TestGameMode>(GameMode);  
            if (MyGameMode != nullptr)  
            {  
                // Adds a UObject-based member function delegate. UObject delegates keep a weak reference to your object.  
                // 注册一个对象方法  
                MyDelegateHandle = MyGameMode->MyMulticastDelegate.AddUObject(this, &AMulticastDelegateListener::ToggleLight);  
            }  
        }  
      
    }  
      
    // Called every frame  
    void AMulticastDelegateListener::Tick( float DeltaTime )  
    {  
        Super::Tick( DeltaTime );  
      
    }  
      
    void AMulticastDelegateListener::ToggleLight()  
    {  
        PointLight->ToggleVisibility();  
    }  
      
    void AMulticastDelegateListener::EndPlay(const EEndPlayReason::Type EndPlayReason)  
    {  
        Super::EndPlay(EndPlayReason);  
        UWorld* TheWorld = GetWorld();  
        if (TheWorld != nullptr)  
        {  
            AGameMode* GameMode = Cast<AGameMode>(UGameplayStatics::GetGameMode(TheWorld));  
            AUE4TestGameMode * MyGameMode = Cast<AUE4TestGameMode>(GameMode);  
            if (MyGameMode != nullptr)  
            {  
                // Removes a function from this multi-cast delegate's invocation list (performance is O(N)). Note that the order of the delegates may not be preserved!  
                MyGameMode->MyMulticastDelegate.Remove(MyDelegateHandle);  
            }  
        }  
    }  

    MyTriggerVolume.cpp 的实现为:

    // Broadcasts this delegate to all bound objects, except to those that may have expired.  
    MyGameMode->MyMulticastDelegate.Broadcast();  

    广播函数很像我们之前的 ExecuteIfBound函数,但有一点不同,它不需要检查是否有函数绑定在委托上。

    最后的效果是,如果我们往场景中拖放了四五个MulticastDelegateListener,当我们进入触发区域,它们的灯会同时打开或关闭,因为每个实例函数都被添加到委托集合当中;

    如果拖放了四五个DelegateListener 到场景中,当我们进入触发区域,只有最后一个拖进场景的灯会亮,这是因为委托只绑定了最后一个实例函数。

  • 相关阅读:
    JAVA学习总结-基础语法
    git stash save -a 遇到的坑 , 弹出匿藏错误
    TP5模型belongsTo和hasOne这两个方法的区别
    phpstorm设置的快捷键突然失效了,提示: IdeaVim ...
    layui 时间选择器 不要秒的选项
    SQL 判断表是否存在 数据表不存在是致命错误
    layui 第三方组件 eleTree 树组件 树形选择器
    tp5 ThinkPHP5 自定义异常处理类
    TP5隐藏url中的index.php
    phpstorm断点调试 php.ini 文件中 Xdebug 配置
  • 原文地址:https://www.cnblogs.com/timy/p/8682692.html
Copyright © 2011-2022 走看看