• News

• Industries

• Learning & Support

• Community

• Marketplace

Adjust collision penetration, but only along a given axis?

Not sure if anybody has tried this and is able to help. I’m trying to get a ‘Safe’ aim location for my games’ abilities. The pawns are just spheres and the game is top-down (but 3D), which makes life a little easier.

In order to get the desired ‘Aim Location’, I simply get the stick magnitude, multiply it by the abilities ‘range’, then do a trace down from the sky to find the floor at that location. I then offset that location by the height of the orb from it’s floor, and that’s my ‘Target Location’ Quite easy right?

``````

const FVector ShootDirection = FRotator(0.f, ShootYaw, 0.f).Vector();
const FVector2D AimDirXY = FVector2D(ShootDirection);
const FVector2D OrbPosXY = FVector2D(OrbLocation);

// Clamp to maximum possible based on ammo
// We have to reduce maximum a tiny bit, to account for floating point error when checking if we can fire or not.
const float MaxPossibleDistance = GetMaxPossibleTravelDistance();
const float ClampMax = FMath::Max(MaxPossibleDistance, AimableConfig.MinRange) - MAX_DISTANCE_FUDGE_FACTOR;
const float AimTravelDistance = FMath::Min(FMath::GetMappedRangeValueClamped(FVector2D(0.f, 1.f), FVector2D(AimableConfig.MinRange, AimableConfig.MaxRange), InAimMagnitude), ClampMax);
FVector2D AimEndPointXY = OrbPosXY + (AimDirXY * AimTravelDistance);

const FVector TraceStart = FVector(AimEndPointXY, WORLD_MAX);
const FVector TraceEnd = FVector(AimEndPointXY, MyWS->KillZ);

``````

Now here’s the tricky part - I need that resulting location to be ‘Made Safe’, so that the Sphere can be placed there without being blocked by surrounding geometry. This is how I do it right now, by getting the overlapping collisions at that location, and applying the required penetration adjustment. This is similar to how Unreal does it for actors and components.

``````

bool UECGameplayStatics::TryAdjustment(UWorld* InWorldContext, const FCollisionShape& InCollisionShape, const FVector& InTestLocation, FVector& OutSafeLocation, const ECollisionChannel InBlockingChannel, const FCollisionQueryParams& InQParams, const FCollisionResponseParams& InRParams)
{
// Compute Adjustment, if we found a blocking hit
TArray<FOverlapResult> Overlaps;
const bool bFoundBlockingHit = InWorldContext->OverlapMultiByChannel(Overlaps, InTestLocation, FQuat::Identity, InBlockingChannel, InCollisionShape, InQParams, InRParams);
if (bFoundBlockingHit)
{
FMTDResult MTDResult;
uint32 NumBlockingHits = 0;
for (const FOverlapResult& OverlapItr : Overlaps)
{
UPrimitiveComponent* const OverlapComponent = OverlapItr.GetComponent();
if (OverlapComponent && OverlapComponent->GetCollisionResponseToChannel(InBlockingChannel) == ECR_Block)
{
NumBlockingHits++;
const bool bSuccess = OverlapComponent->ComputePenetration(MTDResult, InCollisionShape, InTestLocation, FQuat::Identity);
if (bSuccess)
{
}
else
{
UE_LOG(LogECGame, Warning, TEXT("TryAdjustment: Invalid adjusted collision for %s (%s). Dist: %f"), *GetNameSafe(OverlapComponent), *GetNameSafe(OverlapComponent->GetOwner()), MTDResult.Distance);
return false;
}
}
}

if (NumBlockingHits == 0 || ProposedAdjustment.IsZero())
{
OutSafeLocation = InTestLocation;
return true;
}
else
{
return true;
}
}
else
{
OutSafeLocation = InTestLocation;
return true;
}
}

``````

Again - pretty simple right? The problem is, I ONLY want to adjust collision along the axis that I’m aiming along, because I don’t want the resulting aim position moving too much and I don’t want the position to exceed the range. I also don’t want it being propelled up into the Sky etc.

So, given the code above, if I know my ‘Aim Vector’, how can I make this work so that the ProposedAdjustment only goes back along the Aim Vector? I can’t just do loads of sphere traces, because there’s an almost infinite number of possibilities and I need this code to be as fast as possible.

Any ideas?

Just as an afterthought, I can do this, but it doesn’t necessarily mean I won’t still be overlapping some objects… hmm.

``````

// We can only adjusted collision BACK along the aim axis, unfortunately, this makes this an iterative approach :/
const float PenetrationDot = FVector::DotProduct(AdjustAxis, MTDResult.Direction);
if (PenetrationDot > 0.f)
{