函数返回引用
- 函数返回引用与返回指针本质一样都是返回地址, 否则函数将返回数据的拷贝
ps: 不要将局部(函数中)创建的引用作为函数返回值
// 全局变量
int val = 10;
// 定义函数返回引用
int& RetValRef()
{
return val;
}
// 将函数作为左值使用时(使用引用接收返回值),返回地址
// 本质上发生 int* const v1 = &val;
int& v1 = RetValRef();
// 引用 v1 作为左值可修改 val 的值
v1 = 20;
// 将函数作为右值使用时,返回该地址指向的值
// 本质上发生调用解引用 *val 对 v2 进行赋值
int v2 = RetValRef();
- UFUNCTION 和 DELEGATE 在蓝图中返回 UStruct 只能使用返回引用或值
- UFUNCTION 和 DELEGATE 在蓝图中返回 UClass 可以返回裸指针
- 多播委托在返回结构体引用时, 必须加 const
// 返回 * 将报错, Inappropriate '*' on variable of type 'FLevelUpInfo'
UFUNCTION()
FUStructType* GetStructInfo(int32 XP);
// 返回引用 OK
UFUNCTION()
FUStructType& GetStructInfo(int32 XP);
// UClass 返回 * OK
UFUNCTION()
AActor* GetEnemyActor();
// NewValue 如果不加 const 要报错, 但 MyActor 返回 * OK
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FSomeDelegate, const FAttributeInfoData&, Data, AActor*, MyActor, const FVector&, NewValue);
函数参数是引用
- 非 const 参数会在蓝图中作为输出 Pin
// 参数 TargetLocation 将作为输出 Pin
UFUNCTION(BlueprintCallable)
void SpawnProjectile(FVector& TargetLocation);
- const 参数会在蓝图中作为输入 Pin
// 将 TargetLocation 定义为 const, 将作为输入 Pin, 但 TargetLocation 不能在函数体中修改
UFUNCTION(BlueprintCallable)
void SpawnProjectile(const FVector& TargetLocation);
// 或使用 UPARM(ref) 宏, 使用该宏可以在函数体中修改 TargetLocation
UFUNCTION(BlueprintCallable)
void SpawnProjectile(UPARM(ref) FVector& TargetLocation);
修改函数返回值 / 参数显示名称
- 使用 UPARAM 宏可修改函数参数或返回值的显示名称
UPARAM(DisplayName = "People Name") FString GetName(UPARAM(DisplayName = "Identifier") float id);
- 推荐使用引用参数作为函数返回值
// 在蓝图节点中输出 Pin 与函数返回值效果一样, 同时使用引用参数还可以返回多个返回值
UFUNCTION(BlueprintCallable)
void GetOverlapActors(TArray<AActor*>& OutActors);
虚幻中的指针
- 对于智能指针 TSharedPtr, TWeakPtr, and TUniquePtr 不能用于 UObject (UCALSS) 类型, UObject 使用虚幻自己的反射系统管理
- 什么时候用 raw pointer
一般函数局部变量与函数返回值使用 raw pinter, 超出范围时局部变量本身自动销毁(不是指向的内存哈) - 什么时候用 TObjectPtr
类的成员变量是 UObject 类型时可使用 TObjectPtr 替代 raw pointer, 注意:不管哪种指针必须标记 UPROPERTY 来让该指针被虚幻垃圾回收跟踪. 如果不想把变量标记为 UPROPERTY 可以使用 TWeakObjectPtr (不是 TWeakPtr), 它所指向的 UObject 被销毁时会置空而不会悬挂. - 什么时候用引用
确定变量有值; 函数形参; 函数返回值; 这几种情况可偏向使用引用 - 什么时候用智能指针
不能是 UObject 类型. 所以 USTRUCT 很适合使用. USTRUCT 不属于 UObject 体系, 不由垃圾回收管理,但它可以使用智能指针提供的垃圾回收管理来防止 UObject 被销毁时产生的 USTRUCT 指针悬挂
引擎中的对象处理
- 不再被引用或已被显式标记为销毁的 UObject 将被垃圾回收机制定期清除
- 只有标记了 UProperty 的 AActor 或 UActorComponent 的指针变量以及容器中存储的指针,在清理时会自动赋空值 (= nullptr)
- 对于未标记 UProperty 的原始指针,它引用的 UObject 被销毁时不会被赋空值,将出现悬挂指针.
- UStruct 被当作数值类型处理并且不会垃圾回收, new 出来的 UStruct 可使用智能指针管理回收
IsValid
IsValid 既判断 nullptr 又判断是否准备被销毁(等待垃圾回收)
// Enemy 有可能会被销毁时使用 IsValid
if(IsValid(Enemy)) {...}
// 如果只是简单的 Proerty 等可使用
if(MaterialInstance) {...}
C++ 中定义接口
- 对于无返回值的接口可以在 Event Graph 中实现 (类似 custom event),对于有返回值的接口需要通过 Override Function 来实现.
- 在蓝图中调用接口时可像静态函数一样调用 (不需要Cast),如果 Target 未实现该函数则会略过该函数的调用
/**
* 定义为 BlueprintImplementableEvent 的函数
* 表明该方法将只能在蓝图中实现,不能在 C++ 中实现
* 函数不能加 virtual,会导致编译报错
* 使用 const 将引用参数变更为输入 PIN (否则会作为输出 PIN)
* 接口中的 BlueprintCallable 表明该函数将作为静态函数可在蓝图中直接调用
*/
UFUNCTION(BlueprintImplementableEvent, BlueprintCallable)
void DoMotionWarp(const FVector& FacingTarget);
/**
* 定义为 BlueprintNativeEvent 的函数
* 表明即可在蓝图中实现, 也可在 C++ 中定义默认实现
* 函数不能加 virtual,会导致编译报错
* 在 C++ 中实现该函数需要加上 _Implementation
*/
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
void SetCombatTarget(AActor* InCombatTarget);
在 C++ 中调用 BlueprintNativeEvent 接口,需要调用 Native C++ 实现的版本
// 判断是否实现接口
if (Actor->Implements<UCombatInterface>())
{
// 调用 Native C++ 的实现
if (!ICombatInterface::Execute_IsDead(Actor))
{
OutActors.AddUnique(Actor);
}
}
在接口中使用 BlueprintNativeEvent 返回引用时发现报错(返回类型不匹配)
// 报错
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
TArray<FTaggedMontage>& GetAttackMontages();
// 改这样实现
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
void GetAttackTaggedMontages(TArray<FTaggedMontage>& OutTaggedMontages);
DELEGATE 小坑
- DYNAMIC Delegate 参数需要加名称
/* DYNAMIC delegate 参数需要加名称 */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnFloatValueChangedSignature, float, Value);
- 非 DYNAMIC Delegate 参数不能加名称, 否则报错 ‘参数过多’
/* 推荐参考官方定义方式 */
DECLARE_MULTICAST_DELEGATE_OneParam(FOnFloatValueChangedNative, float /* NewValue */);
DYNAMIC Delegate 对绑定对象保持弱引用关系, 对象销毁后不再调用绑定的函数. 但注意发现在 UserWidget Remove from parent 后再次重新绑定会造成重复的累积绑定,原因可能是未被立即回收,需要手动调用 Unbind 函数
字符串
- TEXT()宏(注意不是FText)是根据环境判断是否为字符串前面加 ‘L’, 如 TEXT(“AB”) 相当于 L"AB", 又叫宽字符串
- 字符串前面加 ‘L’ 的意思是将字符串作为 Unicode 编码方式解析, 否则作为 ANSI 编码来解析. Unicode 每个字符占两个字节, ANSI 占一个且内容少容易乱码
常用 FunctionLibrary
// 创建 AudioComponent 并挂载到指定组件上, 跟随物体并持续发声
UGameplayStatics::SpawnSoundAttached
// 在指定位置播放声音, 一次性音效
UGameplayStatics::PlaySoundAtLocation
// 在指定位置创建 NiagaraComponent
UNiagaraFunctionLibrary::SpawnSystemAtLocation
// 额外的 DrawDebug 方法
UKismetSystemLibrary::DrawDebugArrow
// 各种物理检测函数
UKismetSystemLibrary::SphereTraceSingle
查看 Graph Node 源码
- 材质节点源码
UnrealEngine\Engine\Source\Runtime\Engine\Private\Materials\MaterialExpressions.cpp