UE4 C++ Tips


篇寫的是關於UE4的C++方面的小技巧:

1.在構造函數里

//構建組件
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
//把組件放到其它組件下
VRCamera->SetupAttachment(VROrigin);
//下面這條不能用於構造函數中,否則編輯器崩潰或報錯
//VRCamera->AttachToComponent(VROrigin, FAttachmentTransformRules::SnapToTargetIncludingScale);

2.加載資源

具體細節教程(非本人制作):https://ke.qq.com/course/308721

//同步加載,一般用於少量物體加載
//從內存中讀取文件,但由於還沒從硬盤中讀取,所以內存中沒有(耗時相對較短),因此讀取失敗
UHapticFeedbackEffect_Base* ShakeEffect = FindObject<UHapticFeedbackEffect_Base>(NULL,TEXT("HapticFeedbackEffect_Curve'/Game/VirtualRealityBP/Blueprints/MotionControllerHaptics.MotionControllerHaptics'"));

//從硬盤中讀取文件,放到內存中(耗時相對較長)
UHapticFeedbackEffect_Base* ShakeEffect = LoadObject<UHapticFeedbackEffect_Base>(NULL,TEXT("HapticFeedbackEffect_Curve'/Game/VirtualRealityBP/Blueprints/MotionControllerHaptics.MotionControllerHaptics'"));

//讀取文件,只能用於構造函數

  static ConstructorHelpers::FObjectFinder<UStaticMesh> Object(TEXT("StaticMesh'/Engine/BasicShapes/Sphere.Sphere'"));
  Sphere->SetStaticMesh(Object.Object);



.h:
FStreamableManager* WealthLoader;
TSharedPtr<FStreamableHandle> WealthHandle;

  //各種Class的地址
  UPROPERTY(EditAnywhere)
  TArray<TSoftClassPtr<UObject>> ClassWealthPaths;



.cpp:
//異步加載,一般用於大量物體加載 void AWelathActor::StreamableManagerOperate() { //創建加載管理器 WealthLoader = new FStreamableManager(); //執行異步加載,添加資源鏈接數組和加載完成回調函數,其中TexturePath為加載內容的地址 WealthHandle = WealthLoader->RequestAsyncLoad(TexturePath, FStreamableDelegate::CreateUObject(this, &AWelathActor::StreamableManagerLoadComplete));
  //如果加載的內容是UClass,則需要先在藍圖那賦值要加載的UClass,然后再進行加載

  //獲取所有資源路徑
  TArray<FSoftObjectPath> ObjectWealthPaths;
  for (int i = 0; i < ClassWealthPaths.Num(); ++i)
  {
  ObjectWealthPaths.Push(ClassWealthPaths[i].ToSoftObjectPath());
  }
  //進行異步加載
  WealthHandle = WealthLoader.RequestAsyncLoad(ObjectWealthPaths,
  FStreamableDelegate::CreateUObject(this, &AAsynClassActor::LoadWealthCompleted));

}

void AWelathActor::StreamableManagerLoadComplete()
{
    //加載完成后動態修改圖片
    TArray<UObject* >OutObjects;
    WealthHandle->GetLoadedAssets(OutObjects);
    for (int32 i = 0; i < OutObjects.Num(); ++i)
    {
        UTexture2D* WorkTexture = Cast<UTexture2D>(OutObjects[i]);
        if (WorkTexture)
        {
            TextureGroup.Add(WorkTexture);
        }
    }
}

//加載UClass

  void AAsynClassActor::LoadWealthCompleted()
  {
  //獲取所有Class
  TArray<UObject*> WealthObjects;
  WealthHandle->GetLoadedAssets(WealthObjects);
  for (int i = 0; i < WealthObjects.Num(); ++i)
  {
  //把Object轉為UClass
  UClass* WeathClass = Cast<UClass>(WealthObjects[i]);
  //生成AActor,由於這里的地址指向的都是AActor,故這里生成AActor.
  AActor* Wealthactor = GetWorld()->SpawnActor<AActor>(WeathClass, FVector(0.f, 0.f, 1000.f)
  , FQuat::Identity.Rotator());
  //填充到數組
  WealthActors.Push(Wealthactor);
  }
  }

 

3.通過UObjectLibrary獲取批量內容的地址

.h:
class UObjectLibrary* ObjectLibrary;

.cpp:
void AWelathActor::ObjectLibraryOperate()
{
    if (!ObjectLibrary)
    {
        ObjectLibrary = UObjectLibrary::CreateLibrary(UObject::StaticClass(), false, false);
        //添加到根那,防止被UE4的垃圾回收機制干掉
        ObjectLibrary->AddToRoot();
    }

    //搜索所有Texture的路徑
    ObjectLibrary->LoadAssetDataFromPath(TEXT("/Game/Resource/UI/Texture/MenuTex"));

    TArray<FAssetData> TextureData;
    ObjectLibrary->GetAssetDataList(TextureData);

    for (int32 i=0; i<TextureData.Num(); ++i)
    {
        TexturePath.AddUnique(TextureData[i].ToSoftObjectPath());
    }
}

4.計時器

.h:
FTimerHandle CountdownTimerHandle;
//如果委托事件有參數
void ShiningObject(AStaticMeshActor* Object); .cpp:
//事件委托 FTimerDelegate UpdateTextureDele = FTimerDelegate::CreateUObject(this, &AWelathActor::UpdateTexture);
//如果事件有參數
FTimerDelegate UpdateTextureDele = FTimerDelegate::CreateUObject(this, &ABIMVRPawn::ShiningObject, Object);
//每0.5秒執行一次事件委托 GetWorld()->GetTimerManager().SetTimer(CountdownTimerHandle, UpdateTextureDele, 0.5f, true); //停止運行定時器 GetWorldTimerManager().ClearTimer(CountdownTimerHandle);

 5.隨機數

//產生隨機整數(范圍1~5)
int AWelathActor::Rand5()
{
    FRandomStream Stream;
    Stream.GenerateNewSeed();
   //返回值優化
    return Stream.RandRange(1, 5);
}

 6.UE4的智能指針

  可參考:https://www.cnblogs.com/timy/p/8685953.html

  官方文檔:https://docs.unrealengine.com/en-us/Programming/UnrealArchitecture/SmartPointerLibrary

7.C++與藍圖交互

  參考視頻:https://ke.qq.com/course/308721

.h:
   //藍圖調用,C++實現
    UFUNCTION(BlueprintCallable, Category = "FrameWork")
        void CAFuncOne(int32 Input, bool& Output);
    //只能在藍圖實現
    UFUNCTION(BlueprintImplementableEvent, Category = "FrameWork")
        void CAFuncTwo(int32 Input, bool& Output);
    //藍圖和C++都可實現,C++實現需要加后綴_Implementation
    UFUNCTION(BlueprintNativeEvent, Category = "FrameWork")
        void CAFuncThree(int32 Input, bool& Output);
  
//變量在藍圖中可讀寫
  
  UPROPERTY(BlueprintReadWrite, Category = "FrameWork")

   int A;



.cpp:

void AFWCharacter::CAFuncOne(int32 Input, bool& Output)
{

}

void AFWCharacter::CAFuncThree_Implementation(int32 Input, bool& Output)
{

}

 8. C++中的代碼注釋有中文時的特殊處理

  如果不處理,可能會出現在藍圖中調用C++函數時,注釋亂碼的情況或者用C++寫了一個UE4模板,調用此模板時,c++的注釋亂碼的情況

  處理方法之一:將文件保存為utf8格式。方法:https://blog.csdn.net/jiegemena/article/details/79369650

9.UE4的內存管理

  UObject有一個垃圾回收系統來管理它們。而非UObject派生的(例如UStruct、UUserWidget等)則需要用智能指針來管理它們的生命周期。

  垃圾回收系統詳解:https://wiki.unrealengine.com/Garbage_Collection_%26_Dynamic_Memory_Allocation

10.調用&修改參數

  盡量用get()、set()來調用修改,直接改參數可能會失敗。(盡管參數是public的)

//修改參數成功
Cast<UStaticMeshComponent>(HighLightThis->GetRootComponent())->SetRenderCustomDepth(WantHighLight);
//修改參數失敗
Cast<UStaticMeshComponent>(HighLightThis->GetRootComponent())->bRenderCustomDepth = WantHighLight;

 11.打包可能遇到的問題&解決方法

解決:

 

12. Delay函數調用

.h:
//一定要BlueprintCallable
UFUNCTION(BlueprintCallable, Category = "RoomVR")
void FinishMission();

.cpp:

FLatentActionInfo Action;
Action.CallbackTarget = this;
Action.ExecutionFunction = "FinishMission";
Action.UUID = 123;
Action.Linkage = 0;
UKismetSystemLibrary::Delay(GetWorld(), 2.0f, Action);

 

13. 創建動態材質

//獲取材質
UMaterialInterface* HintMaterial = LoadObject<UMaterialInterface>(NULL, TEXT("Material'/Game/Material/SpecialBrick.SpecialBrick'"));

//創建動態材質
UMaterialInstanceDynamic* HintMaterialDynamic = UMaterialInstanceDynamic::Create(HintMaterial, nullptr);
//修改材質參數
HintMaterialDynamic->SetVectorParameterValue("Color", FLinearColor::Green);

 

14. 關於打印(Printstring)

//float轉Fstring
UKismetSystemLibrary::PrintString(this, "Value: " + FString::SanitizeFloat(1.23));
//bool轉FString
UKismetStringLibrary::Conv_BoolToString(true);

 

15. Timeline

.h:

    UPROPERTY()
    class UTimelineComponent* MyTimeLine;
    UPROPERTY()
    class UCurveFloat* FloatCurve;
    UFUNCTION()
    void TimelineCallback(float val);

    UFUNCTION()
    void TimelineFinishedCallback();

    void PlayTimeline();

    UPROPERTY()
    TEnumAsByte<ETimelineDirection::Type> TimelineDirection;    

.cpp:

AYourClass::AYourClass()
{
    static ConstructorHelpers::FObjectFinder<UCurveFloat> Curve(TEXT("/Game/Curves/C_MyCurve"));
    check(Curve.Succeeded());
    
    FloatCurve = Curve.Object;
}

void AYourClass::BeginPlay()
{
    FOnTimelineFloat onTimelineCallback;
    FOnTimelineEventStatic onTimelineFinishedCallback;

    Super::BeginPlay();
        
    if (FloatCurve != NULL)
    {
        MyTimeLine = NewObject<UTimelineComponent>(this, FName("TimelineAnimation"));
        //Timeline是來自藍圖的
        MyTimeLine->CreationMethod = EComponentCreationMethod::UserConstructionScript;
        //把它加到組件數組中,從而讓它得以保存
        this->BlueprintCreatedComponents.Add(MyTimeLine);
        //可以作為引用(聯網用)
        MyTimeLine->SetNetAddressable();
        //Set which object the timeline should drive properties on
        MyTimeLine->SetPropertySetObject(this);
        MyTimeLine->SetDirectionPropertyName(FName("TimelineDirection"));
        //Timeline不循環,長度為1秒
        MyTimeLine->SetLooping(false);
        MyTimeLine->SetTimelineLength(1.0f);
        MyTimeLine->SetTimelineLengthMode(ETimelineLengthMode::TL_LastKeyFrame);
        MyTimeLine->SetPlaybackPosition(0.0f, false,false);
        //綁定Timeline運行時,執行的曲線和事件;綁定Timeline結束時的事件。
        onTimelineCallback.BindUFunction(this, FName{ TEXT("TimelineCallback") });
        onTimelineFinishedCallback.BindUFunction(this, FName{ TEXT("TimelineFinishedCallback") });
        MyTimeLine->AddInterpFloat(FloatCurve, onTimelineCallback);
        MyTimeLine->SetTimelineFinishedFunc(onTimelineFinishedCallback);
        MyTimeLine->RegisterComponent();
        MyTimeLine->Play();
        
    }

void AYourClass::Tick(float deltaTime)
{
    Super::Tick(deltaTime);

    if (MyTimeline != NULL)
    {
        MyTimeline->TickComponent(deltaTime, ELevelTick::LEVELTICK_TimeOnly, NULL);
    }
}

void AYourClass::TimelineCallback(float interpolatedVal)
{
    // This function is called for every tick in the timeline.
}

void AYourClass::TimelineFinishedCallback()
{
    // This function is called when the timeline finishes playing.
}

void AYourClass::PlayTimeline()
{
    if (MyTimeline != NULL)
    {
        MyTimeline->PlayFromStart();
    }
}

 16. VS里不能啟動UE4時

  出現問題如下圖:

  

  解決辦法:

  

  然后就可以了。

 

17. 關於LoadObject<UBlueprint>

  打包后,應用讀取不了這個藍圖(在編輯器可以),故會出現問題。

  解決辦法:

  1. 將此藍圖本地化(可能可以,但由於本地化會觸發其它問題,導致打包失敗,故未測試)

  2. 如果讀此藍圖是為了獲取它的class,從而動態地生成此藍圖的話,可以用TSubclassOf,然后在項目中賦值。

  3. 使用FClassFinder:

.h:    
//控制器的類
TSubclassOf<ABIMVRController> BIMVRControllerBP;
    
.cpp:
static ConstructorHelpers::FClassFinder<ABIMVRController> 
        BIMVRControllerBPFinder(TEXT("Blueprint'/Game/VRPlayer/BIMVRController.BIMVRController_C'"));
    BIMVRControllerBP = BIMVRControllerBPFinder.Class;

 

  盡量不要用LoadObject<UBlueprint>。

 

18. UE4文件必須文件夾

  

  當然,沒有代碼的話,Source文件夾不需要。Media文件夾只是做模板的時候需要,其它時候可以不需要。

 

19. 生成物體

  

//生成物體
 AStaticMeshActor* CopyActor = GetWorld()->SpawnActor<AStaticMeshActor>(AStaticMeshActor::StaticClass());

 

20. 修改Plugins

  直接在UE4引擎上改是不行的。

  正確做法是把plugins從原Plugin中復制一份,且放進工程里編譯。

  

  

 

21. 制作模板

  如果需要制作自己的模板(UE4中直接調用),可參照https://blog.csdn.net/u014532636/article/details/72832926

  注意,如果模板里有自己寫的C++文件,注意這些C++文件名不要與模板名字雷同,如:

  

  如果模板的名字也叫BimVR,那么調用模板時會觸發UE4埋下的陷井:UE4會修改同名的C++文件名字和內容,導致BUG出現。

  總之小心命名就好。

 

22. 項目改名 

   把項目與VS關掉后,重命名此項目即可。改名后,把VS、Intermediate文件夾、Saved文件夾刪掉,然后右鍵項目重新生成VS文件。

  C++里面不需要進行修改,經測試,改名后,未發現什么問題。

 

23. 在本地文件中寫數據

  這段代碼可以生成word文檔,如果文檔絕對位置不存在目標文檔(FileName),則會創造一個該命名的word文檔。

  如果有,則會覆蓋原文檔。

bool ABIMVRPawn::WriteFile(FString TestString, FString FileName)
{
    if (FileName.IsEmpty())
    {
        FileName = "MyWord";
        UKismetSystemLibrary::PrintString(this, "FileName.IsEmpty");
    }

    //文檔相對位置
    FString Path = FString("Res/");
    //文檔絕對位置
    FString AbsoPath = FPaths::GameContentDir() + Path + FileName + ".doc";
    //輸出pdf文件,文件會損壞,估計要按照特定的格式輸入數據
    //FString AbsoPath = FPaths::GameContentDir() + Path + FileName + ".pdf";
    if (!TestString.IsEmpty())
    {
        if (FFileHelper::SaveStringToFile(TestString, *AbsoPath)) return true;
        else UKismetSystemLibrary::PrintString(this, "WriteFail: " + AbsoPath);
    }
    return false;
}

 

24. 關於指針指向的東西是否有效

  眾所周知,空指針會導致應用崩潰。所以,安全起見,使用指針前,先檢查它是否有效。如:

if(!MyGCProtectedObj) return;

 

  但是,在UE4里,僅僅這樣是不夠的,某些情況下,還是會崩。

  因為指針可能不是空的,但它指向的是未完全析構的UObject,此時使用此指針也會崩潰,故還要檢查指針指向的物體是否有效:

if(!MyGCProtectedObj) return;
if(!MyGCProtectedObj->IsValidLowLevel()) return;

 

25. UE4的垃圾回收系統

  參考官方的解釋:http://api.unrealengine.com/CHN/Programming/Introduction/index.html

  首先,UE4的垃圾回收系統是基於反射系統來實現的。 

  然后,這個系統有個叫根集的概念,該根集基本上是一個對象列表這些對象是回收程序知道將不會被垃圾回收的對象

  如何添加UObject到根集中?使用UPROPERTY,或者AddToRoot();

UPROPERTY()
   UObject* MyObject;

void CreateObject()
{
   MyObject = NewObject<UObject>();
 //手動添加到根集中
UObject* Object;
Object = NewObject<UObject>();
Object->AddToRoot();
}

  上述代碼中,我們新建了個UObject,且實例化了。

  當我們要刪除它時,我們可以把它變成空指針,然后它原本指向的東西會自動被垃圾回收系統檢測到,且刪除

MyObject = nullptr;

  或者手動刪除它:

MyObject->ConditionalBeginDestroy();

  Actor通常不會被垃圾回收。因為Actor會自動成為根集的一部分。

  故,我們要手動刪除它:

    AActor* TestGCActor;
    TestGCActor->Destroy();

  注意,上述代碼只是展示了刪除方法,具體應用時,TestGCActor應該先生成出來!

  調用Destroy()后,它們將不會被立即刪除,而是在下次垃圾回收時進行清理。

 

26. 生成物體

AStaticMeshActor* IntroducingActor = GetWorld()->SpawnActor<AStaticMeshActor>(AStaticMeshActor::StaticClass());

 

27. 類命名前綴

  • 派生自 Actor 的類帶有 A 前綴,如AController。

  • 派生自 Object 的類帶有 U 前綴,如UComponent。

  • Enums 的前綴是 E,如EFortificationType。

  • Interface 的前綴通常是 I,如IAbilitySystemInterface。

  • Template 的前綴是 T,如TArray。

  • 派生自 SWidget 的類(Slate UI)帶有前綴 S,如SButton。

  • 其他類的前綴為字母F ,如FVector。

 

28. Widget模塊

  如果想新建的類里包含Widget模塊,如:

  

  或者新建一個繼承自UUserWidget的c++類時,

  則需要在Build.cs文件中添加UMG模塊:

  

 

29. VS的代碼塊

  VS編輯器的一個命令:#pragma region 。。。 #pragma endregion。

  它可以把一堆代碼視為一個代碼塊,可以收縮或展開這段代碼。

  如:

#pragma region Test

void .....

void .....
 
void .....

#pragma endregion

  這段代碼塊名字為Test。

 

30. BindKey

  如果想用BindKey來把某個按鍵與事件綁定起來,如:

PlayerInputComponent->BindKey(EKeys::J, IE_Pressed, this, &ARPCCourseCharacter::KeyJEvent);

   則需要在Build.cs文件中添加Slate模塊:

  

 

31. GetAllActorsOfClass

  想獲取當前世界的某個類的所有物體時,可以用GetAllActorsOfClass:

TArray<AActor*> UIArray;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AIntroduceUI::StaticClass(), UIArray);

 

32. 對TMap進行For-Each

// TMap——迭代器返回鍵-值對
TMap<FName, AActor*> NameToActorMap = GetMapFromSomewhere();
for (auto& KVP :NameToActorMap)
{
    FName Name = KVP.Key;
    AActor* Actor = KVP.Value;

    // ...
}

   請記住,auto 關鍵字不會自動指定指針/引用,您需要自行添加。

 

33. 調用OnBeginOverLap

在藍圖里是這樣的:

  

 

 

 在C++里:

.h:
    UFUNCTION()
    virtual void CollisonEvent(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
	UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep,
	const FHitResult & SweepResult);

.cpp:
    	//碰撞Delegate
	FScriptDelegate CollisionDelegate;
	CollisionDelegate.BindUFunction(this, "CollisonEvent");
	//綁定開始觸碰事件
	TouchArea->OnComponentBeginOverlap.Add(CollisionDelegate);
  
void ALiftingActor::CollisonEvent(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
	
	if (OtherActor)
	{
		UKismetSystemLibrary::PrintString(this, "Result: " + OtherActor->GetName());
	}
	
	
}

  

  OnEndOverlap同理。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM