4

我正在尝试构建一个可以在边缘环绕的矩形网格。任何玩电子游戏的人都可能熟悉这个概念:在世界地图上朝一个方向走得足够远,你最终会回到起点。但是,这会在设置视口时造成一些困难,因为边缘可以滚动到负坐标区域。

取负坐标并确定其实际值很容易:

function GetRealCoords(value: TPoint): TPoint;
begin
   result := ModPoints(AddPoints(value, MAP_SIZE), MAP_SIZE);
end;

其中 AddPoints 和 ModPoints 只需将+mod运算符分别应用于两个输入的每个坐标以产生输出值。

麻烦的是扭转这个操作。给定一个坐标为正的点和一个 TRect ,其中 Top 和 Left 值可以是正数或负数,(并且 Bottom 或 Right 可能超出地图的边缘),并且在全局范围内声明 MAP_SIZE,是有什么方法可以确定该点是否落在查看矩形所覆盖的区域内,而不必运行相同的计算多达四个不同的时间?

4

3 回答 3

4

我相信是这样。

我能想到的最糟糕的情况 (grid=[0,1)x[0,1) ) 是:Top=-0.25,Left=-0.25, Bottom=0.25, Right=0.25

这看起来像(包装时):

 ______
|_|  |_|
|      |
|_    _|
|_|__|_|

现在,您必须测试四个角以查看该点是否位于其中。但是,我相信通过在空间 [1,2)x[1,2) 中进行测试,您可以避免该问题,因为它再次变成了一个矩形。

 ______
|      |
|      |
|     _|_
|____|   |
     |___|

通过计算矩形的宽度和高度来简化问题。

Width=Mod(Right-Left+MAP_SIZE,MAP_SIZE)
Height=Mod(Bottom-Top+MAP_SIZE,MAP_SIZE)

现在,计算左上角的包裹位置

LeftNew=Mod(Left+MAP_SIZE,MAP_SIZE)
TopNew=Mod(Top+MAP_SIZE,MAP_SIZE)

计算新的底部和右侧:

RightNew=LeftNew+Width
BottomNew=TopNew+Height

现在,对于您希望测试的每个点,添加 MAP_SIZE,并测试它是否在新矩形内!

TestNew=AddPoints(Test,MAP_SIZE)

If (TestNew.X>=LeftNew && TestNew.X<=RightNew && TestNew.Y>=TopNew && TestNew.T<=BottomNew)
{
  We have a point inside!
}

我没有对此进行详尽的测试,但我目前认为它是正确的。

于 2009-07-12T02:10:16.427 回答
3

有了这个,您可以测试您的点是否在矩形内。

function PointInRect(aPoint:TPoint;aRect:TRect):boolean;
begin
  Result:=(aPoint.X >= aRect.Left  ) and 
          (aPoint.X <  aRect.Right ) and 
          (aPoint.Y >= aRect.Top   ) and 
          (aPoint.Y <  aRect.Bottom);
end;

但如果我正确理解你的描述,你想要这样的东西:

function NormalisePoint(aPoint:TPoint;aRect:TRect):TPoint;
var Width,Height:integer;
begin
  Width  := aRect.Right-aRect.Left;
  Height := aRect.Bottom-aRect.Top;

  if (Width=0) then
    aPoint.X := aRect.Left
  else
  begin
    while (aPoint.X< aRect.Left  ) do inc(aPoint.X,Width );
    while (aPoint.X>=aRect.Right ) do dec(aPoint.X,Width );
  end;

  if (Height=0) then
    aPoint.Y := aRect.Top
  else
  begin
    while (aPoint.Y< aRect.Top   ) do inc(aPoint.Y,Height);
    while (aPoint.Y>=aRect.Bottom) do dec(aPoint.Y,Height);
  end;
  Result := aPoint;
end;
于 2009-07-12T02:17:45.073 回答
0

在你做二维之前先考虑一维。你想知道一个数字是否在一个可能环绕的范围内,例如。在时钟的 7 到 2 范围内是 3。一旦你有了它,你就可以对 X 和 Y 坐标进行测试。

我对更简单问题的解决方案:

//assumes start and end are both in [0, divisor). (Because .net and most other languages do modulus WRONG.)
double ClockDistance(double start, double end, double clockSize) {
    return (end - start + clockSize) % clockSize;
}
//assumes inclusive bounds
bool ClockBetween(int n, double start, double end, double clockSize) {
    return ClockDistance(start, n, clockSize) 
           <= ClockDistance(start, end, clockSize);
}

概括为:

//assumes rects oriented so bottom < top, not the other way around like in UI
bool RectContains(double x, double y, double left, double bottom, double right, double top, double worldWidth, double wordlHeight) {
    return ClockBetween(x, left, right, worldWidth) 
           && ClockBetween(y, bottom, top, worldHeight);
}
于 2009-07-13T08:33:33.867 回答