文章目录
个人笔记,记录一些之后可能会忘记来查的小点,因为时间跨度比较长,所以内容从浅到深都有,大的点会贴一些觉得比较好的文章,或者自己另外写一篇博客
VS
编辑器
注释:Ctrl+K,Ctrl+C
去注释:Ctrl+K,Ctrl+U
格式化代码:Ctrl+K,Ctrl+F
书签:Ctrl+K,Ctrl+K
上一个书签:Ctrl+K,Ctrl+P
下一个书签:Ctrl+K,Ctrl+N
代码区域划分(#region会自动补全)
#pragma region xxxx
#pragma endregion
全部折叠:Ctrl+M、Ctrl+M
全部打开:Ctrl+M、Ctrl+L
切换当前块的折叠:Ctrl+M、Ctrl+O
折叠当前块:双击这条线的任何地方
查找文件、符号、类型:Ctrl+, 然后输入f跳转文件,输入#跳转符号
切换.cpp和.h文件:Ctrl+K Ctrl+O
高级查找:Shfit+Ctrl+F
当前文档内查找函数:Alt+M
正则表达式替换:
用括号括出字符串,在替换项内可以使用参数的形式进行使用
Set(.{3,7})Value
UBTFunction::Set$1Value
上一个光标位置:Alt+左
下一个光标位置:Alt+右
配置光标前进后退:
运行调试
生成:Ctrl+B
取消生成:Ctrl+Pause(Break)
开始运行调试:F5
运行并停在Main函数:F11
断点:F9
继续运行:F5
调试下一步:F10
调试下一步(进去):F11
调试跳出:Shift+F11
附加到进程:Alt+Shfit+P
从进程脱离:Debug - Windows - Processes
- 条件断点
-
如果有去自己尝试的,会发现各种操作都不允许,说是有副作用,或者找不到运算符。
-
使用成员变量而非函数,类型判断使用C++而非UE,例如:
dynamic_cast<ANavigationData*>(Object) != nullptr
-
获取UObject所在World:
((ULevel*)this->OuterPrivate)->OwningWorld
-
从World获取UNavigationSystemV1的某个属性
dynamic_cast<UNavigationSystemV1*>(UE::CoreUObject::Private::ResolveObjectHandle(NavigationSystem.ObjectPtr.Handle))->DefaultDirtyAreasController
-
查看TSet的Num:
S.Elements.Data.ArrayNum - S.Elements.NumFreeIndices
-
FString子串
wcsstr((wchar_t*)MyString.Data.AllocatorInstance.Data,L"Search substring")
-
FName子串
strstr(((FNameEntry&)GNameBlocksDebug[MyFName.DisplayIndex.Value>>FNameDebugVisualizer::OffsetBits] [FNameDebugVisualizer::EntryStride*(MyFName.DisplayIndex.Value&FNameDebugVisualizer::OffsetMask)]).AnsiName ,"Search substring")
-
关闭代码优化,更好调试:
UE_DISABLE_OPTIMIZATION_SHIP
// 代码
UE_ENABLE_OPTIMIZATION_SHIP
-
更改执行流:断点后,拖拽左侧的黄色箭头
-
查看返回值:监视输入$returnvalue
-
监视变量的修改:
注意是当前对象的某个成员变化,所以不能用于nullptr -
调试占位的bool:先使用&取出地址,然后断点 - 新建 - 数据断点,输入地址,然后字节选择1
VSCode
https://blog.csdn.net/jk_chen_acmer/article/details/115001623
VA
快捷菜单:Shfit+Alt+Q
切换.h和.cpp:Alt+O
注释/去注释:/
查找定义:Alt+G
查找继承/重载链:Shfit+Alt+G
查找符号:Shfit+Alt+S
查找引用:Shfit+Alt+F
查找文件:Shfit+Alt+O
蓝图 / 编辑器
拖动多个引脚:按住Ctrl键并拖动引脚
整理蓝图:
-
资源标记为修改:
UObject::Modify UObjectBaseUtility::MarkPackageDirty
-
标记当前关卡为已修改:
FScopedLevelDirtied LevelDirtyCallback; LevelDirtyCallback.Request();
-
设置 Actor 名字,Actor 所处文件夹
FActorLabelUtilities::SetActorLabelUnique(Actor, TEXT("MyName")) ResultingBattleWall->SetFolderPath(TEXT("A/B"));
-
设置 Actor 无法被拖动
Actor->SetLockLocation(true);
-
用代码修改蓝图配置:
bp_cdo = bp_class->GetDefaultObject() ... EditorAssetLibrary::SaveLoadedAsset(bp_class)
-
编辑器内热更新代码:Ctrl+Alt+F11
-
快速批量修改资源属性:
- 多选资源后右键 - Asset Actions - Edit Selection in Property Matrix
- 可以多选行后批量同时修改
- 单机Pin图案可以选定想要查看的哪些属性
关键字
-
Editor下变灰不可编辑
UPROPERTY(EditDefaultsOnly, Meta = (EditCondition = "AreaType == ETriggerAreaMode::Circle")) FCircleTrigger CircleTrigger;
可以添加EditConditionHides,在不可编辑的情况下,会隐藏该属性
#if WITH_EDITOR // 可以自定义是否可以编辑 virtual bool CanEditChange(const FProperty* InProperty) const override; if (!Super::CanEditChange(InProperty)) { return false; } //看这个类中是否有这个属性 if(InProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UXXX, XXX)) { return bXXX; } return true; #endif
-
可以让一个bool类型的变量,变成复选框,更详细的参考:Unreal虚幻引擎属性编辑条件
UPROPERTY(EditDefaultsOnly, Category="Function | Basic", meta=(InlineEditConditionToggle="MaxTargets")) bool bLimitTarget = false; // Target count limitation UPROPERTY(EditDefaultsOnly, Category="Function | Basic", meta=(EditCondition="bLimitTarget")) int32 MaxTargets = 1;
HideEditConditionToggle:隐藏内联复选框(多个会混乱,只保留一个即可)
-
AdvancedDisplay
-
meta = (ClampMin = 10.f, ClampMax = 150.f) 此处0.xf时前面的0不可省略,莫名其妙
-
DefaultToInstanced 和 Instanced
- Instanced是指生成新的对象时,从CDO的该属性复制一份,而不是单纯的指针指向源地址
- DefaultToInstanced表示该类型默认就是Instanced的,不用在用的地方再加Instanced
- 有一个很好的用法:配合EditInlineNew,定义一个不确定类型的支持反射的配置项,Lyra示例:
UCLASS(EditInlineNew, Abstract) class LYRAGAME_API ULyraAbilityCost : public UObject UCLASS(meta=(DisplayName="Inventory Item")) class ULyraAbilityCost_InventoryItem : public ULyraAbilityCost UPROPERTY(EditDefaultsOnly, Instanced, Category = Costs) TArray<TObjectPtr<ULyraAbilityCost>> AdditionalCosts;
-
关于动态类型,可以看下这篇文章 Link,使用 FInstancedStruct,定义动态类型的结构体而不是 UObject
// 注意基类的析构函数需要改为虚函数 // 5.1打包后不能成功引用住Object,5.3官方代码有直接带Uobject的,应该不会有这个问题 UPROPERTY(EditAnywhere, NoClear, Meta = (ExcludeBaseStruct, BaseStruct = "/Script/Chooser.ContextObjectTypeBase"), Category = "Input") TArray<FInstancedStruct> ContextData; STRUCT(DisplayName = "Asset") struct CHOOSER_API FAssetChooser : public FObjectChooserBase { GENERATED_BODY() // FObjectChooserBase interface virtual UObject* ChooseObject(FChooserEvaluationContext& Context) const final override; public: UPROPERTY(EditAnywhere, Category = "Parameters") TObjectPtr<UObject> Asset; }; // TInstancedStruct可以直接获取特定类型的指针 UPROPERTY(EditAnywhere, Category = Foo) TArray<TInstancedStruct<FTestStructBase>> TestArray; FTestStruct Temp; // derived from FTestStructBase Temp.TurnRate = 15; XXX.InitializeAsScriptStruct(FTestStruct::StaticStruct(), (const uint8*)&Temp);
-
-
类可蓝图化
UCLASS(Blueprintable)
-
让组件类可以派生蓝图类
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent),Blueprintable )
-
meta = (ContentDir, LongPackageName),可以让 FDirectoryPath 出现相对于Content目录的路径选择器,最后选择的路径是项目路径为相对路径
Actor
-
开启Tick
PrimaryActorTick.bCanEverTick = true;
-
销毁时调用
AActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
-
AActor延时销毁
AActor->SetLifeSpan(3)
FTimerHandle handle; GetWorldTimerManager().SetTimer(handle, [this, AActor]() {this->GetWorld()->DestroyActor(AActor); }, 3, 0);
-
获取Actor位置/旋转
GetActorLocation()/GetActorRotation()
-
设置Actor位置和旋转
SetActorLocationAndRotation(FVector, FVector)
-
获取Actor方向向量
this->GetActorForwardVector()
-
Actor朝镜头射线位置移动
TraceLength = 2500.f; LerpSpeed_Location = 10.f; void AXXX::Tick(float DeltaSeconds) APlayerCameraManager* CM = UGameplayStatics::GetPlayerController(GetWorld(), 0)->PlayerCameraManager; FVector Location = CM->GetCameraLocation(); FVector Direction = CM->GetCameraRotation().RotateVector(FVector(1, 0, 0)); FCollisionQueryParams Params; Params.AddIgnoredActor(this); Params.bTraceComplex = false; if (GetWorld()->LineTraceSingleByObjectType(Hit, Location, Location + TraceLength * Direction, FCollisionObjectQueryParams(ECC_WorldStatic), Params)) FVector TargetLocation = UKismetMathLibrary::VInterpTo(GetActorLocation(), Hit.Location, DeltaSeconds, LerpSpeed_Location); SetActorLocation(TargetLocation);
-
旋转线性变化
RotateSpeed = 45.f; LerpSpeed_Rotation = 720.f; void AXXX::AddRotation(float Rotation) AdditionalRotation += Rotation * RotateSpeed; void AXXX::Tick(float DeltaSeconds) float RValue = FMath::Sign(AdditionalRotation) * FMath::Min(DeltaSeconds * LerpSpeed_Rotation, FMath::Abs(AdditionalRotation)); AdditionalRotation -= RValue; AddActorWorldRotation(FRotator(0, RValue, 0));
-
获取存在时间
GetGameTimeSinceCreation()
-
按类型寻找Actor
#include "EngineUtils.h" for (TActorIterator<AMyPlayerController> it(GetWorld(), AMyPlayerController::StaticClass()); it; ++it) { if (it) { (*it) } }
UGameplayStatics::GetAllActorsOfClassWithTag(GetWorld(), ACoreRPGCharacter::StaticClass(), TEXT("Player"), TArray<>);
-
生成蓝图类(蓝图类路径后加
_C
)UClass* BlueprintVar = StaticLoadClass(AShooter::StaticClass(), nullptr, TEXT("Blueprint'/Game/BP/BP_Shooter.BP_Shooter_C'")); AShooter* shooter = GetWorld()->SpawnActor<AShooter>(BlueprintVar);
-
将Actor绑定到另外一个Actor
shooter->AttachToActor(this, FAttachmentTransformRules::KeepRelativeTransform); shooter->SetActorRelativeLocation(FVector(-60, -80, 120));
-
在蓝图内选择类型(填写蓝图路径),创建某种类型的蓝图类实例
.h UPROPERTY(EditAnywhere) TSubclassOf<AActor> Creation; UPROPERTY(EditAnywhere) FString Path; AActor* Item; .cpp const TCHAR* tmp = *Path; UClass* BlueprintVar = StaticLoadClass(Creation, nullptr, tmp); Item = GetWorld()->SpawnActor<AActor>(BlueprintVar);
Component
-
构造函数内创建组件
USceneComponent* P = CreateDefaultSubobject<USceneComponent>(TEXT("Name"));
- 不是USenceComponent组件会自动绑定,就上面一行代码即可
- USenceComponent组件设为Root
RootComponent = P;
- 绑到其他USenceComponent下面(加到Socket下)
P->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
-
设置组件的相对位置
SetRelativeLocation(FVector)
-
动态创建组件,有两个接口AActor::AddComponent和AActor::AddComponentByClass,都可以通过将bDeferredFinish改为false,进行属性的设定。
- 两个接口都会将新建的组件存到BlueprintCreatedComponents内部,避免GC
- UActorComponent::PostInitProperties内也有AddOwnedComponent的操作
- bDeferredFinish时,需要手动调用FinishAddComponent,其实内部就是OnComponentCreated、RegisterComponent
- 两个接口都会将新建的组件存到BlueprintCreatedComponents内部,避免GC
-
删除创建的组件:
UActorComponent::DestroyComponent
-
获取组件(获取某种特定类、Tag的组件)
TArray<UParticleSystemComponent*>Jets; GetComponents(Jets ,true); // true表示获取子Actor的组件 for (int32 i = 0; i < Jets.Num(); i++) { if (!(Jets[i]->ComponentTags.Contains(TEXT("Jet")))) { Jets.RemoveAt(i); i--; } }
-
设置组件是否激活
C->SetActive(true);
-
开启Tick
PrimaryComponentTick.bCanEverTick = true;
-
开关粒子系统组件的某个发射器
UParticleSystemComponent* Jet = Cast<UParticleSystemComponent>(GetComponentByClass(UParticleSystemComponent::StaticClass())); Jet->SetEmitterEnable(TEXT("Main"), true);
-
胶囊体组件的真实半高
UCapsuleComponent->GetScaledCapsuleHalfHeight()
-
移除组件
RemoveFromParent()
-
删除组件
Widget->UnregisterComponent(); Widget->DestroyComponent();
-
隐藏
SetVisibility(false) SetHiddenInGame(true)
-
关闭静态网格体碰撞
FrameCubeMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("FrameCubeMesh")); FrameCubeMesh->SetGenerateOverlapEvents(false); FrameCubeMesh->SetCollisionProfileName(TEXT("NoCollision"));
Pawn/Character And Controller
Controller作为控制器,只需要执行控制部分的功能,即:不管你控制的是哪个角色,都需要的普适性功能
Pawn/Character则是实现Controller不适合实现的其他角色部分
-
获取当前Pawn的控制器
GetController()
-
获取指定PlayerController
#include "Kismet/GameplayStatics.h" UGameplayStatics::GetPlayerController(GetWorld(), 0)
-
设置重生时间
virtual void PostInitializeComponents()override; float APlayerControllerInGame::GetMinRespawnDelay() { AProjectWPlayerState* PS = GetPlayerState<AProjectWPlayerState>(); if (PS && PS->AS) { return 3 + PS->AS->GetLevel(); } return 1; }
AI
Decorator Task C++
UE4 行为树节点C++
AI 感知
AI 感知
UE4:AI‘s MoveTo——代码分析
UE4 HTN插件源码解析
HTN框架介绍、开发心得
UE4 行为树节点C++
从Engine\Source\Runtime\AIModule\Classes\BehaviorTree\Tasks\BTTask_WaitBlackboardTime.h开始了解会比较好
-
初始化行为树和黑板
void AMonsterAI::OnPossess(APawn* InPawn) { Super::OnPossess(InPawn); if (RunBehaviorTree(BTree)) { UBehaviorTreeComponent* BT = Cast<UBehaviorTreeComponent>(GetComponentByClass(UBehaviorTreeComponent::StaticClass())); UBlackboardComponent* BB = BT->GetBlackboardComponent(); BB->SetValueAsFloat(TEXT("PatrolRadius"), PatrolRadius); } }
-
设置黑板键
UBehaviorTreeComponent* BT = Cast<UBehaviorTreeComponent>(GetComponentByClass(UBehaviorTreeComponent::StaticClass())); UBlackboardComponent* BB = BT->GetBlackboardComponent(); BB->SetValueAsObject("TargetActor", Player);
-
修改寻路网格颜色
-
寻路的路径
UNavigationSystemV1::FindPathToLocationSynchronously(&OwnerComp, RawStartLocation, RawGoalLocation); Path->PathPoints[I]
光照
-
环境内各个元素的作用:
- DirectionalLight:提供平行光源
- SkyAtmosphere:提供天空盒
- VolumetricCloud:提供云
- ExponentialHeightFog:提供远处(地面线下)的漫反射,不至于一坨黑
-
Ctrl+L+鼠标移动:修改 DirectionalLight 的光照方向
- 与水平线夹角越小,光线越弱,配合天空大气和体积云,可以做出傍晚的感觉
-
PointLight、SpotLight可以修改 Source Radius 让阴影变得柔和,RectLight则是调整 Source Width 和 Source Height
-
开启光追:Project Setting
- Default RHI:DirectX 12
- Dynamic Global Illumination Method:Lumen
- Reflection Method:Lumen
- Support Hardware Ray Tracing:True
- 重启
-
去除光照贴图:World Setting - Force No Precomputed Lighting:True,再Build Light Only
-
关闭光照:Affects World:False
-
环境配置工具:Windows - Env.Light Mixer
- 可以一键添加 DirectionalLight、SkyAtmosphere、ExponentialHeightFog、VolumetricCloud
-
材质的Basic Color中的V值,是颜色自身的光线反射率,也就是Max(R,G,B)
- 在使用Lumen时,V值越高,反射的光线越多,光线射进房间但是其它地方太暗可能就是材质反射率太低
- 反射率应该在0.04-0.85之间,中间应该是0.18
-
材质快捷键
- 实数、二元组、三元组(RGB)、四元组(RGBA):1234+鼠标左键
- 加:A+鼠标左键
- 乘:M+鼠标左键
- 除:D+鼠标左键
- 材质节点:T+鼠标左键
- 标准化:N+鼠标左键
- 新建实数参数:S+鼠标左键
- 新建四元组参数:V+鼠标左键
- IF:I+鼠标左键
-
可以通过颜色*实数(光照强度)连接emissive,实现自发光,但推荐仅用于点缀
-
柔和光源造成的影子:Cast Ray Traced Shadows:Enabled,如果有噪点,将Samples Per Pixel改为4
-
SkyLight:
- Source Type改为SLS_SpecifiedCubemap后,选择一个Cubemap,就可以使用HDRI的光照
- Lower Hemisphere Is Black:来自下方的光的颜色是否变为 Lower Hemisphere Color,下图是开启后红色的效果和关闭的效果
-
禁用自动曝光:
- Metering Mode:AEM_Manual
- Apply Physical Camera Exposure:False
- 如果比较暗,可以调高整体曝光Exposure Compensation
-
去除房间内蓝光:
- ExponentialHeightFog 的 Volumetric Fog 改为 True
-
增加光的体积感:调高DirectionalLight 的 Volumetric Scattering Intensity可以做到全局的,局部使用其它光源也可以
-
配置只有走到光方向的位置(窗口)才能看到体积光(不调也可以看需求):调高ExponentialHeightFog的Scattering Distribution
-
解决Lumen下有一些时隐时现 / 抖动的地方:
- 后处理体积搜索Lumen:Lumen Scene Lighting Quality改为2,Final Gather Quality改为2,Final Gather Lighting Update Speed改为0.5
- 或者增加局部的光源,因为这些地方没有直射光只有反射光所以不稳定
-
星空
- Engine Content内搜索BP_Sky_Sphere拖入,修改Sun Height为-1即可
- Cloud Speed:10
- Cloud Opacity:1
- Stars Brightness:0.4
- Engine Content内搜索BP_Sky_Sphere拖入,修改Sun Height为-1即可
数学
-
fabs、PI、FMath::Min
-
夹角
float AAIMastermind::GetAngle(FVector A, FVector B) { A.Normalize(); B.Normalize(); return FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(A,B))); }
-
两个点的方向Rotator
UKismetMathLibrary::FindLookAtRotation(FVector, FVector)
FRotator RotationOnEnd = (RawTargetLocation - RawLocationOnStart).Rotation()
-
获取当前旋转体的X方向
FVector di = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
-
四元数操作
FQuat OriginalQuat = Temp2->GetComponentQuat(); FQuat DeltaQuat = FQuat(FVector::RightVector, TurnY/60); FQuat DeltaQuat2 = FQuat(FVector::ForwardVector, TurnX / 60); FQuat DeltaQuat3 = FQuat(FVector::UpVector, TurnZ / 60); FQuat FinalQuat = OriginalQuat * DeltaQuat * DeltaQuat2 * DeltaQuat3; Temp2->SetWorldRotation(FinalQuat)
Other API
-
静态加载(构造函数内使用,所以路径只能写死)
其实ConstructorHelpers内部调用的也是StaticLoad,只不过是将ConstructorHelpers结构体定义成Static,在CDO的时候进行加载后,之后在异步加载对象的时候,就直接使用之前在主线程加载好的对象即可。(StaticLoad不允许在异步线程调用)
-
资源(UObject对象)
// 只能在构造函数内使用 static ConstructorHelpers::FObjectFinder<UTexture> CubeVisualAsset(TEXT("/Script/Engine.Texture2D'/Game/ProjectXX/.../ObjectName.ObjectName'")); // Game/ProjectXX/.../ObjectName 也可以 if (CubeVisualAsset.Succeeded()) CubeVisualAsset.Object;
-
类
// 只能在构造函数内使用 static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/ThirdPerson/Blueprints/BP_ThirdPersonCharacter")); if (PlayerPawnBPClass.Class != NULL) { DefaultPawnClass = PlayerPawnBPClass.Class; }
-
-
动态加载
-
资源(UObject对象)
if (UTexture* TextureFound = LoadObject<UTexture>(this, TEXT("Game/ProjectXX/.../ObjectName")))
-
类
UClass* AssetClass = LoadClass<UObject>(nullptr, TEXT("/Game/ThirdPerson/Blueprints/BP_ThirdPersonCharacter"));
-
-
设置静态网格体
SetStaticMesh(UStaticMesh)
-
Pawn旋转应用到摄像机
camera->bUsePawnControlRotation = true;
-
绑定轴
PlayerInputComponent->BindAxis("Forward", this, &AMe::MoveForward);
-
绑定事件
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AMe::StartJump); PlayerInputComponent->BindAction("Jump", IE_Released, this, &AMe::EndJump);
-
是否尝试跳跃
bPressedJump
-
射线检测
//DrawDebugLine(GetWorld(), Target, Target + FVector(0, 0, -1000), FColor::Red, true, 5); FCollisionQueryParams Param(SCENE_QUERY_STAT(YourTag), false, nullptr); FHitResult Hit; bool bSuccess = GetWorld()->LineTraceSingleByObjectType(Hit, Target, Target + FVector(0, 0, -1000), ECC_WorldStatic, Param); if (bSuccess && Hit.Actor != nullptr && Hit.Actor.Get() != nullptr) { GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Yellow, Hit.GetActor()->GetFName().ToString()); // 销毁射线检测到的第一个Actor GetWorld()->DestroyActor(Hit.GetActor()); }
-
胶囊体检测
FHitResult Temp; FVector StartP, EndP; TArray<TEnumAsByte<EObjectTypeQuery>> ObjectTypes; if (UKismetSystemLibrary::CapsuleTraceSingleForObjects(this, StartP, EndP, CapsuleRadius, CapsuleHalfHeight , { UEngineTypes::ConvertToObjectType(ECC_WorldStatic) } , false, { IgnoreActor }, bDrawDebug ? EDrawDebugTrace::ForOneFrame : EDrawDebugTrace::None, Temp, true))
-
画出锥形
UKismetSystemLibrary::DrawDebugConeInDegrees(Me->GetWorld(), Me->GetActorLocation(), Me->GetActorForwardVector(), SightRadius, SightAngle, SightAngle, 24, FColor(255, 176, 223), 0, 10);
-
画出水平的圆
UKismetSystemLibrary::DrawDebugCircle(MeshComp, Center, Radius, 36, FLinearColor::Green, Duration, 0, FVector(0.f, 1.f, 0.f), FVector(1.f, 0.f, 0.f), false);
-
各种追踪检测
#include "Kismet/KismetSystemLibrary.h" TArray<FHitResult> HitResult; TArray<AActor*> IgnoreResult; IgnoreResult.Add(this); FVector Start = this->GetActorLocation(); FVector End = this->GetActorLocation() + this->GetActorForwardVector() * 100; // 需要进行去重 UKismetSystemLibrary::SphereTraceMulti(this->GetWorld(), Start, End, 100, (ETraceTypeQuery)3, true, IgnoreResult, EDrawDebugTrace::ForDuration, HitResult, true, FLinearColor::Black, FLinearColor::Blue, 60); TSet<AActor*>Set; for (FHitResult& Hit : HitResult) { if(Hit.Actor == nullptr || Hit.Actor.Get() == nullptr)return; ARootCharacter* Role_ = Cast<ARootCharacter>(Hit.Actor.Get()); if (Role_) { UE_LOG(LogTemp, Log, TEXT("Projectile Explode to Actor")); if(Set.Contains(Role_))continue; Set.Add(Role_);
-
延时
#include "TimerManager.h" FTimerHandle handle; GetWorldTimerManager().SetTimer(handle, []() {}, 3, 0);
-
循环操作
void ABlockManager::KeyRightStart() { GetWorldTimerManager().SetTimer(RightHandle, [this]() { ... }, 间隔, 1); } void ABlockManager::KeyRightEnd() { GetWorldTimerManager().ClearTimer(RightHandle); }
-
设置Mesh的材质参数
Mesh->SetScalarParameterValueOnMaterials("DissolveValue", ToDissolve);
-
设置材质
int32 matsNum = staticMeshComponent->GetNumMaterials(); if (matsNum == 1) { staticMeshComponent->SetMaterial(0, InMaterial); return true; }
-
渐变类
// By JkChen #include "RandomChanger.h" ARandomChanger::ARandomChanger() { PrimaryActorTick.bCanEverTick = true; } void ARandomChanger::BeginPlay() { Super::BeginPlay(); Enabled = false; } // 效果:在[Min,Max]中随机变化,变化较为柔和 // 实现:在Min到Max内随机一个作为下一个目标点,TurnCycle时间一个周期 void ARandomChanger::StartChanger(float StartPoint, float _TurnCycle, float _Min, float _Max) { Enabled = true; Pre = Value = StartPoint; Min = _Min, Max = _Max; NowTime = 0; TurnCycle = _TurnCycle; Nex = FMath::FRandRange(Min, Max); } void ARandomChanger::Tick(float DeltaTime) { Super::Tick(DeltaTime); if (Enabled) { NowTime += DeltaTime; Value = (TurnCycle - NowTime) * Pre * (1.0 / TurnCycle) + NowTime * Nex * (1.0 / TurnCycle); if (NowTime > TurnCycle) { NowTime = 0; Pre = Value; Nex = FMath::FRandRange(Min, Max); } } } void ARandomChanger::EnableChanger() { Enabled = true; } void ARandomChanger::DisableChanger() { Enabled = false; }
-
获取相机位置和朝向向量(命中屏幕中心)
auto cm = UGameplayStatics::GetPlayerController(GetWorld(), 0)->PlayerCameraManager; FVector location = cm->GetCameraLocation(); FVector direction = cm->GetCameraRotation().RotateVector(FVector(1, 0, 0)); // 射线为location, location + 1000 * direction
-
子类类型(用于避免互相include)
TSubclassOf<AActor> Creation
-
通过绑定弹簧臂实现与人物旋转脱离
CameraBoom->bUsePawnControlRotation = true;
USpringArmComponent* sp = Cast<USpringArmComponent>(GetComponentByClass(USpringArmComponent::StaticClass())); if (sp) { shooter->AttachToComponent(sp, FAttachmentTransformRules::KeepRelativeTransform); shooter->SetActorRelativeLocation(FVector(450, 0, 100)); }
-
角色按键绑定委托
DECLARE_DELEGATE_OneParam(FDelegateInt, int); PlayerInputComponent->BindAction<FDelegateInt, ACastleCharacter, int>("Skill0", IE_Pressed, this, &ACastleCharacter::UseSkill, 0); //void UseSkill(int idx);
-
原型
template< class DelegateType, class UserClass, typename... VarTypes > FInputActionBinding& BindAction( const FName ActionName, const EInputEvent KeyEvent, UserClass* Object, typename DelegateType::template TUObjectMethodDelegate< UserClass >::FMethodPtr Func, VarTypes... Vars ) { FInputActionBinding AB( ActionName, KeyEvent ); AB.ActionDelegate.BindDelegate<DelegateType>(Object, Func, Vars...); return AddActionBinding(MoveTemp(AB)); }
-
-
飞行模式
void ACastleCharacter::ToggleFly() { // 状态维护 UCharacterMovementComponent* CM = GetCharacterMovement(); CM->SetMovementMode(MOVE_Flying); } void ACastleCharacter::ToggleWalk() { UCharacterMovementComponent* CM = GetCharacterMovement(); CM->SetMovementMode(MOVE_Falling); } void ACastleCharacter::Jump() { bPressedJump = true; JumpKeyHoldTime = 0.0f; UCharacterMovementComponent* CM = GetCharacterMovement(); if (CM->MovementMode == EMovementMode::MOVE_Flying) { // 飞行按下掉落 ToggleWalk(); } else if (CM->IsFalling()) { // 空中按下飞行 ToggleFly(); } } void ACastleCharacter::StopJumping() { bPressedJump = false; ResetJumpState(); }
-
动态读取动态(先加载一个挂载UParticleSystemComponent的空Actor(UParticleSystemComponent的Template不能为空,随便设置一个就行),设置UParticleSystemComponent的粒子系统Template,最后ResetNextTick)
UClass* BlueprintVar = StaticLoadClass(AActor::StaticClass(), nullptr, TEXT("Blueprint'/Game/Effect/AdvancedMagicFX14/Particles/Mine/BP_ParticleBase.BP_ParticleBase_C'")); AActor* ParticleBase = GetWorld()->SpawnActor<AActor>(BlueprintVar); UParticleSystemComponent* sc = Cast<UParticleSystemComponent>(ParticleBase->GetComponentByClass(UParticleSystemComponent::StaticClass())); sc->Template = LoadObject<UParticleSystem>(NULL, TEXT("ParticleSystem'/Game/Effect/AdvancedMagicFX14/Particles/Mine/P_Fire.P_Fire'")); sc->ResetNextTick(); ParticleBase->AttachToActor(this, FAttachmentTransformRules::KeepRelativeTransform); ParticleBase->SetActorRelativeLocation(FVector::ZeroVector); ParticleBase->SetLifeSpan(3);
static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/ThirdPersonCPP/Blueprints/ThirdPersonCharacter")); if (PlayerPawnBPClass.Class != NULL) { DefaultPawnClass = PlayerPawnBPClass.Class; }
-
在不生成对象(Actor)的情况下,获取蓝图的配置
UPROPERTY(EditAnywhere, Category = "SplineMove") TSubclassOf<AActor> SplineContainer; // 直接Cast UBlueprintGeneratedClass UBlueprintGeneratedClass* Class = Cast<UBlueprintGeneratedClass>(SplineContainer); if (Class) { USplineComponent* Spline = nullptr; // 获取SimpleConstructionScript if (!Class->SimpleConstructionScript) { return false; } // Node里面存的是Component for (USCS_Node* Node : Class->SimpleConstructionScript->GetAllNodes()) { // 该Node的类型 if (Node->ComponentClass->IsChildOf(USplineComponent::StaticClass())) { // 组件模板就是用来存你编辑的信息的 if (USplineComponent* Spline_ = Cast<USplineComponent>(Node->ComponentTemplate)) { Spline = Spline_; break; } } }
-
设置角色速度
GetCharacterMovement()->Velocity
-
设置暂停
UGameplayStatics::SetGamePaused(GetWorld(), true)
-
是否暂停
UGameplayStatics::IsGamePaused(GetWorld())
-
是否是否暂停
UGameplayStatics::SetGamePaused(GetWorld(), true)
-
暂停切换 / 菜单栏
void APlayerControllerInGame::ToggleInputMode() { if (UGameplayStatics::IsGamePaused(GetWorld())) { ToggleToGameMode(); } else { ToggleToUIMode(); } } void APlayerControllerInGame::ToggleToUIMode() { widget->SetVisibility(ESlateVisibility::Visible); //UE_LOG(LogTemp, Log, TEXT("Change to UI")); FInputModeGameAndUI InputMode; InputMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock); InputMode.SetHideCursorDuringCapture(false); SetInputMode(InputMode); bShowMouseCursor = true; UGameplayStatics::SetGamePaused(GetWorld(), true); } void APlayerControllerInGame::ToggleToGameMode() { widget->SetVisibility(ESlateVisibility::Hidden); //UE_LOG(LogTemp, Log, TEXT("Change to Game")); FInputModeGameOnly InputMode; SetInputMode(InputMode); bShowMouseCursor = false; UGameplayStatics::SetGamePaused(GetWorld(), false); }
-
退出游戏
//UKismetSystemLibrary::QuitGame(GetWorld(), UGameplayStatics::GetPlayerController(GetWorld(), 0), EQuitPreference::Quit, true); GetWorld()->GetFirstPlayerController()->ConsoleCommand("quit"); //FGenericPlatformMisc::RequestExit(false); // 貌似会关闭整个进程,So,关闭UE4编辑器
-
获取当前世界的tick时间
GetWorld()->TimeSeconds
-
播放动画蒙太奇,返回动画长度,可以设置播放速率,需要在动画蓝图内添加Slot
PlayAnimMontage(AttackedAnim)
-
判断类是否存在某成员
static FProperty* Prop = FindFieldChecked<FProperty>(ClassName::StaticClass(), GET_MEMBER_NAME_CHECKED(ClassName, PropertyName)); return Prop; // Returns FName(TEXT("MemberName")), while statically verifying that the member exists in ClassName #define GET_MEMBER_NAME_CHECKED(ClassName, MemberName) \ ((void)sizeof(UE4Asserts_Private::GetMemberNameCheckedJunk(((ClassName*)0)->MemberName)), FName(TEXT(#MemberName)))
UMG
-
创建UserWidget类
UPROPERTY(EditDefaultsOnly) TSubclassOf<UUserWidget> PlayerAndStateUI; UPlayerStateAndSkillWidget* widget;
widget = CreateWidget<UPlayerStateAndSkillWidget>(GetWorld(), PlayerAndStateUI); widget->AddToViewport();
- 可通过以下类创建UserWidget
UWidget、UWidgetTree、APlayerController、 UGameInstance、 UWorld
- 可通过以下类创建UserWidget
-
获取Widget
GetWidgetFromName(Name)
-
自动绑定控件
UPROPERTY(meta = (BindWidget)) // 该变量会自动寻找名为SettingImage的按钮进行绑定,如果没找到会编译出错 class UButton* SettingImage;
-
按钮绑定事件
button->OnClicked.AddDynamic(this, &UMyUserWidget::ClickButton);
-
图片绑定事件
image->OnMouseButtonDownEvent.BindUFunction(this, "UseSpell");
-
Set
text->SetText(FText::FromString(FString(TEXT("Hu Hahaha")))); image->SetIsEnabled(0); text->SetVisibility(ESlateVisibility::Visible);
-
设置TextBlock颜色
MyTextBlock->SetColorAndOpacity(FSlateColor(FLinearColor(R / 255.0f, G / 255.0f, B / 255.0f, 1)));
-
鼠标永久可见
void AMyPlayerController::BeginPlay() { widget = Cast<UMyUserWidget>(CreateWidget(this, Creation)); widget->AddToViewport(); bShowMouseCursor = true; }
-
Widget移动
UImage* image = Cast<UImage>(GetWidgetFromName(TEXT("Mouse"))); UCanvasPanelSlot* slot = Cast<UCanvasPanelSlot>(image->Slot); slot->SetPosition(slot->GetPosition() + FVector2D(0, 1));
-
将Widget的位置设置到世界Actor的位置:
FVector2D ScreenPosition; if (UWidgetLayoutLibrary::ProjectWorldLocationToWidgetPosition(GetOwningPlayer(), Target->GetActorLocation(), ScreenPosition, false)) { if (UCanvasPanelSlot* CanvasSlot = Cast<UCanvasPanelSlot>(Img->Slot)) { ScreenPosition -= CanvasSlot->GetSize() / 2;; CanvasSlot->SetPosition(ScreenPosition); } }
-
获取屏幕大小
UGameplayStatics::GetPlayerController(GetWorld(), 0)->GetViewportSize(x_int,y_int)
-
获取鼠标位置
float px, py; if (UGameplayStatics::GetPlayerController(GetWorld(), 0)->GetMousePosition(px, py))
-
获取当前控件在当前窗口大小下的缩放
UWidgetLayoutLibrary::GetViewportScale(this)
-
判断是否在屏幕内
bool AMyCharacter::IsPointInMyScrrenViewport(FVector Point, FVector2D& Result) { if (GetLocalRole() == ROLE_AutonomousProxy) { APlayerController* Ctrl = GetController<APlayerController>(); ULocalPlayer* LP = Ctrl ? Ctrl->GetLocalPlayer() : nullptr; if (LP && LP->ViewportClient) { FSceneViewProjectionData ProjectionData; if (LP->GetProjectionData(LP->ViewportClient->Viewport, eSSP_FULL, ProjectionData)) { FMatrix const ViewProjectionMatrix = ProjectionData.ComputeViewProjectionMatrix(); FVector2D ScreenPosition; bool bResult = FSceneView::ProjectWorldToScreen(Point, ProjectionData.GetConstrainedViewRect(), ViewProjectionMatrix, ScreenPosition); if (bResult && ScreenPosition.X > ProjectionData.GetViewRect().Min.X && ScreenPosition.X < ProjectionData.GetViewRect().Max.X && ScreenPosition.Y > ProjectionData.GetViewRect().Min.Y && ScreenPosition.Y < ProjectionData.GetViewRect().Max.Y) { Result = ScreenPosition; return true; } } } } Result = FVector2D::ZeroVector; return false; }
-
UI设置到屏幕中心
int32 VX, VY; APlayerController* Ctrl = GetController<APlayerController>(); if (Ctrl) { Ctrl->GetViewportSize(VX, VY); } float Scale = UWidgetLayoutLibrary::GetViewportScale(this); // 设到中心,但是UI会处于中心点右下角,需要减去一半大小 UI->SetPositionInViewport(FVector2D(VX, VY) / 2 - UI->GetRootWidget()->GetDesiredSize() / 2 * Scale);
-
关闭 FocusWidget 边框显示:项目设置 - RenderFocusRule 改为 Never
-
设置Widget的位置(到鼠标位置)(与锚点的偏移会受到窗口大小缩放的影响,所以需要除以缩放)
UImage* image = Cast<UImage>(GetWidgetFromName(TEXT("Mouse"))); UCanvasPanelSlot* slot = Cast<UCanvasPanelSlot>(image->Slot); auto pc = UGameplayStatics::GetPlayerController(GetWorld(), 0); if (image && pc) { float px, py; if (pc->GetMousePosition(px, py)) { UCanvasPanel* cp = Cast<UCanvasPanel>(GetWidgetFromName(TEXT("ScreenPanel"))); float scale = UWidgetLayoutLibrary::GetViewportScale(this); slot->SetPosition(FVector2D(px / scale, py / scale)); } }
-
设置鼠标位置(需要在Build.cs里面加SlateCore)
UGameplayStatics::GetPlayerController(GetWorld(), 0)->SetMouseLocation(x,y)
-
世界3D Widget
UWidgetComponent* Widget = NewObject<UWidgetComponent>(this); Widget->RegisterComponent(); UUserWidget* AlertUI = CreateWidget<UUserWidget>(GetWorld(), Asset_AlertUI); Widget->SetWidget(AlertUI); Widget->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore); // 宽长比过小会挤压UI Widget->SetDrawSize(FVector2D(1920, 1080)); Widget->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepWorldTransform); Widget->SetWidgetSpace(EWidgetSpace::World); Widget->SetVisibility(true); Widget->SetWorldLocation(xxx); Widget->SetWorldRotation(xxx);
-
屏幕2D Widget
UUserWidget* AlertUI = CreateWidget<UUserWidget>(GetWorld(), Asset_AlertUI); AlertUI->AddToViewport(); AlertUI->SetVisibility(ESlateVisibility::Visible); int32 VX = 0, VY = 0; APlayerController* Ctrl = GetController<APlayerController>(); if (Ctrl) { Ctrl->GetViewportSize(VX, VY); } float Scale = UWidgetLayoutLibrary::GetViewportScale(this); // 中心点偏移,一定有Scale FVector2D EndP = FVector2D(VX, VY) / 2 + 偏移 * Scale; // 需要减去Widget自身半长宽,这个使用GetDesiredSize得到 // 但是在第一次NativeTick之前GetDesiredSize为0,需要调用ForceLayoutPrepass AlertUI->ForceLayoutPrepass(); AlertUI->SetPositionInViewport(EndP - AlertUI->GetRootWidget()->GetDesiredSize() / 2 * Scale); float Angle = 45; AlertUI->SetRenderTransformAngle(Angle);
-
- OnDrugDetect 创建一个 WidgetDrug 类存储信息,再OnDrug的时候使用这个信息更新位置
FText/FString/FName/TChar/ANSICHAR
-
https://www.cnblogs.com/KillerAery/p/14385574.html
-
制作字符串:
FString::Printf(TEXT("%d"), i) FString::Printf(TEXT("Blueprint'/Game/ProjectW/Skill/BP_%s.BP_%s_C'"), *(MyFName.ToString()), *(MyFName.ToString()));
#include "Misc/StringBuilder.h" TStringBuilder<2048> SB; SB << TEXT("If"); return *SB
-
互转:
/ → F S t r i n g \to FString →FString → F T e x t \to FText →FText → F N a m e \to FName →FName F S t r i n g → FString \to FString→ / F T e x t : : F r o m S t r i n g ( M y S t r i n g ) FText::FromString(MyString) FText::FromString(MyString) F N a m e ( ∗ M y S t r i n g ) FName(*MyString) FName(∗MyString) F T e x t → FText \to FText→ M y T e x t . T o S t r i n g ( ) MyText.ToString() MyText.ToString() / F N a m e ( ∗ ( M y T e x t . T o S t r i n g ( ) ) ) FName(*(MyText.ToString())) FName(∗(MyText.ToString())) F N a m e → FName \to FName→ M y N a m e . T o S t r i n g ( ) MyName.ToString() MyName.ToString() F T e x t : : F r o m N a m e ( M y N a m e ) FText::FromName(MyName) FText::FromName(MyName) / 字面值 → \to → T E X T ( " 1 " ) TEXT("1") TEXT("1") F T e x t : : F r o m S t r i n g ( T E X T ( " 1 " ) ) FText::FromString(TEXT("1")) FText::FromString(TEXT("1")) T E X T ( " 1 " ) TEXT("1") TEXT("1") -
FString 转 TCHAR*:
const TCHAR* MyConstTchar = *MyString
-
std::string 转 FString
FString MyString(MyStdString.c_str())
-
FString 转 std::string
std::string MyStdString(TCHAR_TO_UTF8(*MyString))
-
FString 转 char*
char* x = TCHAR_TO_ANSI(*FString())
-
FString 转 int/float
FString MyString = "1108.1110"; int32 MyInt32 = FCString::Atoi(*MyString); float MyInt32 = FCString::Atof(*MyString);
-
测试
FName MyName = TEXT("1"); FText MyText = FText::FromString(TEXT("1")); FString MyString = TEXT("1"); MyText = FText::FromName(MyName); MyText = FText::FromString(MyString); MyName = FName(*MyString); MyName = FName(*(MyText.ToString())); MyString = MyText.ToString(); MyString = MyName.ToString(); const TCHAR* MyConstTchar = *MyString;
-
ON_SCOPE_EXIT,推出当前作用域时执行
CurrentPlan->bGetNextPrimitiveStepsForRunning = true; ON_SCOPE_EXIT { CurrentPlan->bGetNextPrimitiveStepsForRunning = false; }; ......
-
其它转ANSICHAR
const ANSICHAR* InputTags_ANSI = TCHAR_TO_ANSI(*InputTags.ToString());
-
输出地址
#include "Engine/Engine.h" GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, FString::Printf(TEXT("AutonomousProxy: 0x%u%"), &Value));
UE
-
查看引擎版本号:
Engine\Source\Runtime\Launch\Resources\Version.h
// 5.3.2 #define ENGINE_MAJOR_VERSION 5 #define ENGINE_MINOR_VERSION 3 #define ENGINE_PATCH_VERSION 2
-
配置文件
-
UFUNCTION不能重载(序列化)
-
添加头文件,以Engine\Source\Runtime\NavigationSystem\Public\NavigationSystem.h为例
Runtime和Public都是一个很明显的分界线,所以#include "NavigationSystem/Public/NavigationSystem.h" #include "NavigationSystem.h"
然后到.Build.cs,将"NavigationSystem"加到PublicDependencyModuleNames.AddRange内
-
UObject销毁
UObject::MarkPendingKill() //此方法执行后,所有指向此实例的指针将设置为NULL,并在下一次GC时删除
-
增加Insight Trace:
// 字面量 TRACE_CPUPROFILER_EVENT_SCOPE(DelaySceneRenderCompletion_TaskWait); // 运行时 TRACE_CPUPROFILER_EVENT_SCOPE_TEXT(*(FString(TEXT("test.")) + GetName())); // TRACE_CPUPROFILER_EVENT_SCOPE_STR // 记录UObject #define SCOPE_CYCLE_UOBJECT(Name, Object) \ FScopeCycleCounterUObject ObjCycleCount_##Name(Object); SCOPED_BOOT_TIMING("xxx")
-
开启UFunction Trace,在启动命令行加入:
-statenamedevents -trace=default,AssetLoadTime
- 具体在:
#define PER_FUNCTION_SCRIPT_STATS ((STATS || ENABLE_STATNAMEDEVENTS) && 1) void UObject::CallFunction( FFrame& Stack, RESULT_DECL, UFunction* Function ) { #if PER_FUNCTION_SCRIPT_STATS const bool bShouldTrackFunction = (Stack.DepthCounter <= GMaxFunctionStatDepth); SCOPE_CYCLE_UOBJECT(FunctionScope, bShouldTrackFunction ? Function : nullptr); #endif // PER_FUNCTION_SCRIPT_STATS SCOPE_CYCLE_UOBJECT(ContextScope, GVerboseScriptStats ? this : nullptr);
-
Insight 操作:参考
- F聚焦某个片段,C垂直方向调整缩放,双击片段高亮同类型片段,双击空白区域取消高亮
- Ctrl双击片段:选中当前片段的时间区域
-
打印堆栈信息
static void PrintStackInfo(int32 Depth = 20) { FString Result = ""; TArray<FProgramCounterSymbolInfo> Infos = FPlatformStackWalk::GetStack(1, Depth); for (const FProgramCounterSymbolInfo& Info : Infos) { Result += FString::Printf(TEXT("%s %s Line:%d\n"), ANSI_TO_TCHAR(Info.FunctionName), ANSI_TO_TCHAR(Info.Filename), Info.LineNumber); } UE_LOG(LogTemp, Error, TEXT("%s"), *Result); } or FDebug::DumpStackTraceToLog(ELogVerbosity::Display);
-
前置声明
- 在.h内加入class MyClass
- 在.cpp可以include不会造成循环include
-
UE4 重定向模块名或者类名(加入到Config/DefaultEditor.ini),参考
- 重定向模块名,例如将插件内的类,转移到项目内部,这个时候类所处的模块会发生变化,例如PLUGINA_API变为PROJECTA_API,而这里的PLUGINA和PROJECTA就是两个模块,可以一次性将该模块的所有内容合并到另外一个模块,则使用:
[CoreRedirects] +PackageRedirects=(OldName="/Script/PLUGINA", NewName="/Script/PROJECTA")
- 第二种是重定向某个类,例如重命名cpp类时,原来的cpp类派生出的蓝图类需要重定向,则使用(注意去掉F、A、U等前缀):
+ClassRedirects=(OldName="/Script/PROJECTA.ClassA",NewName="/Script/PROJECTA.ClassB")
- 重定向模块名,例如将插件内的类,转移到项目内部,这个时候类所处的模块会发生变化,例如PLUGINA_API变为PROJECTA_API,而这里的PLUGINA和PROJECTA就是两个模块,可以一次性将该模块的所有内容合并到另外一个模块,则使用:
-
UINTERFACE(Blueprintable) class UReactToTriggerInterface : public UInterface { GENERATED_BODY() }; class IReactToTriggerInterface { GENERATED_BODY() public: /** 对激活该对象的触发器体积做出响应。如果响应成功,返回“真(true)”。*/ UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category="Trigger Reaction") bool ReactToTrigger() const; };
-
纯虚函数
virtual void UseOnce() PURE_VIRTUAL (USkillComponent::UseOnce, ); virtual bool UseOnce() PURE_VIRTUAL (USkillComponent::UseOnce, return false;); virtual bool UseOnce() = 0; //无法实例化、抽象类
-
加载 / 卸载LevelInstance
bool bSuccess; LoadedBlock = ULevelStreamingDynamic::LoadLevelInstance(this, LevelName, LevelLocation, LevelRotation, bSuccess); LoadedBlock->SetIsRequestingUnloadAndRemoval(true); LoadedBlock->MarkPendingKill();
-
异步加载SoftObject,并在加载完成后触发回调:
TArray<FSoftObjectPath> AssetsToLoad; auto Callback = [this, ...]() {}; FStreamableManager& StreamableManager = UAssetManager::GetStreamableManager(); StreamingHandle = StreamableManager.RequestAsyncLoad(AssetsToLoad, Callback);
-
RootMotion施加的位置:
if( HasAnimRootMotion() ) { const FQuat OldActorRotationQuat = UpdatedComponent->GetComponentQuat(); const FQuat RootMotionRotationQuat = RootMotionParams.GetRootMotionTransform().GetRotation(); if( !RootMotionRotationQuat.IsIdentity() ) { const FQuat NewActorRotationQuat = RootMotionRotationQuat * OldActorRotationQuat; MoveUpdatedComponent(FVector::ZeroVector, NewActorRotationQuat, true); }
-
使用UStruct/UScriptStruct指针动态创建结构体对象
UStruct* Type; uint8* RowData = (uint8*)FMemory::Malloc(Type->GetStructureSize()); // 调用构造函数,初始化虚表 Type->InitializeStruct(RowData); // 可选,反序列化 ((FXXX*)RowData)->Serialize(Ar)
-
类判断
- 两个对象类相同:
GetClass()
- 是否属于某个类:
GetClass()->IsChildOf(AClass1::StaticClass())
- 结构体:
Value.StaticStruct() == FBlackBoardBool::StaticStruct()
- 两个对象类相同:
-
优先队列
Frontier.HeapPush(NewPlan, FComparePlanCosts()); Frontier.HeapPop(Plan, FComparePlanCosts()); struct FComparePlanCosts { FORCEINLINE bool operator()(const TSharedPtr<FHTNPlan>& A, const TSharedPtr<FHTNPlan>& B) const { return A.IsValid() && B.IsValid() ? A->Cost < B->Cost : B.IsValid(); } };
-
自定义Sort
struct FXXX { struct FSort { FORCEINLINE bool operator()(const FXXX& A, const FXXX& B) const { return A.Priority > B.Priority; } }; }; XXXArray.Sort(FXXX::FSort());
-
GameFramework Objects Creation Order and Calls
-
各个GameFramework
/ 权威 主控 模拟 GameInstance Yes Yes Yes GameMode Yes No No GameState Yes Yes Yes PlayerController Yes Yes No PlayerState Yes Yes Yes -
UE Network 基础
- https://stonelzp.github.io/ue4-multiplay-framework/
- https://alexandrite.top/2020/05/20/ue4-wang-luo-bian-cheng-ue4/
-
传递函数:
bool XXX::XXX(TFunctionRef<bool(const int32&)> XXX) XXX([](const int32& InElement) { return true; });
-
FVector初始值(1.xxxxe38)这种,使用Equal无法进行正确判断
-
private: static class UClass * __cdecl ***::GetPrivateStaticClass(void)
的几种可能-
没有引入模块(插件),需要在.build.cs的
PublicDependencyModuleNames
里面加对应模块PublicDependencyModuleNames.AddRange( new string[] { } );
-
引入模块,但是该模块的某个类没有加上XXX_API,所以这个类视为模块内使用,不可被其它模块使用,在以下位置加上即可
class XXX_API UMyClass
-
-
强制转换
operator FVector() const { return FVector(X, Y, Z); };
-
(Editor)调试网络延时:NetEmulation.PktLag 300
-
(Editor)调试网络延时波动:NetEmulation.PktLagVariance 100
-
(Server)时间推进速度:Slomo 0.5
-
关闭运行时失去焦点后降低帧数:EditorPreference - Use Less CPU when in background
-
获取所有UObject成员
TArray<UObject*> ChildObjects; GetObjectsWithOuter(AActor, ChildObjects, false, RF_NoFlags, EInternalObjectFlags::PendingKill);
-
遍历反射属性、不使用字面量获取类型名称
for (TFieldIterator<FProperty> It(Comp->GetClass(), EFieldIteratorFlags::ExcludeSuper); It; ++It) { FProperty* Property = *It; if (Property->GetCPPType() == Type1->GetStructCPPName()) { FScalableFloat* InData = It->ContainerPtrToValuePtr<FScalableFloat>(Comp); InData->SetValue(Value); } if (Property->GetCPPType() == Type2->GetStructCPPName()) { FAttributeBasedFloat* InData = It->ContainerPtrToValuePtr<FAttributeBasedFloat>(Comp); InData->Coefficient = Value; } }
-
不使用字面量获取枚举、成员、函数名称
GET_ENUMERATOR_NAME_CHECKED(EnumName, EnumeratorName) GET_MEMBER_NAME_CHECKED(ClassName, MemberName) GET_MEMBER_NAME_STRING_CHECKED(ClassName, MemberName) GET_FUNCTION_NAME_CHECKED(ClassName, FunctionName) GET_FUNCTION_NAME_STRING_CHECKED(ClassName, FunctionName) // 函数还可以确认参数类型 GET_FUNCTION_NAME_CHECKED_TwoParams(ClassName, FunctionName, ArgType1, ArgType2)
-
获取CDO(Class Default Object)
TSubclassOf<UXXX>XXX; XXX->GetDefaultObject<UXXX>();
-
开启PushModel,使用bool判断代替值判断:
// 定义同步属性 FDoRepLifetimeParams SharedParams; SharedParams.bIsPushBased = true; DOREPLIFETIME_WITH_PARAMS_FAST(AXXX, XXX, SharedParams); // 在修改后手动设置脏 MARK_PROPERTY_DIRTY_FROM_NAME(AXXX, XXX, this);
-
调节分辨率
-
命令行
r.SetRes 2560x1440f/w // fullscreen or window
-
代码
// EWindowMode::Windowed GEngine->GetGameUserSettings()->SetFullscreenMode(EWindowMode::WindowedFullscreen); GEngine->GetGameUserSettings()->SetScreenResolution(FIntPoint(1920, 1080)); GEngine->GetGameUserSettings()->ApplySettings(true);
-
-
命令行打包,命令参考
- 来到Engine\Build\BatchFiles,打开cmd,运行RunUAT即可
-
打包后LoadObject失败:
- 原因:因为资源没有被cook进包体(因为是代码去读取,所以没有被检测出引用)。
- 解决办法:将所在路径加入到项目设置的Additional Asset Directories to Cook内即可
-
蓝图内修改属性后自动做出调整
#if WITH_EDITOR && USE_EQS_DEBUGGER virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; #endif //WITH_EDITOR && USE_EQS_DEBUGGER
-
蓝图可访问该私有变量:meta = (AllowPrivateAccess = “true”)
-
开启 Nanite:在StaticMesh上右键 - Nanite - Enable
- 可以过滤所有的 StaticMesh 一键转换
-
查看 Nanite 三角面:Lit - 改为Nanite Visualization - Triangles
-
后处理体积
- 全局适用:Infinite Extent (Unbound):True
-
运行时显示碰撞:show collision
-
导出内存分析数据:游戏内 MemReport -full
-
Enum使用位运算
enum class EXXX : uint16 { A = 0x01, B = 0x02, C = 0x03 } using T = std::underlying_type_t<EXXX>; // uint16 EXXX xxx; if((T)xxx & (T)EXXX::A) { dosomething(); }
定义可以Tick的WorldSubsystem
... UXXXSystem : public UTickableWorldSubsystem
virtual TStatId GetStatId() const override;
virtual void Tick(float DeltaTime) override;
TStatId UXXXSystem::GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(UXXXSystem, STATGROUP_Tickables);
}
void UXXXSystem::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
定义自定义StatId:
// .h
DECLARE_STATS_GROUP(TEXT("Task Graph Tasks"), STATGROUP_TaskGraphTasks, STATCAT_Advanced);
DECLARE_CYCLE_STAT_EXTERN(TEXT("FReturnGraphTask"), STAT_FReturnGraphTask, STATGROUP_TaskGraphTasks, CORE_API);
DECLARE_STATS_GROUP(TEXT("AbilitySystem"), STATGROUP_AbilitySystem, STATCAT_Advanced);
DECLARE_CYCLE_STAT_EXTERN(TEXT("GetActiveEffectsData"), STAT_GameplayEffectsGetActiveEffectsDuration, STATGROUP_AbilitySystem, );
// .cpp
DEFINE_STAT(STAT_FReturnGraphTask);
return GET_STATID(STAT_FReturnGraphTask);
DEFINE_STAT(STAT_GameplayEffectsGetActiveEffectsDuration);
SCOPE_CYCLE_COUNTER(STAT_GameplayEffectsGetActiveEffectsDuration);
C++
Final
static_cast、dynamic_cast、const_cast和reinterpret_cast
Lambda
隐式转换
宏展开顺序
FILE, FUNCTION, LINE
move forward
-
函数指针作为返回值
int Add_Implement(int a, int b) { return a + b; } int (*Add())(int, int) { return Add_Implement; } cout << Add()(1, 2); // 3
-
宏:转字符串
1. #:在宏展开的时候会将#后面的参数替换成字符串,如: #define p(exp) printf(#exp); 调用p(test)的时候会将#exp换成"test" 2. ##:将参数作为字面值,与其它部分连接,作为一个变量名、函数名、类名等 #define cat(x,y) x##y 调用cat(var, 123)展开后成为var123. 3. #@:将值序列变为一个字符 #define ch(c) #@c 调用ch(a)展开后成为'a'.
-
传递任意类型的函数:
template<typename IterateFunc> inline void ForeachItem(const IterateFunc& Func) { int32* Ptr; Func(Ptr); } ForeachItem([&Ans](int32* Ptr) {++Ans; });
-
宏展开补充
#define Cb(A,B,C) A##B##C #define CC(A,B,C) Cb(A,B,C) #define Pi 3.14 // 展开实参后代入Cb CC(__LINE__,_,Pi) -> Cb(5,_,3.14) -> 5_3.14 // ##相连的部分不再展开,仅始为字符串 Cb(__LINE__,_,Pi) -> __LINE___Pi
这是为什么 GENERATED_BODY 需要转到 BODY_MACRO_COMBINE后,再转一层 BODY_MACRO_COMBINE_INNER 的原因
-
局部函数
struct Helper { static void GetEndRotation(const FNavigationPath& Path, FRotator& OutEndRotation) { int32 All = Path.GetPathPoints().Num(); if (All > 1) { OutEndRotation = (Path.GetPathPointLocation(All - 1).Position - Path.GetPathPointLocation(All - 2).Position).Rotation(); } } }; Helper::GetEndRotation(Path, OutEndRotation);
-
节省函数返回值的Cast
//类内部: FORCEINLINE APawn* GetPawn() const { return Pawn; } /** Templated version of GetPawn, will return nullptr if cast fails */ template<class T> T* GetPawn() const { return Cast<T>(Pawn); }
-
判断该类是否继承了某一接口
//注意:这里是UMyInterface而不是IMyInterface AActor::StaticClass()->ImplementsInterface(UMyInterface::StaticClass());
-
Lambda
默认是值捕获,值捕获时捕获当前的值,就算之后值变了,Lambda内还是那个值
auto ChangeToForward = [this](FVector Input)->FVector { FVector Forward = this->GetControlRotation().Vector(); Forward.Normalize(); Forward *= Input.Size(); return Forward; };
-
lambda实现递归:
// 不用 auto std::function<int(int,int)> sum; // 捕获自己 sum = [term,next,&sum](int a, int b)->int { if(a>b) return 0; else return term(a) + sum(next(a),b); };
-
Link Error:自己找找为什么符号没有被找到,可能是以下原因
- 循环引用
- Build.cs没加入
- 插件内部类,没有加XXX_API进行导出
- 类静态成员变量没有在cpp内实例化
-
SetTimer 传参
FTimerHandle Handle; FTimerDelegate TheDelegate = FTimerDelegate::CreateUObject(this, &AMyGameMode::Func, Para1, Para2); // call this->Func after 5s without loop, parameter Player GetWorldTimerManager().SetTimer(Handle, TheDelegate, 5.0f, false );
枚举
USTRUCT
[UE4] c++中使用struct
USTRUCT的使用
UStruct 与 Traits
自定义结构体序列化
自定义结构体序列化
TArray、TMap、TTuple、TPair、UEnum
-
TMap遍历
UPROPERTY(EditAnywhere) TMap<TSubclassOf<AMonsterCharacter>, float> SpawnMonsters; for (TPair<TSubclassOf<AMonsterCharacter>, float>& element : SpawnMonsters) { Sum += element.Value; }
for (auto It = Groups.CreateIterator(); It; It++) { if (!It->Key) { It.RemoveCurrent(); continue; }
-
TMap Sort
Ability_Cost.ValueSort([](float A, float B) { return A < B; });
-
自写的UStruct作为TMap的Key时,需要重载等于号(Contains等函数)和GetTypeHash(哈希)
USTRUCT() struct FTwoGameplayTagContainer { GENERATED_BODY() UPROPERTY(EditAnywhere) FGameplayTagContainer A; UPROPERTY(EditAnywhere) FGameplayTagContainer B; bool operator== (const FTwoGameplayTagContainer& Other) const { return A == Other.A && B == Other.B; } friend uint32 GetTypeHash(const FTwoGameplayTagContainer& A) { return GetTypeHash(A.A.ToString() + "$" + A.B.ToString()); // 多个时可以使用 return HashCombine(GetTypeHash(A.OwnerComp), GetTypeHash(A.TemplateNode)); } };
-
ENum暴露蓝图
UENUM(BlueprintType) enum class EAIState : uint8 { Born UMETA(DisplayName = "Born"), Idle UMETA(DisplayName = "Idle"), InAttack UMETA(DisplayName = "In Attack"), Attack UMETA(DisplayName = "Attack"), OutAttack UMETA(DisplayName = "Out Attack"), Dead UMETA(DisplayName = "Dead"), };
-
Enum变量转int、获取字符串
const UEnum* EnumPtr = StaticEnum<EAIState>(); //FindObject<UEnum>(ANY_PACKAGE, TEXT("EAIState"), true); return FString::Printf(TEXT("Set state to %s"), *EnumPtr->GetNameStringByValue((int32)AIState));
-
Enum获取DisplayName
UEnum::GetDisplayNameTextByValue
-
在黑板使用C++定义的ENum,填到Enum Name里面就行了
-
TTuple
TArray<TTuple<float, ARPGAICharacter*>> DisAndAI; for(ARPGAICharacter* AI : Elem.Value.AIs) { DisAndAI.Add(MakeTuple(AI->GetDistanceTo(Elem.Key), AI)); } DisAndAI.Sort([](TTuple<float, ARPGAICharacter*> A, TTuple<float, ARPGAICharacter*> B) { return A.Get<0>() < B.Get<0>(); });
-
TPair
TPair<int32, int32> X(1, 2); X.Key = 2; X.Value = 2; X = MakeTuple(2, 3);
-
FastArray
会乱序,但是因为使用手动MarkDirty,不用每次全部比较,并且在增加和删除时不会将变动的位置全部发送
// 结构体继承FFastArraySerializerItem USTRUCT() struct FXXX: public FFastArraySerializerItem { GENERATED_USTRUCT_BODY() UPROPERTY() int32 XXX; // 客户端同步回调 void PreReplicatedRemove(); void PostReplicatedAdd(); void PostReplicatedChange(); }; // 数组包成一个类,继承FFastArraySerializerItem USTRUCT() struct FXXXContainer: public FFastArraySerializer { GENERATED_USTRUCT_BODY() UPROPERTY() TArray<FXXX> Items; bool NetDeltaSerialize(FNetDeltaSerializeInfo & DeltaParms) { return FFastArraySerializer::FastArrayDeltaSerialize<FXXX, FXXXContainer>(Items, DeltaParms, *this); } template< typename Type, typename SerializerType > bool ShouldWriteFastArrayItem(const Type& Item, const bool bIsWritingOnClient) { // Type is FXXX, 定制是否需要同步 ... if (bIsWritingOnClient) { return Item.ReplicationID != INDEX_NONE; } return true; } void AddItem(FXX XX) { FXX& Item = Items.Add_GetRef(MoveTemp(XX)); // 增加或修改元素后,需要对相应的元素手动 MarkDirty MarkItemDirty(Item); } void RemoveItem(const FXX& XX) { for(int32 Idx = 0; Idx < Items.Num(); ++Idx) { if(Items[Idx] == XX) { Items.RemoveAt(Idx); // 删除后,需要手动 MarkArrayDirty MarkArrayDirty(); return } } } }; template<> struct TStructOpsTypeTraits<FXXXContainer> : public TStructOpsTypeTraitsBase2<FXXXContainer> { enum { WithNetDeltaSerializer = true, }; };
编译选项
时间
序列化
反射
使用:
《InsideUE4》UObject(十三)类型系统-反射实战
原理:
UE 反射实现分析:基础概念
UE4反射机制
UE4 反射系统
// 根据反射调用函数
template<typename... TReturns, typename... TArgs>
void InternalCallUFunction(UObject* Object, UFunction* Function, TTuple<TReturns...>& OutParams, TArgs&&... Args)
{
if (!Object || !Function)
{
return;
}
uint8* OutParamsBytes = (uint8*)&OutParams;
TTuple<TArgs...> InParams(Forward<TArgs>(Args)...);
uint8* InParamsByte = (uint8*)&InParams;
void* FuncParamsStructAddr = (uint8*)FMemory_Alloca(Function->ParmsSize);
FMemory::Memzero(FuncParamsStructAddr, Function->ParmsSize);
for (TFieldIterator<FProperty> It(Function); It; ++It)
{
FProperty* Property = *It;
void* PropAddr = Property->ContainerPtrToValuePtr<void*>(FuncParamsStructAddr);
if ((Property->PropertyFlags & CPF_Parm) == CPF_Parm && (Property->PropertyFlags & CPF_ReturnParm) == 0)
{
int32 Size = Property->GetSize();
int32 Offset = Property->GetOffset_ForInternal();
FMemory::Memcpy(PropAddr, InParamsByte + Offset, Size);
}
}
Object->ProcessEvent(Function, FuncParamsStructAddr);
OutParamsBytes = (uint8*)&OutParams;
for (TFieldIterator<FProperty> It(Function); It; ++It)
{
FProperty* Prop = *It;
if (Prop->PropertyFlags & CPF_OutParm)
{
const void* PropBuffer = Prop->ContainerPtrToValuePtr<void*>(FuncParamsStructAddr);
Prop->CopyCompleteValue(OutParamsBytes, PropBuffer);
OutParamsBytes += Prop->GetSize();
}
}
}
template<typename... TReturn, typename... TArgs>
void CallUFunction(UObject* Object, FName FunctionName, TTuple<TReturn...>& OutParams, TArgs&&... Args)
{
if (!Object)
{
return;
}
UFunction* Func = Object->FindFunction(FunctionName);
if (!Func)
{
return;
}
InternalCallUFunction<TReturn...>(Object, Func, OutParams, Forward<TArgs>(Args)...);
}
TTuple<> EmptyReturn;
if (Property && Property->GetCPPType() == TEXT("bool"))
{
InternalCallUFunction(Object, Function, EmptyReturn, Input > 0);
}
else
{
InternalCallUFunction(Object, Function, EmptyReturn, Input);
}
委托
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnGridEmpty, int32, X, int32, Y);
FOnGridEmpty OnGridEmpty;
OnGridEmpty.AddDynamic(this, &UCardManager::UpperCardExtend);
智能指针
https://blog.csdn.net/weixin_43844254/article/details/99752599
https://zhuanlan.zhihu.com/p/369974105
广播
网络同步
《Exploring in UE4》关于网络同步的理解与思考[概念理解]
《Exploring in UE4》网络同步原理深入(上)[原理分析]
《Exploring in UE4》网络同步原理深入(下)
关于网络同步的理解与思考
属性复制
构造函数:bReplicates = true;
virtual void GetLifetimeReplicatedProps(TArray< FLifetimeProperty >& OutLifetimeProps) const override;
void ARPGAICharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(ARPGAICharacter, Target);
}
UPROPERTY(Replicated)
仅在主控端同步
void AActor::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const
{
DOREPLIFETIME_CONDITION( AActor, ReplicatedMovement, COND_AutonomousOnly );
}
自定义Asset类型
https://blog.csdn.net/qq_29667889/article/details/109307203
https://zhuanlan.zhihu.com/p/41332747
射线检测
Log
定义自己的Log分类:
// .h
DECLARE_LOG_CATEGORY_EXTERN(YourLog, Log, All);
// .cpp
DEFINE_LOG_CATEGORY(YourLog);
-
输出Log
// 带端前缀 UKismetSystemLibrary::PrintString(GetWorld(), TEXT(""), true, false);
#include "Engine/Engine.h" // 五秒。 // 参数中的-1"键"值类型参数能防止该消息被更新或刷新。 GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("Hello World"));
-
输出Log
UE_LOG(LogTemp, Log, TEXT("Change Text By Cpp"));
UE_LOG(LogTemp, Log, TEXT("UseSpell: %d"), cooling.Num());
UE_LOG(LogTemp, Log, TEXT("%s"), *MyString);
Slate
https://unrealcommunity.wiki/creating-an-editor-module-x64nt5g3
https://docs.unrealengine.com/4.27/zh-CN/ProgrammingAndScripting/Slate/Overview/
https://zhuanlan.zhihu.com/p/418944210
https://zhuanlan.zhihu.com/p/450038103
https://zhuanlan.zhihu.com/p/377745543
https://zhuanlan.zhihu.com/p/45682313
性能分析
stats性能埋点 初步
stats性能埋点
如何应对CPU帧率瓶颈和卡顿
Unreal Insights
开始结束性能录制:
录制后自动打开Insight
IK-Rig
https://www.bilibili.com/video/BV1BS4y1H7qL?p=2&vd_source=4de8d23df531f514b8f60748534ebf62
异步操作
UE4原子操作与无锁编程
UE4/UE5的TaskGraph
UE4 多线程 创建与使用
UE4 C++基础 - 多线程
C++实现异步操作Delay
UE4关卡流式加载与Latent机制
包、模块、资源管理
UE4的资源管理
UE4加载模块分析笔记(一)
UE4加载模块分析笔记(二)
资源引用 FSoftObjectPath TSoftObjectPtr TSoftClassPtr
文件操作
查找文件 移动文件 删除文件
FPaths::ProjectContentDir() + XXX
创建UAsset资源
序列化保存关卡内所有Actor到UAsset
本地化
https://blog.csdn.net/u010385624/article/details/89705285