Hello, I’m trying to get a dynamic decal that warps around terrain properly. It is a square indicator for where the camera is looking (later to be used as an indicator when selecting a location to build something).
Currently I am doing this by tracing each tick and drawing the decal to where they are looking. For flat terrain this work fine:
http://static.pantherdev.com/misc/terrain_flat.jpg
However, for hilly terrain it is clipped by the landscape:
http://static.pantherdev.com/misc/terrain_hill.jpg
I tried to destroy and recreate the decal when it needed to go to a new location, but it didn’t help. I’m hoping I am just misunderstanding something simple since I am so new to the platform.
Here is how the tick trace is currently implemented:
void AMinervaCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
FHitResult RV_Hit(EForceInit::ForceInit);
if (bIsBuilding && DoTrace(&RV_Hit, &RV_InteractionTraceParams) && RV_Hit.bBlockingHit)
{
ShowBuildEffect(RV_Hit);
}
else
{
HideBuildEffect();
}
}
FORCEINLINE void AMinervaCharacter::ShowBuildEffect(const FHitResult Impact)
{
if (BuildSelectorEffect == NULL)
{
FActorSpawnParameters spawnParams = FActorSpawnParameters();
spawnParams.Name = FName(TEXT("BuildSelector"));
spawnParams.Owner = this;
BuildSelectorEffect = GetWorld()->SpawnActor<ABuildSelectorEffect>(BuildSelectorTemplate, spawnParams);
}
BuildSelectorEffect->Show(Impact);
}
FORCEINLINE void AMinervaCharacter::HideBuildEffect()
{
if (BuildSelectorEffect != NULL)
{
BuildSelectorEffect->Hide();
}
}
bool AMinervaCharacter::DoTrace(FHitResult* RV_Hit, FCollisionQueryParams* RV_TraceParams)
{
if (Controller == NULL) // access the controller, make sure we have one
{
return false;
}
// get the camera transform
FVector CameraLoc;
FRotator CameraRot;
GetActorEyesViewPoint(CameraLoc, CameraRot);
//setup the trace
const FVector Start = CameraLoc;
const FVector End = CameraLoc + (CameraRot.Vector() * PlayerInteractionDistance);
// set some parameters
RV_TraceParams->bTraceComplex = true;
RV_TraceParams->bTraceAsyncScene = true;
RV_TraceParams->bReturnPhysicalMaterial = true;
// do the line trace
bool DidTrace = GetWorld()->LineTraceSingle(
*RV_Hit, //result
Start, //start
End, //end
ECollisionChannel::ECC_WorldStatic, //collision channel
*RV_TraceParams
);
return DidTrace;
}
Here is the BuildSelectorEffect’s Show/Hide methods:
void ABuildSelectorEffect::Hide()
{
if (Decal != NULL)
{
Decal->DetachFromParent();
}
}
void ABuildSelectorEffect::Show(FHitResult Impact)
{
// no need to do anything if impact point is the same
if (SurfaceHit.ImpactPoint == Impact.ImpactPoint) {
return;
}
// update the surface hit we have stored
SurfaceHit = Impact;
if (Decal == NULL)
{
if (DefaultDecal.DecalMaterial)
{
// spawn a new decal at the new location
Decal = UGameplayStatics::SpawnDecalAttached(
DefaultDecal.DecalMaterial,
FVector(DefaultDecal.DecalSize, DefaultDecal.DecalSize, 1.0f),
SurfaceHit.Component.Get(),
SurfaceHit.BoneName,
SurfaceHit.ImpactPoint,
SurfaceHit.ImpactNormal.Rotation(),
EAttachLocation::KeepWorldPosition,
DefaultDecal.LifeSpan
);
}
}
else
{
SurfaceHit.ImpactPoint.X = FMath::GridSnap(SurfaceHit.ImpactPoint.X, GridSnapSize);
SurfaceHit.ImpactPoint.Y = FMath::GridSnap(SurfaceHit.ImpactPoint.Y, GridSnapSize);
if (!Decal->IsAttachedTo(SurfaceHit.Component.Get())) {
Decal->DetachFromParent();
Decal->AttachTo(SurfaceHit.Component.Get());
}
Decal->SetWorldLocationAndRotation(SurfaceHit.ImpactPoint, SurfaceHit.ImpactNormal.Rotation());
}
}