Faild to Replace SoftObjectPtr/SoftObjectPath References

Force Delete 删除资产的时候,硬引用到该资产的地方会被置空,但是软引用到该资产的地方都不会置空。

另外如果同一个资产同时硬软引用到该被删除的资产,那硬引用和软引用都会被清空

我自己尝试修改了一下,表面上可以解决问题,但是不确定这样是不是正确的改法:

1.ObjectTools.cpp 3326行,在RecursiveRetrieveReferencers(InObjectsToDelete, ReferencingObjects);后面

`// Preload referencer assets before final replace referencing.
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT(“AssetRegistry”));
TArray DiskReferences;
for(UObject* ObjectToDelete : InObjectsToDelete)
{
AssetRegistryModule.Get().GetReferencers(ObjectToDelete->GetOutermost()->GetFName(), DiskReferences);
}

TArray DiskReferenceAssetDatas;
for (auto& ReferencePackageName : DiskReferences)
{
AssetRegistryModule.Get().GetAssetsByPackageName(ReferencePackageName,DiskReferenceAssetDatas);
}

for (auto& AssetData : DiskReferenceAssetDatas)
{
ReferencingObjects.Add(AssetData.GetAsset());
}`2.ArchiveReplaceObjectRef.h的函数operator<<(FSoftObjectPath& Value)后添加

virtual FArchive& operator<<(FSoftObjectPtr& Value) override { if (UObject* Obj = Value.ToSoftObjectPath().ResolveObject()) { *this << Obj; Value = Obj; } else { FArchiveReplaceObjectRefBase::operator<<(Value); } return *this; }3.FindReferencersArchive.h最后添加两函数声明

COREUOBJECT_API virtual FArchive& operator<<(struct FSoftObjectPath& Value) override; COREUOBJECT_API virtual FArchive& operator<<(FSoftObjectPtr& Value) override ;4.FindReferencersArchive.cpp最后添加两函数定义

`FArchive& FFindReferencersArchive::operator<<(struct FSoftObjectPath& Value)
{
if (!Value.IsValid())
return *this;

if (IsObjectReferenceCollector())
{
FString AssetName = Value.GetAssetPathString();
FString ReferencerName = PotentialReferencer->GetPathName();
if (Value.IsValid() && AssetName != ReferencerName)
{
UObject* Obj = Value.ResolveObject();
int32* pReferenceCount = TargetObjects.GetRefCountPtr(Obj);
if ( pReferenceCount != nullptr )
{
// if this object was serialized via a FProperty, add it to the list
if (GetSerializedProperty() != nullptr)
{
ReferenceMap.AddUnique(Obj, GetSerializedProperty());
}

// now increment the reference count for this target object
(*pReferenceCount)++;
}
}
}

return *this;
}

FArchive& FFindReferencersArchive::operator<<(FSoftObjectPtr& Value)
{
if (!Value.IsValid())
return *this;

if (IsObjectReferenceCollector())
{
FString AssetName = Value.ToSoftObjectPath().GetAssetPathString();
FString ReferencerName = PotentialReferencer->GetPathName();
if (Value.IsValid() && AssetName != ReferencerName)
{
UObject* Obj = Value.ToSoftObjectPath().ResolveObject();
int32* pReferenceCount = TargetObjects.GetRefCountPtr(Obj);
if ( pReferenceCount != nullptr )
{
// if this object was serialized via a FProperty, add it to the list
if (GetSerializedProperty() != nullptr)
{
ReferenceMap.AddUnique(Obj, GetSerializedProperty());
}

// now increment the reference count for this target object
(*pReferenceCount)++;
}
}
}

return *this;
}`

你好,看描述你的意思是软引用到该资产和硬软引用到该被删除的资产结果不一致是吧?删除的是什么类型的资产?有稳定的重现方式吗我本地测一下

我按照重现步骤本地测试了一下,似乎没发现有报错信息,我上传了一下测试​视频

1.创建如下UDataAsset Class,定义SoftPathObj和SoftPtrObj两个属性

2.实例化成uasset资产A,资产中给SoftPathObj和SoftPtrObj挂上任何的资产B,保存

3.把资产B文件直接删掉,弹窗点击“Force Delete”

4.返回查看资产A,内容浏览器中右键该资产A,资产操作->验证资产,会发现会因为软引用到丢失的资产而报错,而鼠标Hover到资产A中SoftPathObj和SoftPtrObj两个属性上可以看到还是引用了资产B

UCLASS(BlueprintType)

class UTestAssetReference_NonInstancedObj : public UDataAsset

{

GENERATED_BODY()

public:

UPROPERTY(EditAnywhere)

FSoftObjectPath SoftPathObj;

UPROPERTY(EditAnywhere)

TSoftObjectPtr<UObject> SoftPtrObj;

};

感谢回复

看视频最后鼠标Hover在​SoftPathObj和SoftPtrObj上,看到Tip里显示了那个删掉的资产引用,这与我遇到的情况一致。

猜测是引用还在,使用 "Reference Viewer…"查看引用也可以看到引用了一个 None,而鼠标Hover也有删掉资产的名字。

至于没重现 “验证资产” 报错,我猜测是因为你使用的引擎版本低于5.5,我看5.4是没有这个报错的(AssetReferencingPolicySubsystem.cpp 82行)

https://github.com/EpicGames/UnrealEngine/blob/ef1397773d160d39423feb90cb2196ddfaa1e2ae/Engine/Plugins/Editor/AssetReferenceRestrictions/Source/AssetReferenceRestrictions/Private/AssetReferencingPolicySubsystem.cpp\#L82

虽然这是一个简单的报错,但是在我们项目里,有一套比较严格的资源检查规则,直接会影响到提交。​

奇怪我视频里也是用的5.5,可能需要对比一下不同版本看看有什么不一样

看样子是因为开启AssetReferenceRestrictions这个插件导致的。

但是UE官方有没有计划支持这个问题呢?就是在移除资产的时候能像“替换硬引用”那样“替换软引用”,毕竟只替换硬引用而没有替换软引用其这个潜规则有点反直觉了。

可能这个检查有点严格,我觉得最简单的您把那个引用清掉就不会报错了,进入报错资产,选择那个soft property点击clear再提交一下,这种情况我看了下主干也是这样,我可能跟内部讨论下看是否有必要处理

当然在我们的角度下,最好就能统一hard reference和soft reference的效果

引擎删除资产时对软引用通常不会自动警告,因此软引用在目标删除后变成“悬空”而不易察觉。AssetReferenceRestrictions 插件在验证阶段扫描出这些悬空软引用并提示开发者修正。但如果你们就是不需要这个验证,那我觉得你们可以在UAssetReferencingPolicySubsystem::ValidateAssetReferences中判断下这个softref的package是否被删除,删除的话就不提示报错

我是尝试修了这个soft reference没清的问题,修改代码在我首次回答提到的那一大堆代码,已经在项目里使用了一个多月,未有问题反馈。

主要是想看看官方老师有没有更准确的修正方案,或者帮忙看看这样改造会不会有问题。

看代码你是想要在序列化的时候想要清掉,我理解这个插件的用意是把这个错误报出来让开发者意识到有个路径不对的问题,可能和设计时相违背,其他的我倒没什么想法