FadeObjects - إخفاء الأشياء بين الكاميرا والشخصية

الصورة

ذات مرة ، كان من الضروري كتابة وحدة نمطية لإخفاء الأشياء بين الكاميرا والشخصية ، أو بين العديد من الشخصيات في لعبة RTS. أريد أن أشارك أولئك الذين بدأوا رحلتهم في محرك Unreal. هذا البرنامج التعليمي ، إذا كان يمكنك تسميته ، فسيستخدم C ++ ، ولكن في المشروع المرفق على github سيكون هناك خيار في Blueprint ، فإن وظيفة كليهما متطابقة.

مثال فيديو


وهكذا ، دعنا نذهب. نقسم مهمتنا إلى عدة صغيرة:

  1. احصل على الأشياء بين الكاميرا والشخصية.
  2. قم بتغيير مادة هذه الأشياء إلى تلك المطلوبة.
  3. قم بتغيير المادة مرة أخرى إلى ما كانت عليه إذا لم يتداخل الكائن مع مراجعة شخصيتنا.

نحتاج إلى مؤقتين ، أحدهما لإضافة كائنات إلى الصفيف للعمل معهم ، والثاني لتغيير الكائن نفسه ، في هذه الحالة أقوم بتغيير المادة من عادي إلى شفاف قليلاً. يمكنك استبدال هذه المواد بأي مناسبة لك.

SFadeObjectsComponent.h

FTimerHandle timerHandle_ObjectComputeTimer; FTimerHandle timerHandle_AddObjectsTimer; 

بمجرد أن يكون الكائن في الصفيف ، لمزيد من العمل ، نحتاج إلى تذكر بعض خصائصه ، على سبيل المثال ، ما هي المادة التي كان عليها قبل أن نغيرها ، لأنه يجب علينا تغييرها مرة أخرى. أيضًا ، في حالتنا ، نخفي ، وعند الضرورة ، نعيد الحالة الأولية للكائن تدريجيًا ، لذلك نحتاج إلى تذكر حالته الحالية.

للقيام بذلك ، سنقوم بإنشاء هيكل:
 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; } }; 


نحتاج أيضًا إلى بعض الإعدادات المتاحة من Blueprint للتشغيل المرن لمكوننا. مثل نوع التصادم لتحديد الأشياء ، حجم الكبسولة (الحزمة نفسها) من الشخصية إلى الكاميرا ، كلما كان الحجم أكبر ، سيتم التقاط المزيد من الأشياء حول الشخصية.

 // 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; 

المسافة التي سيتم إخفاء الأشياء عندها.

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

وبالطبع ، فئة الشخصيات نفسها أو الممثلين الآخرين في المشهد.

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

لن نقوم بتحليل جميع المتغيرات المستخدمة ، يمكنك التعرف على المصادر بشكل مستقل.

دعنا ننتقل إلى التنفيذ. في BeginPlay ، قم بتشغيل مؤقتاتنا. بدلاً من المؤقتات ، يمكنك بالطبع استخدام EventTick ، ​​ولكن من الأفضل عدم القيام بذلك ، فالعملية نفسها لتغيير المواد إذا كان عدد كبير من الكائنات باهظ الثمن بالنسبة لوحدة المعالجة المركزية.

SFadeObjectsComponent.cpp

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

وظيفة إضافة كائن إلى صفيف. هنا أود أن أشير إلى أنه لا يضيف الممثل نفسه فقط في المشهد ، ولكن أيضًا مكوناته و SkeletalMesh ، إذا لزم الأمر.
 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(); } 


وظيفة للعمل مع الكائنات التي تغير المواد من الأصلي إلى المطلوب والعكس صحيح.
 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); } } } } 


لا يوجد شيء خاص لإخباره هنا ، بعض الأجزاء من التعليمات البرمجية وما إلى ذلك مع التعليقات. يظهر الفيديو في البداية النتيجة. أريد إضافة الإعدادات التي تمت تهيئة المكون بها فقط.

 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); 

ربما شخص ما سيكون مفيدا. أو سيقول شخص ما رأيهم في التعليقات.

رابط للمصدر

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


All Articles