Я следовал руководству по движку Unity, чтобы создать функцию слияния стен из zelda в движке Unreal, и я реализовал алгоритм, как описано в руководстве.
Система работает правильно, когда угол между двумя гранями стен составляет ровно 90°, но перестает работать, когда угол равен любому другому (например, 120°, 135° и т. д.).
Признаки:
- Вращение выходит за пределы или за пределы целевой стены.
- Отлично работает для углов 90 градусов.
Минимальный воспроизводимый пример
Код: Выделить всё
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;
}
Этот алгоритм отлично работает для углов в 90 градусов, но не работает для произвольных углов. Я подозреваю, что проблема в расчете расстояния до поворота, который предполагает угол 90 градусов. Как я могу настроить алгоритм для обработки произвольных углов между гранями стен?
Буду признателен за любую помощь или понимание.
Дополнительный контекст
- Версия Unreal Engine: 5.7.3
Подробнее здесь: https://stackoverflow.com/questions/798 ... wall-merge