FadeObjects-隐藏摄像机和角色之间的对象

图片

曾经,有必要编写一个模块以在摄像机和角色之间或RTS游戏的几个角色之间隐藏对象。 我想分享给那些开始使用虚幻引擎的人。 如果可以的话,本教程将使用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来代替计时器,但最好不要这样做,如果CPU上有大量对象,操作本身会改变材质,那会更好。

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/zh-CN425389/


All Articles