目录

前言UE 源码查看SNodePanel::ZoomLevelsFFixedZoomLevelsContainer::ZoomLevels两 ZoomLevels 之间的联系FZoomLevelsContainer函数分析

结论

具体实现验证

前言

最近有做到一个需求。 某个 Panel 放大,最大的放大倍数都不满足项目的需求。 所以需要自定义放大倍数,把放大倍数放大。 网上查了很多资料。很少有记录这样的需求。相关资源都很少。 UE 源码配合 Google 才能初窥门径。

UE 源码查看

在 Google 上搜索:UE5 modify ZoomLevel

SNodePanel::ZoomLevels

发现源码是在 SNodePanel.h 文件中,点进去查看(我下载的UE源码版,没有的小伙伴可以自行去 GitHub 拉下来)

实际项目 Debug,鼠标滚轮放大之后,会跑到这个函数里面来。

float SNodePanel::GetZoomAmount() const

{

if (bAllowContinousZoomInterpolation)

{

return FMath::Lerp(ZoomLevels->GetZoomAmount(PreviousZoomLevel), ZoomLevels->GetZoomAmount(ZoomLevel), ZoomLevelGraphFade.GetLerp());

}

else

{

return ZoomLevels->GetZoomAmount(ZoomLevel);

}

}

我们来看看 ZoomLevels 是什么东西。

SNodePanel.h

ZoomLevels:用于将 ZoomLevel 值映射到实际节点缩放值的接口。 ZoomLevels 官方文档

发现他是 struct FFixedZoomLevelsContainer 类型,点进去看看。

FFixedZoomLevelsContainer::ZoomLevels

发现一个成员变量:ZoomLevels。 构造函数里面里面初始化了 ZoomLevels 的一些信息。

这里需要区分下两个 ZoomLevels。

SNodePanel::ZoomLevelsFFixedZoomLevelsContainer::ZoomLevels

问:那么这两 ZoomLevels 是怎么联系起来的呢?

两 ZoomLevels 之间的联系

我们先来看 SNodePanel 的 Construct 函数(Construct 用于初始化成员变量) 发现这里初始化 SNodePanel::ZoomLevels 是调用了 SetZoomLevelsContainer。 SetZoomLevelsContainer 是一个模板函数。

这里我们可以看出来,如果 SNodePanel::ZoomLevels 没有被初始化,那么就会构造一个 FFixedZoomLevelsContainer 类型的 ZoomLevels。

所以还是没看出来他们有什么联系,我们先继续往下分析。

FZoomLevelsContainer

我们回过头来看 FFixedZoomLevelsContainer。

struct FFixedZoomLevelsContainer : public FZoomLevelsContainer

他是继承自 FZoomLevelsContainer,我们点进去看看。

/**

* Interface for ZoomLevel values

* Provides mapping for a range of virtual ZoomLevel values to actual node scaling values

*/

struct FZoomLevelsContainer

{

/**

* @param InZoomLevel virtual zoom level value

*

* @return associated scaling value

*/

virtual float GetZoomAmount(int32 InZoomLevel) const = 0;

/**

* @param InZoomAmount scaling value

*

* @return nearest ZoomLevel mapping for provided scale value

*/

virtual int32 GetNearestZoomLevel(float InZoomAmount) const = 0;

/**

* @param InZoomLevel virtual zoom level value

*

* @return associated friendly name

*/

virtual FText GetZoomText(int32 InZoomLevel) const = 0;

/**

* @return count of supported zoom levels

*/

virtual int32 GetNumZoomLevels() const = 0;

/**

* @return the optimal(1:1) zoom level value, default zoom level for the graph

*/

virtual int32 GetDefaultZoomLevel() const = 0;

/**

* @param InZoomLevel virtual zoom level value

*

* @return associated LOD value

*/

virtual EGraphRenderingLOD::Type GetLOD(int32 InZoomLevel) const = 0;

// Necessary for Mac OS X to compile 'delete ;'

virtual ~FZoomLevelsContainer( void ) {};

};

看下来可以理解为它是一个接口,也就是其它语言中的 Interface。

函数分析

拿 FFixedZoomLevelsContainer 做例子,我们应该很容易分析出 FZoomLevelsContainer 各个函数的意义。 先解释几个变量

ZoomLevel:放大级别ZoomAmount: 具体放大的倍数

然后再解释下其中几个函数的意义

// 给一个 ZoomLevel 返回对应的 ZoomAmount

virtual float GetZoomAmount(int32 InZoomLevel) const = 0;

// 给一个 ZoomAmount 返回一个最接近的 ZoomLevel

virtual int32 GetNearestZoomLevel(float InZoomAmount) const = 0;

// 给一个 ZoomLevel 返回对应的显示文本

virtual FText GetZoomText(int32 InZoomLevel) const = 0;

// 返回一共有多少的 放大级别

virtual int32 GetNumZoomLevels() const = 0;

// 返回默认的放大级别(用于初始化)

virtual int32 GetDefaultZoomLevel() const = 0;

// 返回对应 ZoomLevel 的 LOD 级别

virtual EGraphRenderingLOD::Type GetLOD(int32 InZoomLevel) const = 0;

// 析构函数

virtual ~FZoomLevelsContainer( void ) {};

所以实际上,两个 ZoomLevels 并没有实际的联系,只是为了 GetZoomAmount 方便返回对应的 ZoomAmount,提前在 FFixedZoomLevelsContainer 里面写死了对应的放大级别。

所以这里其实也可以用一些数学方法在 Runtime 的时候动态计算 ZoomAmount。

结论

所以只要我们自己写一个 struct 继承自 FZoomLevelsContainer,重写里面的函数,然后在自己的 Panel 类里面提前初始化一下 SNodePanle::ZoomLevels 就可以自己控制放大级别了。

如果不提前初始化 ZoomLevels,那么 SNodePanle::Construct 就会自动初始化一个 FFixedZoomLevelsContainer 类型的 ZoomLevels(上文有提及)

具体实现

自定义一个 ZoomLevelsContainer 继承自 FZoomLevelsContainer

具体有多种办法实现对应的纯虚函数。 我这里没有像 FFixedZoomLevelsContainer 一样自己包了一个 ZoomLevles ,而是通过数学计算出对应的 ZoomLevel 对应的 ZoomAmount。 建议读者根据实际项目去重写。

struct FCustomPanelZoomLevelsContainer

: public FZoomLevelsContainer

{

virtual float GetZoomAmount(int32 InZoomLevel) const override

{

return 1.f / FMath::Square(GetNumZoomLevels() - InZoomLevel + 1);

}

virtual int32 GetNearestZoomLevel(float InZoomAmount) const override

{

for (int32 ZoomLevelIndex = 0; ZoomLevelIndex < GetNumZoomLevels(); ++ZoomLevelIndex)

{

if (InZoomAmount <= GetZoomAmount(ZoomLevelIndex))

{

return ZoomLevelIndex;

}

}

return GetDefaultZoomLevel();

}

virtual FText GetZoomText(int32 InZoomLevel) const override { return FText::AsNumber(GetZoomAmount(InZoomLevel));}

virtual int32 GetNumZoomLevels() const override { return 300;}

virtual int32 GetDefaultZoomLevel() const override { return GetNumZoomLevels() - 10; }

virtual EGraphRenderingLOD::Type GetLOD(int32 InZoomLevel) const override {return EGraphRenderingLOD::DefaultDetail;}

};

你肯定有一个自定义的 Panle 类继承自 SNodePanel,比如叫做 SMyPanle。

class SMyPanle: public SNodePanel

{

public:

SMyPanle();

//Some members

void Construct()

{

ZoomLevels = MakeUnique();

// 一定要在 SNodePanel::Construct 之前进行 ZoomLevels 的初始化

SNodePanel::Construct();

}

}

在其他业务代码里创建这个 Panle 之后,在 UE 编辑器上进行放大就能看出效果了~可以试着自己改改对应的重写函数来看看具体效果。

验证

我这里直接加一个 UE_LOG 看看有没有被调用。

virtual float GetZoomAmount(int32 InZoomLevel) const override

{

UE_LOG(LogTemp, Error, TEXT("GetZoomAmount: InZoomLevel %d"), InZoomLevel);

return 1.f / FMath::Square(GetNumZoomLevels() - InZoomLevel + 1);

}

按道理,正常放大的时候,会调用 GetZoomAmount。

一进入 Panel 就会一直刷。此时打印的是 290. 因为我的 GetDefaultZoomLevel

virtual int32 GetNumZoomLevels() const override { return 300;}

virtual int32 GetDefaultZoomLevel() const override { return GetNumZoomLevels() - 10; }

所以默认返回的 300 - 10 = 290

然后使用鼠标滚轮滑动一下。

缩小 放大

然后发现放大到 290 无法进一步放大了。 这个时候我们按住 Ctrl 再去放大。 就会发现现在可以最大放大到 299

这是因为我们的 GetNumZoomLevels 返回的是 300,所以最大只能 299.

为什么不是 300 而是 299,因为是从 0 开始。

相关文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: