如何将TSubclassOf<>对应的子类类型传入FindNotifyByClass模板函数?
问题描述
我定义了以下结构体:
USTRUCT(BlueprintType) struct FNotifyFunc { GENERATED_USTRUCT_BODY() UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Animation") TSubclassOf<UBaseAnimNotify> Notify; UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Animation") FName FuncName; }; USTRUCT(BlueprintType) struct FAnimations { GENERATED_USTRUCT_BODY() UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Animation") FName NameOfAnimation; UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Animation") UAnimMontage* AnimMontage; UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Animation") TArray<FNotifyFunc> AnimNotifies; };
我有多个UBaseAnimNotify的子类,每个对应不同的通知事件。每个动画都有一组唯一类的通知,希望在游戏启动时遍历结构体完成初始化:
void ABaseRangeWeapon::InitAnimations() { for (const FAnimations AnimationSet : Animations) { if (!AnimationSet.AnimNotifies.Num()) continue; for (const FNotifyFunc NotifySet : AnimationSet.AnimNotifies) { if (auto Notify = AnimUtils::FindNotifyByClass<decltype(NotifySet.Notify->GetDefaultObject())>(AnimationSet.AnimMontage)) { Notify->GetDefaultObject()->OnNotified.AddUFunction(this, NotifySet.FuncName); continue; } //UE_LOG() checkNoEntry(); } } }
AnimUtils::FindNotifyByClass函数实现如下:
template<typename T> static T* FindNotifyByClass(UAnimMontage* Animation) { if (Animation) { for (auto NotifyEvent : Animation->Notifies) { if (auto AnimationNotify = Cast<T>(NotifyEvent.Notify)) { return AnimationNotify; } } } return nullptr; }
尝试传入默认对象并修改FindNotifyByClass来检查UClasses,但无法返回所需类的对象。问题出在TSubclassOf上,希望将子类的类型传递给FindNotifyByClass()。
解决方案
核心问题是decltype(NotifySet.Notify->GetDefaultObject())推导的是UBaseAnimNotify*,无法正确识别子类类型。需要调整工具函数逻辑,直接基于UClass进行匹配,而非依赖模板类型推导。
1. 修改工具函数,新增UClass匹配版本
新增一个非模板重载,直接接收UClass参数来匹配通知类:
static UBaseAnimNotify* FindNotifyByClass(UAnimMontage* Animation, UClass* NotifyClass) { if (!Animation || !NotifyClass) return nullptr; for (auto& NotifyEvent : Animation->Notifies) { if (UBaseAnimNotify* Notify = Cast<UBaseAnimNotify>(NotifyEvent.Notify)) { // 检查当前通知类是否为目标类或其子类 if (Notify->GetClass()->IsChildOf(NotifyClass)) { return Notify; } } } return nullptr; }
2. 更新初始化逻辑
直接传入NotifySet.Notify(TSubclassOf可隐式转换为UClass*),同时修复默认对象调用的错误:
void ABaseRangeWeapon::InitAnimations() { for (const FAnimations& AnimationSet : Animations) // 用引用避免不必要的结构体拷贝 { if (!AnimationSet.AnimMontage || AnimationSet.AnimNotifies.Num() == 0) continue; for (const FNotifyFunc& NotifySet : AnimationSet.AnimNotifies) { if (!NotifySet.Notify || NotifySet.FuncName.IsNone()) continue; if (UBaseAnimNotify* Notify = AnimUtils::FindNotifyByClass(AnimationSet.AnimMontage, NotifySet.Notify)) { // 直接绑定到动画蒙太奇中实际的通知实例,而非默认对象 Notify->OnNotified.AddUFunction(this, NotifySet.FuncName); continue; } UE_LOG(LogTemp, Warning, TEXT("找不到对应通知类 %s,动画:%s"), *NotifySet.Notify->GetName(), *AnimationSet.NameOfAnimation.ToString()); // checkNoEntry(); // 建议用日志替代崩溃,方便调试 } } }
可选:保留模板版本兼容原有调用
如果需要保留模板调用方式,可以新增模板重载,复用上面的UClass匹配逻辑:
template<typename T> static T* FindNotifyByClass(UAnimMontage* Animation) { return Cast<T>(FindNotifyByClass(Animation, T::StaticClass())); }
这样既支持通过TSubclassOf传入类对象,也支持直接用子类类型模板调用。
内容的提问来源于stack exchange,提问作者Alexander Odnoromanenko
相关产品推荐
相关产品推荐

