ue4 SNew补遗

上一篇分析了SNew背后的实现,但是有一个关键问题遗漏了,那就是:

#define SNew( WidgetType, ... ) \
    MakeTDecl<WidgetType>( #WidgetType, __FILE__, __LINE__, RequiredArgs::MakeRequiredArgs(__VA_ARGS__) ) <<= TYPENAME_OUTSIDE_TEMPLATE WidgetType::FArguments()

为何这里要用一个特别奇怪的操作符重载:【<<=】??

这个宏的目的无非是包装了WidgetType实例的创建和初始化,为何不用一个InitFromArg(FArguments& arg)之类的常规成员函数呢?

要解释这个问题,可以看下面一段示例代码:

  TSharedRef<SOverlay> ViewportOverlayWidgetRef = SNew( SOverlay );

    TSharedRef<SGameLayerManager> GameLayerManagerRef = SNew(SGameLayerManager)
        .SceneViewport_UObject(this, &UGameEngine::GetGameSceneViewport, GameViewportClient)
        .UseScissor(false)
        [
            ViewportOverlayWidgetRef
        ];

上面两句代码,都是用SNew创建一个新对象,一个不带参数,一个带了一串链式调用,要支持后者的写法,就是使用【<<=】的目的。

首先,要看清楚这一串链式调用所作用的对象:是FArgment而非WidgetType!

三个调用:SceneViewport_UObject、UseScissor和operator[],分别是用SLATE_ATTRIBUTE、SLATE_ARGUMENT、SLATE_DEFAULT_SLOT三个宏生成的。

然而更重要的是它们都包在SLATE_BEGIN_ARGS/SLATE_END_ARGS之间,而这一对宏就是在WidgetType类里再定义一个内嵌类:

#define SLATE_BEGIN_ARGS( WidgetType )     public:     struct FArguments : public TSlateBaseNamedArgs<WidgetType>     {         typedef FArguments WidgetArgsType;         FORCENOINLINE FArguments()

#define SLATE_END_ARGS() \
    };

这就是说链式调用不可能发生成SNew返回的Widget实例上,如果SNew真的是先返回一个Widget,那代码就大大有误了,连编译都过不了。

然而事实是一切顺利,那上述矛盾如何解决的呢?回过头看一下SNew展开那句代码的最后,是没有分号【;】的!这就给SNew()后面的代码提供了一个奇妙的组合结果!

就比如说下面这句话:

SNew(SGameLayerManager).UseScissor(false);

展开后成为:(简化)

MakeTDecl<SGameLayerManager>() <<= SGameLayerManager::FArguments().UseScissor(false);

一下子就高能了!因为操作符【<<=】的优先级是非常低的,这将导致它后面的代码被先结合起来,运算完了之后再整体做为【<<=】的参数传给前方的Widget实例。

而结合上面提到的链式调用都会返回this,所以后面不管跟多少调用,最终得到的结果都还是一个FArgument对象,恰恰是【<<=】所需要的类型。

此处的巧妙就在于,做为原本在同一个宏(SNew)里定义的三段:Widget <<= FArgument,在最终代码里并没有形成一体,而是利用:

1、<<=的低级先级

2、末尾没带分号

使FArgument与之后的代码优先结合,完成了一连串的链式调用后,再做为整体返还。

现在回到最初的问题:如果不使用操作符重载,而是一个普通的InitFromArg函数,会发生什么情形:

MakeTDecl<WidgetType>(SGameLayerManager).InitFromArg(SGameLayerManager::FArguments()).UseScissor(false);

显然不可能再优先后结合了。

附:SLATE_ATTRIBUTE

SLATE_ATTRIBUTE展开后,生成一个TAttribute< AttrType >成员,及大量对此属性赋值的方法,而此例中带_UObject后缀的版本,则是将其绑定到一个UObject类的成员函数上:

#define SLATE_ATTRIBUTE( AttrType, AttrName ) \
        TAttribute< AttrType > _##AttrName;
     template< class UserClass, typename Var1Type >            WidgetArgsType& AttrName##_UObject( UserClass* InUserObject, typename TAttribute< AttrType >::FGetter::template TUObjectMethodDelegate_OneVar_Const< UserClass, Var1Type >::FMethodPtr InFunc, Var1Type Var1 )            {             _##AttrName = TAttribute< AttrType >::Create( TAttribute< AttrType >::FGetter::CreateUObject( InUserObject, InFunc, Var1 ) );             return this->Me();         } \
时间: 2024-07-31 14:30:43

ue4 SNew补遗的相关文章

ue4 SNew背后的逻辑

ue4的ui库Slate体系非常庞大,即使是在创建对象这一小事上,也是相当复杂: SLATECORE_API TSharedRef<SWidget> SNullWidget::NullWidget = SNew(SNullWidgetContent).Visibility(EVisibility::Hidden); 所有SWidget体系内的对象,都要用SNew这个宏来创建,它的内容是: #define SNew( WidgetType, ... ) \ MakeTDecl<Widget

UE4灯光批量修改插件(如:把关卡中选中的灯光的光照强度同时乘以1.5倍)(C++篇)

C++:首先我们需要创建一个插件类,个人建议使用UE4插件界面创建,该好处会把一些基础类给你创建出来,我使用的是编辑器模式类插件 该插件可在窗口模式那里创建新的窗口 类创建好之后我们就可以看见UE4自身帮你创建了三个类(XXclass,XXModeclass,XXModeToolkitclass),我们写UI层逻辑主要是在XXModeToolkitclass这个类里面写,该类里面就Init()函数就是用来写SlateUI的,其中的ToolkitWidget变量是该SlateUI的最低层的UI,一

[工作积累] Tricks with UE4 PerInstanceRandom

最近在用UE4的Instancing, 发现限制很多. Unity有instancing的attribute array (uniform/constant buffer),通过InstanceID来访问每个instance的数据,实现每个实例的不同的参数(通常的一种做法). 然而Unreal没有这样的功能,只有一些instancing的vertex buffer. shader: 1 #if USE_INSTANCING && !USE_INSTANCING_EMULATED 2 flo

中国VR人才需求占全球18%排名第二,UE4技术美术人才万金难求

虚拟现实持续火爆,行业市场规模也随之水涨船高,未来医疗.娱乐.房地产.教育.旅游等领域还将继续拓展VR技术的应用.资本的入局和技术的发展已经相对稳定,VR行业必将迎来更为迅猛的发展阶段. VR在中国的快速发展有目共睹,短短两年时间已经形成了较为完善的产业链,令人惊叹.而与产业链的快速发展形成鲜明对比的,却是人才的远远落后.近期,全球最大的职场社交平台领英针对全球VR人才进行了一次全面扫描.数据显示,美国VR人才占全球40%,需求占比48%,而中国人才仅为2%,需求却占全球18%,人才短缺现象十分

ue4 c++ 接口

使用UE4接口比起普通的高级语言,要多做很多工作,是因为要兼容蓝图的使用,有一些小坑需要注意,开始吧. 1.新建接口类 打开UE4编辑器,与往常一样,新建C++类,然后选择Object继承,然后取名字,这里我使用工作中我的接口名,作为例子说明,"ITrackTeam" 然后修改继承的类,而且你没有看错,就是写2个class,一个叫UTrackTeam,一个叫ITrackTeam,类名和头文件名可以不一样的,我这边命名没有遵守虚幻的规则,本来应该命名为"UITrackTeam&

Ue4的GitHUB版本版本管理探索

GitHUB是学生党或者业余爱好者不错的选择,如果大家都处在一个局域网一下还是推荐用SVN,毕竟GitHUB的私有仓库要钱,而且网速难以忍受. 首先说一下:Ue4 4.10 默认生成一下文件与文件夹 文件夹 .vs        备用工程文件 config        游戏设置,一些内部属性 content        游戏资源 Intermediate     缓存文件 Source                C++代码文件 文件 Ue4工程文件 Vs工程文件 首先说一下Ue4的版本管

用UE4来做Zego即构的房间列表

Zego即构是一家做直播的服务商,Zego即构自己的房间列表,本文只是测试功能用,相应代码并没完全测试,请选择性参考. 我们在UE4中来实现一下,我感觉这个过程有点意思,UE4中C++与蓝图和UI的互相通信基本全部用到了. Zego即构没有专门的UE4插件,所以我们主要逻辑全部在C++中,蓝图只是辅助. 首先,我们定义一个房间结构,因为要想UE4中C++和蓝图可见可用,我们要用C++实现,并实现特定的写法让蓝图知道. USTRUCT(BlueprintType) struct FRoomBlue

[工作积累] UE4 TAA ReProjection的精度处理

先贴一个UE4 TAA的slidehttps://de45xmedrsdbp.cloudfront.net/Resources/files/TemporalAA_small-59732822.pdf 里面细节问题很多,先记录一下目前想到和遇到的问题,便于备忘,后面有空的话再记录. TAA用到的Velocity和抖动对精度要求比较高, 特别是大场景下误差容易比较大,UE4做了一系列的处理来保持精度. 投影矩阵的精度 1 static const FMatrix InvertProjectionMa

[工作积累] UE4 并行渲染的同步 - Sync between FParallelCommandListSet &amp; FRHICommandListImmediate calls

UE4 的渲染分为两个模式1.编辑器是同步绘制的 2.游戏里是FParallelCommandListSet并行派发的. mesh渲染也分两类,static mesh 使用TStaticMeshDrawList 来绘制, skinned mesh是用DrawingPolicyFactory::DrawDynamicMesh来画.这两类绘制不管是异步还是同步都会调用.具体可以参考DepthRendering.cpp 实际上,有在DX12/Vulkan/Metal 这些支持paralle commi