UE学习笔记--源码分析满满干货:如何自定义放大属性 SNodePanel 的放大级别 ZoomLevels

前言

最近有做到一个需求。
某个 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

  1. SNodePanel::ZoomLevels
  2. FFixedZoomLevelsContainer::ZoomLevels

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

两 ZoomLevels 之间的联系

我们先来看 SNodePanelConstruct 函数(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 <pointer_to_this_object>;'
	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(上文有提及)

具体实现

  1. 自定义一个 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;}
};
  1. 你肯定有一个自定义的 Panle 类继承自 SNodePanel,比如叫做 SMyPanle
class SMyPanle: public SNodePanel
{
public:
	SMyPanle();
	//Some members
	void Construct()
	{
		ZoomLevels = MakeUnique<FCustomPanelZoomLevelsContainer>();
		// 一定要在 SNodePanel::Construct 之前进行 ZoomLevels 的初始化
		SNodePanel::Construct();
	}
}
  1. 在其他业务代码里创建这个 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 开始。

  • 13
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

就一枚小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值