1

我正在尝试解决一个问题,即在可以任意旋转和平移的空间内存在的盒子内找不到点的相对偏移量。

我知道盒子的 WorldSpace 位置(及其 4 个角,图像上的坐标是相对的)及其旋转。这些可以是任意的(它实际上是游戏中的 3D 触发体积,但我们只关心它在自上而下的 2D 平面中)。

在此处输入图像描述

看着它与轴对齐,红点相对位置将是

0.25, 0.25

如果要任意旋转盒子,我似乎无法弄清楚如何保持这一点,因为我们对同一点进行采样(它的世界位置将发生变化),即使盒子的世界旋转已经改变,它的相对位置也不会改变。

在此处输入图像描述

作为参考,红点表示存在于 Box 所包含的场景中的对象。

bool UPGMapWidget::GetMapMarkerRelativePosition(UPGMapMarkerComponent* MapMarker, FVector2D& OutPosition)
{
    bool bResult = false;
    if (MapMarker)
    {
        const FVector MapMarkerLocation = MapMarker->GetOwner()->GetActorLocation();
        float RelativeX = FMath::GetMappedRangeValueClamped(
            -FVector2D(FMath::Min(GetMapVolume()->GetCornerTopLeftLocation().X, GetMapVolume()->GetCornerBottomRightLocation().X), FMath::Max(GetMapVolume()->GetCornerTopLeftLocation().X, GetMapVolume()->GetCornerBottomRightLocation().X)),
            FVector2D(0.f, 1.f),
            MapMarkerLocation.X
        );

        float RelativeY = FMath::GetMappedRangeValueClamped(
            -FVector2D(FMath::Min(GetMapVolume()->GetCornerTopLeftLocation().Y, GetMapVolume()->GetCornerBottomRightLocation().Y), FMath::Max(GetMapVolume()->GetCornerTopLeftLocation().Y, GetMapVolume()->GetCornerBottomRightLocation().Y)),
            FVector2D(0.f, 1.f),
            MapMarkerLocation.Y
        );
        
        OutPosition.X = FMath::Abs(RelativeX);
        OutPosition.Y = FMath::Abs(RelativeY);

        bResult = true;
    }

    return bResult;
}

目前,您可以通过上面的代码看到我只使用 Box 的左上角和右下角来尝试计算偏移量,我知道这不是一个足够的解决方案,因为这样做不允许旋转(Id 需要也使用其他两个角)但是我一生都无法弄清楚我需要做什么才能达到解决方案。

FMath::GetMappedRangeValueClamped

这会将一个范围转换为另一个范围。例如,(20 - 50) 变为 (0 - 1)。

任何有关如何解决此问题的帮助/建议将不胜感激。

谢谢。

更新

@Voo 的评论帮助我意识到解决方案比预期的要简单得多。

在此处输入图像描述

通过知道框的 3 个角的位置,我能够找到这 3 个位置创建的 2 条线上的点,然后简单地将这些点映射到 0-1 范围内,无论框如何被翻译。

bool UPGMapWidget::GetMapMarkerRelativePosition(UPGMapMarkerComponent* MapMarker, FVector2D& OutPosition)
{
    bool bResult = false;
    if (MapMarker && GetMapVolume())
    {
        const FVector MapMarkerLocation = MapMarker->GetOwner()->GetActorLocation();
        const FVector TopLeftLocation = GetMapVolume()->GetCornerTopLeftLocation();
        const FVector TopRightLocation = GetMapVolume()->GetCornerTopRightLocation();
        const FVector BottomLeftLocation = GetMapVolume()->GetCornerBottomLeftLocation();

        FVector XPlane = FMath::ClosestPointOnLine(TopLeftLocation, TopRightLocation, MapMarkerLocation);
        FVector YPlane = FMath::ClosestPointOnLine(TopLeftLocation, BottomLeftLocation, MapMarkerLocation);

        // Convert the X axis into a 0-1 range.
        float RelativeX = FMath::GetMappedRangeValueUnclamped(
            FVector2D(GetMapVolume()->GetCornerTopLeftLocation().X, GetMapVolume()->GetCornerTopRightLocation().X),
            FVector2D(0.f, 1.f),
            XPlane.X
        );

        // Convert the Y axis into a 0-1 range.
        float RelativeY = FMath::GetMappedRangeValueUnclamped(
            FVector2D(GetMapVolume()->GetCornerTopLeftLocation().Y, GetMapVolume()->GetCornerBottomLeftLocation().Y),
            FVector2D(0.f, 1.f),
            YPlane.Y
        );

        OutPosition.X = RelativeX;
        OutPosition.Y = RelativeY;

        bResult = true;
    }

    return bResult;
}

上面的代码是原始问题的修改代码,具有正确的解决方案。

4

1 回答 1

0

假设原点在(x0, y0),其他三个在(x_x_axis, y_x_axis)(x_y_axis, y_y_axis)(x1, y1),对象在(x_obj, y_obj)

对所有五个点执行这些操作:
(1) 将所有五个点平移(-x0, -y0), 使原点移动到(0, 0)(之后(x_x_axis, y_x_axis)移动到(x_x_axis - x0, y_x_axis - y0));
(2)将所有五个点旋转(0, 0)-arctan((y_x_axis - y0)/(x_x_axis - x0))使(x_x_axis - x0, y_x_axis - y0)移动到x_axis;
(3)假设新坐标为(0, 0), (x_x_axis', 0), (0, y_y_axis'), (x_x_axis', y_y_axis'), (x_obj', y_obj'), 则物体的零一坐标为(x_obj'/x_x_axis', y_obj'/y_y_axis');

旋转公式:(x_new, y_new)=(x_old * cos(theta) - y_old * sin(theta), x_old * sin(theta) + y_old * cos(theta))

更新:
注意:

  1. 如果使用距离方法,则必须注意坐标的符号,以防将来物体可能会离开场景;
  2. 如果以后场景中还有其他的变换(比如游戏中有镜像魔法的对称变换,游戏中有冲击波、热浪或引力波的横切变换),那么距离法不再适用,您仍然需要反转场景中的所有转换才能获得对象的坐标。
于 2021-01-11T14:10:00.880 回答