|
UE4中的结构体有两种,其一是 C++ 中定义,不可热更新的 UStruct,其二是脚本中定义,可热更新的 UScriptStruct。
UE4蓝图中自带的结构体是 UUserDefinedStruct,它是 UScriptStruct 的子类。该结构体可在编辑器中定义,在蓝图,以及脚本中使用。
但如果想在脚本中定义结构体,在蓝图中使用,情况就不一样了。因为 UE4 本身并不支持这种操作,不过我们可以参照 UUserDefinedStruct 的做法,实现这一点。
GitHub 上已经有人做出了插件,实现了这个功能:GitHub - 20tab/UnrealEnginePython: Embed Python in Unreal Engine 4
插件使用效果如下:
import unreal_engine as ue
@ustruct()
class TestScriptStruct:
TestBool: bool = ue.uproperty(BlueprintReadWrite = 1, EditAnywhere = 1)
TestInt: int = ue.uproperty(BlueprintReadWrite = 1, EditAnywhere = 1)
TestFloat: float = ue.uproperty(BlueprintReadWrite = 1, EditAnywhere = 1)
TestStr: str = ue.uproperty(BlueprintReadWrite = 1, EditAnywhere = 1)
TestVector: ue.FVector = ue.uproperty(BlueprintReadWrite = 1, EditAnywhere = 1)
TestObject: Object = ue.uproperty(BlueprintReadWrite = 1, EditAnywhere = 1)
但是该插件有个问题,如果脚本中定义的结构体在蓝图中也有使用,那么在我们修改结构体后,由于反射信息发生了变化,而蓝图节点没有重新构造,所以在执行与该结构体相关的蓝图节点时,会崩溃。
为了解决崩溃的问题(本质是 UScriptStruct 热更新的问题),在 UScriptStruct 更新完后,把引用该结构体的蓝图节点重新构造即可。引擎自身对 UUserDefinedStruct 蓝图节点热更新的代码如下(该段代码在引擎源文件 UserDefinedStructureCompilerUtils.cpp 中可找到):
void FUserDefinedStructureCompilerUtils::CompileStruct(class UUserDefinedStruct* Struct, class FCompilerResultsLog& MessageLog, bool bForceRecompile)
{
if (FStructureEditorUtils::UserDefinedStructEnabled() && Struct)
{
TArray<UUserDefinedStruct*> ChangedStructs;
if (FUserDefinedStructureCompilerInner::ShouldBeCompiled(Struct) || bForceRecompile)
{
ChangedStructs.Add(Struct);
}
TMap<UBlueprint*, FUserDefinedStructureCompilerInner::FBlueprintUserStructData> BlueprintsToRecompile;
for (int32 StructIdx = 0; StructIdx < ChangedStructs.Num(); ++StructIdx)
{
UUserDefinedStruct* ChangedStruct = ChangedStructs[StructIdx];
if (ChangedStruct)
{
FStructureEditorUtils::BroadcastPreChange(ChangedStruct);
FUserDefinedStructureCompilerInner::ReplaceStructWithTempDuplicate(ChangedStruct, BlueprintsToRecompile, ChangedStructs);
ChangedStruct->Status = EUserDefinedStructureStatus::UDSS_Dirty;
}
}
// COMPILE IN PROPER ORDER
FUserDefinedStructureCompilerInner::BuildDependencyMapAndCompile(ChangedStructs, MessageLog);
// UPDATE ALL THINGS DEPENDENT ON COMPILED STRUCTURES
TSet<UBlueprint*> BlueprintsThatHaveBeenRecompiled;
for (TObjectIterator<UK2Node> It(RF_Transient | RF_ClassDefaultObject, /** bIncludeDerivedClasses */ true, /** InternalExcludeFlags */ EInternalObjectFlags::PendingKill); It && ChangedStructs.Num(); ++It)
{
bool bReconstruct = false;
UK2Node* Node = *It;
if (Node && !Node->HasAnyFlags(RF_Transient) && !Node->IsPendingKill())
{
// If this is a struct operation node operation on the changed struct we must reconstruct
if (UK2Node_StructOperation* StructOpNode = Cast<UK2Node_StructOperation>(Node))
{
UUserDefinedStruct* StructInNode = Cast<UUserDefinedStruct>(StructOpNode->StructType);
if (StructInNode && ChangedStructs.Contains(StructInNode))
{
bReconstruct = true;
}
}
if (!bReconstruct)
{
// Look through the nodes pins and if any of them are split and the type of the split pin is a user defined struct we need to reconstruct
for (UEdGraphPin* Pin : Node->Pins)
{
if (Pin->SubPins.Num() > 0)
{
UUserDefinedStruct* StructType = Cast<UUserDefinedStruct>(Pin->PinType.PinSubCategoryObject.Get());
if (StructType && ChangedStructs.Contains(StructType))
{
bReconstruct = true;
break;
}
}
}
}
}
if (bReconstruct)
{
if (Node->HasValidBlueprint())
{
UBlueprint* FoundBlueprint = Node->GetBlueprint();
// The blueprint skeleton needs to be updated before we reconstruct the node
// or else we may have member references that point to the old skeleton
if (!BlueprintsThatHaveBeenRecompiled.Contains(FoundBlueprint))
{
BlueprintsThatHaveBeenRecompiled.Add(FoundBlueprint);
BlueprintsToRecompile.Remove(FoundBlueprint);
// Reapply CDO data
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(FoundBlueprint);
}
Node->ReconstructNode();
}
}
}
for (TPair<UBlueprint*, FUserDefinedStructureCompilerInner::FBlueprintUserStructData>& Pair : BlueprintsToRecompile)
{
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Pair.Key);
}
for (UUserDefinedStruct* ChangedStruct : ChangedStructs)
{
if (ChangedStruct)
{
FStructureEditorUtils::BroadcastPostChange(ChangedStruct);
ChangedStruct->MarkPackageDirty();
}
}
}
}
借鉴引擎的做法,稍作修改,我们自定义的 UScriptStruct 也可实现蓝图节点的更新,如此便能解决崩溃的问题。 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|