FadeObjects - Sembunyikan objek antara kamera dan karakter

gambar

Suatu kali, perlu untuk menulis modul untuk menyembunyikan objek antara kamera dan karakter, atau antara beberapa karakter untuk game RTS. Saya ingin berbagi untuk mereka yang memulai perjalanan mereka di Unreal Engine. Tutorial ini, jika Anda bisa menyebutnya demikian, akan menggunakan C ++, tetapi dalam proyek terlampir di github akan ada opsi pada Blueprint, fungsi keduanya sama.

Contoh video


Jadi, ayo pergi. Kami membagi tugas kami menjadi beberapa hal kecil:

  1. Dapatkan objek antara kamera dan karakter.
  2. Ubah materi benda-benda ini ke yang diinginkan.
  3. Ubah materi kembali menjadi apa jika objek tidak mengganggu peninjauan karakter kita.

Kami membutuhkan 2 timer, satu untuk menambahkan objek ke array untuk bekerja dengannya, dan yang kedua untuk mengubah objek itu sendiri, dalam hal ini saya mengubah materi dari normal menjadi sedikit transparan. Anda dapat mengganti bahan ini dengan yang cocok untuk Anda.

SFadeObjectsComponent.h

FTimerHandle timerHandle_ObjectComputeTimer; FTimerHandle timerHandle_AddObjectsTimer; 

Segera setelah objek berada dalam array, untuk pekerjaan lebih lanjut kita perlu mengingat beberapa propertinya, misalnya, bahan apa yang dimilikinya sebelum kita mengubahnya, karena kita harus mengubahnya kembali. Juga, dalam kasus kami, kami bersembunyi, dan bila perlu, kami mengembalikan keadaan awal objek secara bertahap, jadi kami perlu mengingat keadaan saat ini.

Untuk melakukan ini, kami akan membuat struktur:
 USTRUCT() struct FFadeObjStruct { GENERATED_USTRUCT_BODY() UPROPERTY() UPrimitiveComponent* primitiveComp; UPROPERTY() TArray<UMaterialInterface*> baseMatInterface; UPROPERTY() TArray<UMaterialInstanceDynamic*> fadeMID; UPROPERTY() float fadeCurrent; UPROPERTY() bool bToHide; void NewElement(UPrimitiveComponent* newComponent, TArray<UMaterialInterface*> newBaseMat, <UMaterialInstanceDynamic*> newMID, float currentFade, bool bHide) { primitiveComp = newComponent; baseMatInterface = newBaseMat; fadeMID = newMID; fadeCurrent = currentFade; bToHide = bHide; } void SetHideOnly(bool hide) { bToHide = hide; } void SetFadeAndHide(float newFade, bool newHide) { fadeCurrent = newFade; bToHide = newHide; } //For Destroy void Destroy() { primitiveComp = nullptr; } //Constructor FFadeObjStruct() { primitiveComp = nullptr; fadeCurrent = 0; bToHide = true; } }; 


Kami juga memerlukan beberapa pengaturan yang tersedia dari Blueprint untuk operasi komponen kami yang fleksibel. Seperti jenis tabrakan untuk mengidentifikasi objek, ukuran kapsul (balok itu sendiri) dari karakter ke kamera, semakin besar ukurannya, semakin banyak objek di sekitar karakter yang akan ditangkap.

 // Check trace block by this UPROPERTY(EditAnywhere, Category = "Fade Objects") TArray<TEnumAsByte<ECollisionChannel>> objectTypes; // Trace object size UPROPERTY(EditAnywhere, Category = "Fade Objects") float capsuleHalfHeight; // Trace object size UPROPERTY(EditAnywhere, Category = "Fade Objects") float capsuleRadius; 

Jarak di mana objek akan disembunyikan.

 UPROPERTY(EditAnywhere, Category = "Fade Objects") float workDistance; 

Dan tentu saja, karakter kelas itu sendiri atau aktor lain di TKP.

 UPROPERTY(EditAnywhere, Category = "Fade Objects") UClass* playerClass; 

Kami tidak akan menganalisis semua variabel yang digunakan, Anda dapat secara mandiri membiasakan diri dengan sumber.

Mari beralih ke implementasi. Di BeginPlay, jalankan timer kami. Alih-alih timer, Anda dapat, tentu saja, menggunakan EventTick, tetapi lebih baik tidak melakukan ini, operasi itu sendiri untuk mengubah bahan jika sejumlah besar objek cukup mahal untuk CPU.

SFadeObjectsComponent.cpp

 GetWorld()->GetTimerManager().SetTimer(timerHandle_AddObjectsTimer, this, &USFadeObjectsComponent::AddObjectsToHide, addObjectInterval, true); GetWorld()->GetTimerManager().SetTimer(timerHandle_ObjectComputeTimer, this, &USFadeObjectsComponent::FadeObjWorker, calcFadeInterval, true); 

Fungsi menambahkan objek ke array. Di sini saya ingin mencatat bahwa itu tidak hanya menambah aktor sendiri dalam adegan, tetapi juga komponen dan SkeletalMesh, jika perlu.
 void USFadeObjectsComponent::AddObjectsToHide() { UGameplayStatics::GetAllActorsOfClass(this, playerClass, characterArray); for (AActor* currentActor : characterArray) { const FVector traceStart = GEngine->GetFirstLocalPlayerController(GetWorld())->PlayerCameraManager->GetCameraLocation(); const FVector traceEnd = currentActor->GetActorLocation(); const FRotator traceRot = currentActor->GetActorRotation(); FVector traceLentgh = traceStart - traceEnd; const FQuat acQuat = currentActor->GetActorQuat(); if (traceLentgh.Size() < workDistance) { FCollisionQueryParams traceParams(TEXT("FadeObjectsTrace"), true, GetOwner()); traceParams.AddIgnoredActors(actorsIgnore); traceParams.bTraceAsyncScene = true; traceParams.bReturnPhysicalMaterial = false; // Not tracing complex uses the rough collision instead making tiny objects easier to select. traceParams.bTraceComplex = false; TArray<FHitResult> hitArray; TArray<TEnumAsByte<EObjectTypeQuery>> traceObjectTypes; // Convert ECollisionChannel to ObjectType for (int i = 0; i < objectTypes.Num(); ++i) { traceObjectTypes.Add(UEngineTypes::ConvertToObjectType(objectTypes[i].GetValue())); } // Check distance between camera and player for new object to fade, and add this in array GetWorld()->SweepMultiByObjectType(hitArray, traceStart, traceEnd, acQuat, traceObjectTypes, FCollisionShape::MakeCapsule(capsuleRadius, capsuleHalfHeight), traceParams); for (int hA = 0; hA < hitArray.Num(); ++hA) { if (hitArray[hA].bBlockingHit && IsValid(hitArray[hA].GetComponent()) && !fadeObjectsHit.Contains(hitArray[hA].GetComponent())) { fadeObjectsHit.AddUnique(hitArray[hA].GetComponent()); } } } } // Make fade array after complete GetAllActorsOfClass loop for (int fO = 0; fO < fadeObjectsHit.Num(); ++fO) { // If not contains this component in fadeObjectsTemp if (!fadeObjectsTemp.Contains(fadeObjectsHit[fO])) { TArray<UMaterialInterface*> lBaseMaterials; TArray<UMaterialInstanceDynamic*> lMidMaterials; lBaseMaterials.Empty(); lMidMaterials.Empty(); fadeObjectsTemp.AddUnique(fadeObjectsHit[fO]); // For loop all materials ID in object for (int nM = 0; nM < fadeObjectsHit[fO]->GetNumMaterials(); ++nM) { lMidMaterials.Add(UMaterialInstanceDynamic::Create(fadeMaterial, fadeObjectsHit[fO])); lBaseMaterials.Add(fadeObjectsHit[fO]->GetMaterial(nM)); // Set new material on object fadeObjectsHit[fO]->SetMaterial(nM, lMidMaterials.Last()); } // Create new fade object in array of objects to fade FFadeObjStruct newObject; newObject.NewElement(fadeObjectsHit[fO], lBaseMaterials, lMidMaterials, immediatelyFade, true); // Add object to array fadeObjects.Add(newObject); // Set collision on Primitive Component fadeObjectsHit[fO]->SetCollisionResponseToChannel(ECC_Camera, ECR_Ignore); } } // Set hide to visible true if contains for (int fOT = 0; fOT < fadeObjectsTemp.Num(); ++fOT) { if (!fadeObjectsHit.Contains(fadeObjectsTemp[fOT])) { fadeObjects[fOT].SetHideOnly(false); } } // Clear array fadeObjectsHit.Empty(); } 


Fungsi untuk bekerja dengan objek yang mengubah bahan dari aslinya ke yang diperlukan dan sebaliknya.
 void USFadeObjectsComponent::FadeObjWorker() { if (fadeObjects.Num() > 0) { // For loop all fade objects for (int i = 0; i < fadeObjects.Num(); ++i) { // Index of iteration int fnID = i; float adaptiveFade; if (fnID == fadeObjects.Num()) { adaptiveFade = nearObjectFade; } else { adaptiveFade = farObjectFade; } // For loop fadeMID array for (int t = 0; t < fadeObjects[i].fadeMID.Num(); ++t) { float targetF; const float currentF = fadeObjects[i].fadeCurrent; if (fadeObjects[i].bToHide) { targetF = adaptiveFade; } else { targetF = 1.0f; } const float newFade = FMath::FInterpConstantTo(currentF, targetF, GetWorld()->GetDeltaSeconds(), fadeRate); fadeObjects[i].fadeMID[t]->SetScalarParameterValue("Fade", newFade); currentFade = newFade; fadeObjects[i].SetFadeAndHide(newFade, fadeObjects[i].bToHide); } // remove index in array if (currentFade == 1.0f) { for (int bmi = 0; bmi < fadeObjects[fnID].baseMatInterface.Num(); ++bmi) { fadeObjects[fnID].primitiveComp->SetMaterial(bmi, fadeObjects[fnID].baseMatInterface[bmi]); } fadeObjects[fnID].primitiveComp->SetCollisionResponseToChannel(ECC_Camera, ECR_Block); fadeObjects.RemoveAt(fnID); fadeObjectsTemp.RemoveAt(fnID); } } } } 


Tidak ada yang istimewa untuk diceritakan di sini, beberapa potong kode dan sebagainya dengan komentar. Video di awal menunjukkan hasilnya. Saya ingin menambahkan hanya pengaturan dengan mana komponen diinisialisasi.

 PrimaryComponentTick.bCanEverTick = false; bEnable = true; addObjectInterval = 0.1f; calcFadeInterval = 0.05f; fadeRate = 10.0f; capsuleHalfHeight = 88.0f; capsuleRadius = 34.0f; workDistance = 5000.0f; nearCameraRadius = 300.0f; nearObjectFade = 0.3; farObjectFade = 0.1; immediatelyFade = 0.5f; // Add first collision type objectTypes.Add(ECC_WorldStatic); 

Mungkin seseorang akan berguna. Atau seseorang akan mengatakan pendapatnya di komentar.

Tautan ke sumber

Source: https://habr.com/ru/post/id425389/


All Articles