UE中委托的使用很广泛,许多Event的触发都有对应的虚函数和委托,虚函数不用讲,只能在派生类中使用,而委托可以在别的类或者蓝图中使用,就应用范围而言,委托的使用更灵活。以AActor的

  1. /**
  2. * Event when this actor overlaps another actor, for example a player walking into a trigger.
  3. * For events when objects have a blocking collision, for example a player hitting a wall, see 'Hit' events.
  4. * @note Components on both this and the other Actor must have bGenerateOverlapEvents set to true to generate overlap events.
  5. */
  6. virtual void NotifyActorBeginOverlap(AActor* OtherActor);
  7. /**
  8. * Event when this actor overlaps another actor, for example a player walking into a trigger.
  9. * For events when objects have a blocking collision, for example a player hitting a wall, see 'Hit' events.
  10. * @note Components on both this and the other Actor must have bGenerateOverlapEvents set to true to generate overlap events.
  11. */
  12. UFUNCTION(BlueprintImplementableEvent, meta=(DisplayName = "ActorBeginOverlap"), Category="Collision")
  13. void ReceiveActorBeginOverlap(AActor* OtherActor);
  14.  
  15. /**
  16. * Event when an actor no longer overlaps another actor, and they have separated.
  17. * @note Components on both this and the other Actor must have bGenerateOverlapEvents set to true to generate overlap events.
  18. */
  19. virtual void NotifyActorEndOverlap(AActor* OtherActor);

为例,重叠(overlapped)事件发生时,就会触发这组函数(Begin/EndOverlap),但仅限于在派生类中进行重载。其实,AActor提供了另外一种更加灵活的方式,那就是:委托(Delegate)

  1. /**
  2. * Called when another actor begins to overlap this actor, for example a player walking into a trigger.
  3. * For events when objects have a blocking collision, for example a player hitting a wall, see 'Hit' events.
  4. * @note Components on both this and the other Actor must have bGenerateOverlapEvents set to true to generate overlap events.
  5. */
  6. UPROPERTY(BlueprintAssignable, Category="Collision")
  7. FActorBeginOverlapSignature OnActorBeginOverlap;

当然也有对应的End形式。查看FActorBeginOverlapSignature的定义:

  1. // Delegate signatures
  2. DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams( FTakeAnyDamageSignature, AActor*, DamagedActor, float, Damage, const class UDamageType*, DamageType, class AController*, InstigatedBy, AActor*, DamageCauser );
  3. DECLARE_DYNAMIC_MULTICAST_DELEGATE_NineParams( FTakePointDamageSignature, AActor*, DamagedActor, float, Damage, class AController*, InstigatedBy, FVector, HitLocation, class UPrimitiveComponent*, FHitComponent, FName, BoneName, FVector, ShotFromDirection, const class UDamageType*, DamageType, AActor*, DamageCauser );
  4. DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FActorBeginOverlapSignature, AActor*, OverlappedActor, AActor*, OtherActor );
  5. DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FActorEndOverlapSignature, AActor*, OverlappedActor, AActor*, OtherActor );
  6. DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams( FActorHitSignature, AActor*, SelfActor, AActor*, OtherActor, FVector, NormalImpulse, const FHitResult&, Hit );
  7.  
  8. DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam( FActorBeginCursorOverSignature, AActor*, TouchedActor );
  9. DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam( FActorEndCursorOverSignature, AActor*, TouchedActor );
  10. DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FActorOnClickedSignature, AActor*, TouchedActor , FKey, ButtonPressed );
  11. DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FActorOnReleasedSignature, AActor*, TouchedActor , FKey, ButtonReleased );
  12. DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FActorOnInputTouchBeginSignature, ETouchIndex::Type, FingerIndex, AActor*, TouchedActor );
  13. DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FActorOnInputTouchEndSignature, ETouchIndex::Type, FingerIndex, AActor*, TouchedActor );
  14. DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FActorBeginTouchOverSignature, ETouchIndex::Type, FingerIndex, AActor*, TouchedActor );
  15. DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FActorEndTouchOverSignature, ETouchIndex::Type, FingerIndex, AActor*, TouchedActor );
  16.  
  17. DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FActorDestroyedSignature, AActor*, DestroyedActor );
  18. DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FActorEndPlaySignature, AActor*, Actor , EEndPlayReason::Type, EndPlayReason);
  19.  
  20. DECLARE_DELEGATE_SixParams(FMakeNoiseDelegate, AActor*, float /*Loudness*/, class APawn*, const FVector&, float /*MaxRange*/, FName /*Tag*/);

其中DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams,表明这是一个多播。在UE中,委托有单播和多播两种方式,另外还有委托的函数带不带返回值(_RetVal后缀),单播和多播均为无返回值(函数返回类型为void)。

一、单播:

使用如下定义(这里只使用无参和一个参数两种类型,别的类似):

  1. //声明委托(单播),无参数
  2. DECLARE_DELEGATE(MyDelegate)
  3. DECLARE_DELEGATE_OneParam(MyIntDelegate, int32)

这样就声明了两种委托类型,MyDelegate(无参数),MyIntDelegate(带有一个int32参数类型),用法如下:

1)声明和实现回调函数,注意:一定要加UFUNCTION修饰函数声明,因为委托是参与UE的反射系统中的,即UE引擎要知道有这个函数,这在下面的例子中会看到,声明和实现如下:

  1. UFUNCTION()
  2. void IntFunction(int32 Value)
  3. {
  4. GLog->Log(ELogVerbosity::Warning, "IntFunction: " + FString::FromInt(Value));
  5. }
  6.  
  7. UFUNCTION()
  8. void SecondIntFunction(int32 Value)
  9. {
  10. GLog->Log(ELogVerbosity::Warning, "SecondIntFunction: " + FString::FromInt(Value));
  11. }
  12. UFUNCTION()
  13. void ThirdIntFunction(int32 Value)
  14. {
  15. GLog->Log(ELogVerbosity::Warning, "ThirdIntFunction: " + FString::FromInt(Value));
  16. }
  17.  
  18. UFUNCTION()
  19. void SomeFunction()
  20. {
  21. GLog->Log(ELogVerbosity::Warning, "SomeFunction: ");
  22. }

使用方法:

随便找个地方测试,这里在BeginPlay()中测试:

  1. MyIntDelegate IntDelegate;
  2. IntDelegate.BindUFunction(this, FName("IntFunction"));
  3. IntDelegate.BindUFunction(this, FName("SecondIntFunction"));

单播,只能触发一个委托,因此上面虽然绑定了两个函数IntFunction和SecondIntFuction,但是只有第2个函数(SecondIntFunction)得到调用

  1. IntDelegate.Execute(999);

在引擎的日志中显示如下:

注意到一个有趣的地方,我们绑定函数时,不是通过函数的名字来绑定的,而是通过函数的名字对应的FName来绑定的,这是说明(或许称推测更准确),这些函数在UE引擎中通过某种方式(VM更有可能)有函数和其对应名称字符串的一个映射,这也是前面在函数声明处添加UFUNCTION修饰的原因(告诉UHT要把这个函数添加进反射系统)。另外一个有趣的地方是调用委托(IntDelegate.Execute(999))时,VC会自动推导出参数类型(VS2017测试)为int32。

二、多播:多播时可以触发多个委托的函数,多播带有Multicast这样的字符串,这很容易和单播区分出来。多播就意味着可以触发多个事件,如下:

  1. GLog->Log(ELogVerbosity::Warning, TEXT("将要进行多播……。"));
  2. MyIntMulticastDelegate IntMulticastDelegate;
  3. IntMulticastDelegate.AddUFunction(this, FName("IntFunction"));
  4. IntMulticastDelegate.AddUFunction(this, FName("SecondIntFunction"));
  5. IntMulticastDelegate.AddUFunction(this, FName("ThirdIntFunction"));
  6. IntMulticastDelegate.Broadcast(123);

注意,多播要使用Broadcast触发,Broadcast就意味着广播,广播嘛,大家只要愿意,都能收到,这也正是观察者(Observer)模式。

运行结果:

三、扩展:

既然引入时使用了重叠的例子,这里把这个事件再注解下,老了,记性不好:(

  1. void AActor::NotifyActorBeginOverlap(AActor* OtherActor)
  2. {
  3. // call BP handler
  4. ReceiveActorBeginOverlap(OtherActor);
  5. }

该函数是虚函数,调用一个ReceiveActorBeginOverlap函数,ReceiveActorBeginOverlap原型在引入中有原型,看下说明知道,这里其实就是调用蓝图中对应的OnActorBeginOverlap结点(NODE)。所以重载时要注意,如果需要调用蓝图中的事件(通常如此)要显式调用Super::NotifyActorBeginOverlap(OtherActor)。调用的位置,即先于自定义代码,或者在自定义代码后调用,要根据自己的情况,例如,如果在派生类中做些初始化,以供蓝图使用,则应先调用自己的代码,后调用父类,否则,无所谓。

四,进一步说明重叠事件:

在前方中提及,AActor是不参与物体的collide的,这里又有重载,又有多播,这不是打自己的脸?其实不是。AActor是容器!AActor是容器!AActor是容器!所以功能的实现都是通过组件实现的。在前方中说明碰撞(collide,当然包括重叠和Hit,唔,,Hit叫撞击听着不顺就Hit好了)是在UPrimitiveComponent中实现,这里把它扒出来,晾晾。

  1. // @todo, don't need to pass in Other actor?
  2. void UPrimitiveComponent::BeginComponentOverlap(const FOverlapInfo& OtherOverlap, bool bDoNotifies)
  3. {
  4. // If pending kill, we should not generate any new overlaps
  5. if (IsPendingKill())
  6. {
  7. return;
  8. }
  9.  
  10. // UE_LOG(LogActor, Log, TEXT("BEGIN OVERLAP! Self=%s SelfComp=%s, Other=%s, OtherComp=%s"), *GetNameSafe(this), *GetNameSafe(MyComp), *GetNameSafe(OtherComp->GetOwner()), *GetNameSafe(OtherComp));
  11.  
  12. UPrimitiveComponent* OtherComp = OtherOverlap.OverlapInfo.Component.Get();
  13.  
  14. if (OtherComp)
  15. {
  16. bool const bComponentsAlreadyTouching = IsOverlappingComponent(OtherOverlap);
  17. if (!bComponentsAlreadyTouching && CanComponentsGenerateOverlap(this, OtherComp))
  18. {
  19. AActor* const OtherActor = OtherComp->GetOwner();
  20. AActor* const MyActor = GetOwner();
  21.  
  22. const bool bNotifyActorTouch = bDoNotifies && !MyActor->IsOverlappingActor(OtherActor);
  23.  
  24. // Perform reflexive touch.
  25. OverlappingComponents.Add(OtherOverlap); // already verified uniqueness above
  26. OtherComp->OverlappingComponents.AddUnique(FOverlapInfo(this, INDEX_NONE)); // uniqueness unverified, so addunique
  27.  
  28. if (bDoNotifies)
  29. {
  30. // first execute component delegates
  31. if (!IsPendingKill())
  32. {
  33. OnComponentBeginOverlap.Broadcast(this, OtherActor, OtherComp, OtherOverlap.GetBodyIndex(), OtherOverlap.bFromSweep, OtherOverlap.OverlapInfo);
  34. }
  35.  
  36. if (!OtherComp->IsPendingKill())
  37. {
  38. // Reverse normals for other component. When it's a sweep, we are the one that moved.
  39. OtherComp->OnComponentBeginOverlap.Broadcast(OtherComp, MyActor, this, INDEX_NONE, OtherOverlap.bFromSweep, OtherOverlap.bFromSweep ? FHitResult::GetReversedHit(OtherOverlap.OverlapInfo) : OtherOverlap.OverlapInfo);
  40. }
  41.  
  42. // then execute actor notification if this is a new actor touch
  43. if (bNotifyActorTouch)
  44. {
  45. // First actor virtuals
  46. if (IsActorValidToNotify(MyActor))
  47. {
  48. MyActor->NotifyActorBeginOverlap(OtherActor);
  49. }
  50.  
  51. if (IsActorValidToNotify(OtherActor))
  52. {
  53. OtherActor->NotifyActorBeginOverlap(MyActor);
  54. }
  55.  
  56. // Then level-script delegates
  57. if (IsActorValidToNotify(MyActor))
  58. {
  59. MyActor->OnActorBeginOverlap.Broadcast(MyActor, OtherActor);
  60. }
  61.  
  62. if (IsActorValidToNotify(OtherActor))
  63. {
  64. OtherActor->OnActorBeginOverlap.Broadcast(OtherActor, MyActor);
  65. }
  66. }
  67. }
  68. }
  69. }
  70. }

代码有点长,似乎超过50,想必Epic不会找我麻烦,只看后一段好了。

简单来讲就是,如果Actor有效(Valid)则调用自己的Notify对应的事件,然后再广播!顺便做个好人,也帮对方触发一下事件,也帮对方发个小广告,为什么这么做?唔,,,思考一下,很快就明白,在Sweep方式下,被重叠的物体是通常都是被动的,如果它再主动检查,很耗资源,所以,它就在那里吃瓜好了,等我碰到你再帮你触发事件,再帮你发你要发的消息(小广告),反正如果大伙都不动,肯定不会发生碰撞(没有进行物理模拟情况下,有物理模拟,唔,同样的结论似乎也成立)。

五、进进进进一步说明重叠事件:刚才突发奇想,打开物理模拟后又如何?简单测试,随便弄个球好了,球打开物理模拟,设置对pawn对象overlap,勾选generated overlap event,然后在level blueprint中随便打印个字符串,我选择的是输出:overlap字符串。pawn一碰到球,产生了同样的事件,结论成立。

六、感谢Epic提供的源代码,引用我最喜欢的侯捷老师一句话:源码面前,了无秘密。话说侯老师,你不准备写一本《UE4引擎深入浅出》吗?多少money我都支持,擦,,,,MFC深入浅出我买了二本,第一本大多数人都没见过,浅蓝色封面的,可惜送人了。嘻嘻。

UE4的委托的更多相关文章

  1. UE4 中Struct Emum 类型的定义方式 笔记

    UE4 基础,但是不经常用总是忘记,做个笔记加深记忆: 图方便就随便贴一个项目中的STRUCT和 Enum 的.h 文件 Note:虽然USTRUCT可以定义函数,但是不能加UFUNCTION 标签喔 ...

  2. UE4技术总结——委托

    UE4技术总结--委托 目录 UE4技术总结--委托 一.定义 二.用法 2.1 声明与调用委托 2.1.1 单播委托 2.1.1.a 声明 2.1.1.b 绑定 2.1.1.c 执行委托 2.1.1 ...

  3. 【UE4 C++ 基础知识】<8> Delegate 委托

    概念 定义 UE4中的delegate(委托)常用于解耦不同对象之间的关联:委托的触发者不与监听者有直接关联,两者通过委托对象间接地建立联系. 监听者通过将响应函数绑定到委托上,使得委托触发时立即收到 ...

  4. 【UE4 设计模式】观察者模式 Observer Pattern

    概述 描述 定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新.观察者模式又叫做 发布-订阅(Publish/Subscribe)模式 模型-视图(M ...

  5. UE4新手引导之下载和安装虚幻4游戏引擎

    1) 进入虚幻4的官方主页(https://www.unrealengine.com/) 这里你可以获得关于虚幻4的最新资讯,包括版本更新.博客更新.新闻和商城等.自2015年起,该引擎已经提供免费下 ...

  6. UE4新手引导入门教程

    请大家去这个地址下载:file:///D:/UE4%20Doc/虚幻4新手引导入门教程.pdf

  7. C#中的委托解析

    谈及到C#的基本特性,“委托”是不得不去了解和深入分析的一个特性.对于大多数刚入门的程序员谈到“委托”时,都会想到“将方法作为方法的参数进行传递”,很多时候都只是知道简单的定义,主要是因为“委托”在理 ...

  8. C#的委托

    之前本人一直在写一些相对比较基础的C#代码,现在做了一段时间项目了,遇到更麻烦的问题,比如今天要讨论的委托和事件,这个算是C#进阶篇的内容吧.现在自己就把这些天所学习的和自己所理解的和大家分享.有错请 ...

  9. 深刻理解:C#中的委托、事件

    C#中的事件还真是有点绕啊,以前用JavaScript的我,理解起来还真是废了好大劲!刚开始还真有点想不明白为什么这么绕,想想和JS的区别,最后终于恍然大悟! C#中事件绕的根本原因: C#的方法,它 ...

随机推荐

  1. QT项目添加现有文件后不能运行,MFC在类视图中自动隐藏类

    解决方案:1)QT 5.6版本的QtCreator打开pro文件,在最后加一行空行或者删除一行空行,保存即可: 2)在隐藏的类对应的头文件中增加一行或删除一行(空格也可以),即可自动出现.

  2. Jenkins+Git+Maven构建并部署war包到tomcat

    主要思路:1.jenkins从git中拉取项目源码:jenkins使用maven构建并将生成的war部署到tomcat容器下. 环境:Centos7.Maven3.5.3.git(单机) 安装Git ...

  3. repo

    repo init -b remoteBranchName repo sync repo start localBranchName --all 整体切分支 if error is tagger cl ...

  4. 微信小程序---获取上传图片大小

    wx.chooseImage({ count: 1, sizeType: ['compressed'], sourceType: ['album', 'camera'], success: funct ...

  5. scrapy 爬取豆瓣互联网图书

    安装scrapy conda install scrapy 生成一个scrapy项目 scrapy startproject douban settings文件 # -*- coding: utf-8 ...

  6. git--创建空的分支

    背景:项目进行中,需要创建一个空分支.在Git中创建分支,是必须有一个父节点的,也就是说必须在已有的分支上来创建新的分支,如果你的工程已经进行了一段时间,这个时候是无法创建空分支的. 解决方法: 使用 ...

  7. 什么是云?Iaas,Paas和SaaS

    周围的朋友听说我是做云相关的,总是爱问啥是云?别不是虚幻的概念吧.云计算当然不是虚幻的概念,“云”其实是互联网的一个隐喻,简单地说,云计算是通过Internet(“云”)交付计算服务——服务器.存储. ...

  8. Java框架spring 学习笔记(四):BeanPostProcessor接口

    如果我们需要在Spring容器完成Bean的实例化,配置和其他的初始化前后后添加一些自己的逻辑处理. 编写InitHelloWorld.java package com.example.spring; ...

  9. 分布式系统里session同步

    https://blog.csdn.net/xyw591238/article/details/51644315

  10. 字符编码:Unicode和UTF-8之间的关系

    Unicode和UTF-8之间的关系 1. ASCII码 我们知道,在计算机内部,所有的信息最终都表示为一个二进制的字符串.每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256 ...