FadeObjects - Ocultar objetos entre a câmera e o personagem

imagem

Uma vez, foi necessário escrever um módulo para ocultar objetos entre a câmera e o personagem, ou entre vários personagens para um jogo de RTS. Quero compartilhar para aqueles que começaram sua jornada no Unreal Engine. Este tutorial, se você pode chamar assim, estará usando C ++, mas no projeto anexo no github haverá uma opção no Blueprint, a funcionalidade de ambos é idêntica.

Exemplo de vídeo


E então vamos lá. Dividimos nossa tarefa em várias pequenas:

  1. Coloque objetos entre a câmera e o personagem.
  2. Mude o material desses objetos para o desejado.
  3. Mude o material de volta para o que era se o objeto não interferisse na revisão de nosso personagem.

Precisamos de 2 temporizadores, um adiciona objetos à matriz para trabalhar com eles e o segundo para alterar o objeto em si. Nesse caso, altero o material de normal para ligeiramente transparente. Você pode substituir este material por outro adequado para você.

SFadeObjectsComponent.h

FTimerHandle timerHandle_ObjectComputeTimer; FTimerHandle timerHandle_AddObjectsTimer; 

Assim que o objeto estiver na matriz, para mais trabalhos, precisamos lembrar algumas de suas propriedades, por exemplo, que material ele possuía antes de alterá-lo, porque precisamos alterá-lo novamente. Além disso, no nosso caso, ocultamos e, quando necessário, retornamos o estado inicial do objeto gradualmente, portanto, precisamos lembrar seu estado atual.

Para fazer isso, criaremos uma estrutura:
 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; } }; 


Também precisamos de algumas configurações disponíveis no Blueprint para a operação flexível de nosso componente. Como o tipo de colisão para identificar objetos, o tamanho da cápsula (o próprio feixe) do personagem para a câmera, quanto maior o tamanho, mais objetos ao redor do personagem serão capturados.

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

A distância em que os objetos serão ocultados.

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

E, claro, a classe de personagens em si ou outros atores na cena.

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

Não analisaremos todas as variáveis ​​usadas, você pode se familiarizar independentemente com as fontes.

Vamos para a implementação. No BeginPlay, execute nossos temporizadores. Em vez de temporizadores, você pode, é claro, usar o EventTick, mas é melhor não fazer isso, a operação em si é para alterar materiais se um grande número de objetos for muito caro para a CPU.

SFadeObjectsComponent.cpp

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

A função de adicionar um objeto a uma matriz. Aqui, eu gostaria de observar que ele adiciona não apenas o próprio ator na cena, mas também seus componentes e SkeletalMesh, se necessário.
 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(); } 


Uma função para trabalhar com objetos que alteram o material do original para o necessário e vice-versa.
 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); } } } } 


Não há nada especial para contar aqui, alguns trechos de código e assim por diante com comentários. O vídeo no início mostra o resultado. Quero adicionar apenas as configurações com as quais o componente é inicializado.

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

Talvez alguém seja útil. Ou alguém dirá sua opinião nos comentários.

Link para fonte

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


All Articles