Problem Description
I was follwing a tutorial for unity engine to make a feature of wall merging from zelda in unreal engine, and I have implemented the algorithm as described in the tutorial. The system works correctly when the angle between two wall faces is exactly 90°, but breaks when the angle is anything else (e.g. 120°, 135°, etc).
Symptoms:
- Rotation overshoots or undershoots the target wall
- Works perfectly for 90 degree corners
I suspect the issue comes from distance to turn calculations becasuse i assume the angle to be 90 degrees, but I’m not sure how to adjust it for arbitrary angles.
Minimal Reproducible Example
struct FMeshPoint
{
FVector Position;
FVector Normal;
};
TArray MeshPoints;
TArray CornerPoints;
float OffsetMargin = 0.1f;
float StepSize = 50.f;
int32 MaxSteps = 150;
void FindNext(const FVector& StartPoint, const FVector& StartNormal)
{
// Store current point
MeshPoints.Add({ StartPoint, StartNormal });
// Detect corner when surface normal changes
if (MeshPoints.Num() > 1)
{
const FVector PrevNormal = MeshPoints.Last(1).Normal;
// Threshold works for 90° corners, fails for others
if (PrevNormal.Dot(StartNormal) < 0.98f)
{
CornerPoints.Add({ StartPoint, StartNormal });
}
}
if (MeshPoints.Num() >= MaxSteps)
return;
// Tangent along the wall
const FVector Tangent = FVector::CrossProduct(StartNormal, FVector::UpVector).GetSafeNormal();
// Offset slightly off the wall to avoid self-hit
const FVector TraceStart = StartPoint + StartNormal * OffsetMargin;
const FVector TraceEnd = TraceStart + Tangent * StepSize;
FHitResult Hit;
const bool bHit = GetWorld()->LineTraceSingleByChannel(
Hit,
TraceStart,
TraceEnd,
ECC_WorldStatic
);
if (bHit)
{
FindNext(Hit.ImpactPoint, Hit.ImpactNormal);
}
}
void WallMove(float AxisValue, float DeltaTime)
{
const FVector Desired =
AxisValue > 0 ? Target.Position : Origin.Position;
ActorPosition = FMath::VInterpConstantTo(ActorPosition, Desired, DeltaTime, FMath::Abs(AxisValue) * 200.f);
const float DistFromOrigin = FVector::Distance(ActorPosition, Origin.Position);
const float TotalDist = FVector::Distance(Origin.Position, Target.Position);
if (DistFromOrigin < DistanceToTurn || DistFromOrigin > TotalDist - DistanceToTurn)
{
StartRotation(AxisValue > 0);
}
}
void StartRotation(bool bRight)
{
RotationLerp = 0.f;
CurrentNormal = CurrentNormal.GetSafeNormal();
TargetNormal = Target.Normal.GetSafeNormal();
PivotPoint = ComputePivotPoint(Origin.Position, Target.Position, CurrentNormal, DistanceToTurn);
}
void CornerRotate(float AxisValue, float DeltaTime)
{
RotationLerp = FMath::Clamp( RotationLerp + AxisValue * DeltaTime * RotationSpeed, 0.f, 1.f);
// Works for 90°, breaks for arbitrary angles
const FVector BlendedNormal = FMath::Lerp(CurrentNormal, TargetNormal, RotationLerp).GetSafeNormal();
// Actor is rotated around PivotPoint
ActorPosition = PivotPoint + (ActorPosition - PivotPoint) .RotateAngleAxis( FMath::RadiansToDegrees( FMath::Acos( FVector::DotProduct(CurrentNormal, BlendedNormal) ) ), FVector::UpVector);
}
FVector ComputePivotPoint( const FVector& CornerPos, const FVector& NextCorner, const FVector& Normal, float Offset)
{
const FVector WallDir = (NextCorner - CornerPos).GetSafeNormal();
const FVector LineAStart = CornerPos + WallDir * Offset;
const FVector LineADir = Normal;
const FVector LineBStart = CornerPos + Normal * Offset;
const FVector LineBDir = WallDir;
FVector Intersection;
LineLineIntersection( Intersection, LineAStart, LineADir, LineBStart, LineBDir );
return Intersection;
}
bool LineLineIntersection( FVector& OutIntersection, const FVector& P1, const FVector& D1, const FVector& P2, const FVector& D2)
{
const FVector R = P2 - P1;
const FVector C = FVector::CrossProduct(D1, D2);
const float Denom = C.SizeSquared();
if (Denom < KINDA_SMALL_NUMBER)
return false;
const float T = FVector::DotProduct( FVector::CrossProduct(R, D2), C ) / Denom;
OutIntersection = P1 + D1 * T;
return true;
}
Question
This algorithm works perfectly for 90 degree corners, but breaks for arbitrary angles. I suspect the issue is in the distance to turn calculations, which assume a 90 degree angle. How can I adjust the algorithm to handle arbitrary angles between wall faces?
Any help or insight would be appreciated.
Additional Context
- Unreal Engine version: 5.7.3