Anonymous
Unreal порт для Zelda Wall Merge
Сообщение
Anonymous » 26 фев 2026, 12:02
Описание проблемы
Я следовал руководству по движку Unity, чтобы создать функцию слияния стен из zelda в движке Unreal, и я реализовал алгоритм, как описано в руководстве.
Система работает правильно, когда угол между двумя гранями стен составляет
ровно 90° , но
перестает работать, когда угол равен любому другому (например, 120°, 135° и т. д.).
Признаки:
Вращение выходит за пределы или за пределы целевой стены.
Отлично работает для углов 90 градусов.
Я подозреваю, что проблема связана с расчетами
расстояния до поворота , поскольку я предполагаю, что угол равен 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
1772096563
Anonymous
[b]Описание проблемы[/b] Я следовал руководству по движку Unity, чтобы создать функцию слияния стен из zelda в движке Unreal, и я реализовал алгоритм, как описано в руководстве. Система работает правильно, когда угол между двумя гранями стен составляет [b]ровно 90°[/b], но [b]перестает работать, когда угол равен любому другому[/b] (например, 120°, 135° и т. д.). [b]Признаки:[/b] [list] [*]Вращение выходит за пределы или за пределы целевой стены. [*]Отлично работает для углов 90 градусов. [/list] Я подозреваю, что проблема связана с расчетами [b]расстояния до поворота[/b], поскольку я предполагаю, что угол равен 90 градусов, но я не уверен, как отрегулировать его для произвольных углов. [b]Минимальный воспроизводимый пример[/b] [code] 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; } [/code] [b]Вопрос[/b] Этот алгоритм отлично работает для углов в 90 градусов, но не работает для произвольных углов. Я подозреваю, что проблема в расчете расстояния до поворота, который предполагает угол 90 градусов. Как я могу настроить алгоритм для обработки произвольных углов между гранями стен? Буду признателен за любую помощь или понимание. [b]Дополнительный контекст[/b] [list] [*]Версия Unreal Engine: 5.7.3 [/list] Подробнее здесь: [url]https://stackoverflow.com/questions/79896838/unreal-port-for-zelda-wall-merge[/url]