【问题标题】:Calculate distance between points in matrix connected in all directions计算所有方向连接的矩阵中点之间的距离
【发布时间】:2021-04-26 23:23:00
【问题描述】:

我正在尝试制作一款在线游戏,我想在其中模拟一个全方位连接的世界,就像“PAC-MAN”一样。当玩家越过地图的边界时,他将在网格的另一边。

到目前为止一切顺利,我设法构建了一个包含 10x10 矩阵的服务器(我附上了一些 images 作为参考),并使用一些模块化算法计算并将相应的图块发送给客户端给定玩家位置的地图。

所以以“view_distance = 2”为例,服务器会根据玩家的位置发送相应的图块:

Player position (4,4)

Player position (9,5)

Player position (0,0)

我想我的观点是正确的,现在让我们面对我的问题。

当客户端从服务器获取要渲染的图块列表时,它需要计算每个图块到玩家的距离(单位向量),以便它可以在正确的位置实例化它。每个实例图块都有一个脚本,每次玩家移动时都会重新计算与玩家的距离,因此当它比“view_distance”更远时它会自行销毁。

例如,如果我的客户端位于 (9,5) 位置,则图块 (7,7) 的单位向量将为 (-2,2)

客户有以下信息:

  • 他所在的瓷砖:Globals.PlayerInfo.PlayerPosition
  • 地图的长度:Globals.MAP_LENGHT(在本例中为 10)
  • 视距:Globals.DATA_DISTANCE(本例为 2)

如何计算单位向量?我做了以下功能,但它似乎不起作用。我错过了什么吗?

public static Position GetPlayerDistance(Position position)
    {
        var unitVector = new Position() { X = position.X, Y = position.Y };
        if (position.Y > Math.Truncate(Globals.PlayerInfo.PlayerPosition.Y) + Globals.DATA_DISTANCE)
        {
            unitVector.Y = position.Y - Globals.MAP_LENGHT;
        }

        if (position.X > Math.Truncate(Globals.PlayerInfo.PlayerPosition.X) + Globals.DATA_DISTANCE)
        {
            unitVector.X = position.X - Globals.MAP_LENGHT;
        }
        unitVector.X -= (float)Math.Truncate(Globals.PlayerInfo.PlayerPosition.X);
        unitVector.Y -= (float)Math.Truncate(Globals.PlayerInfo.PlayerPosition.Y);

        return unitVector;
    }

【问题讨论】:

    标签: c# unity3d matrix modular-arithmetic


    【解决方案1】:

    你自己说吧,格子是connected in all directions。因此,由于您的网格是“无限的”,因此每个位置都存在“无限”的次数。您要寻找的不是两点之间的距离,而是多种可能性中的最小距离。

    不过,不用担心 ;) 在每个方向(上、下、左、右)检查一次就足够了,并在这些方向上选择最小的距离,因为无论如何其他方向都会更远。

    我所说的只是一个例子。 让我们说

    • 玩家在1,1(红色)
    • 敌人在8,2(蓝色)

    因此,如果我们想获得 X 轴上的最小距离,我们只需检查左右两个方向即可。

    在这种情况下,找到左边的下一个位置很简单:它是实际的玩家位置x = 1

    现在我们要向右移动什么? → 我们只是虚拟地扩展网格(浅灰色)并将玩家位置映射到它在扩展网格中的位置 →x = 11(浅红色)。

    这是一个更好地可视化它的图像

    在代码中,这可能看起来像例如

    public static Position GetPlayerDistance(Position position)
    {
        // Get values local to not go through the accessors all the time
        Position playerPos = Globals.PlayerInfo.PlayerPosition;
        int playerX = (int)playerPos.X;
        int playerY = (int)playerPos.Y;
    
        int ownX = (int)position.X;
        int ownY = (int)position.Y;
    
        // On the X axis gather the next actual or virtual player position
        // where virtual means as if the grid was extended
    
        // Per default assume the positions are equal  
        var nextXLeft = ownX;
        var nextXRight = ownX;
    
        // Is the player actually left of us?
        if(playerX < ownX)
        {
            // Then trivial: the next left position is the actual one
            nextXLeft = playerX;
            // The next right position is a virtual one so we pretend
            // to extend the grid by the length and "copy" the player there
            nextXRight = Globals.MAP_LENGHT + playerX;
        } 
        // Or is the player actually right of us?
        else if (playerX > ownX)
        {
            // Just the other way round
            // this time the next position to the left is virtual
            nextXLeft = -Globals.MAP_LENGHT + playerX;
            // The next right position is the actual one
            nextXRight = playerX;
        }
    
        // Now we calculate the directed distances in both directions
        var distanceLeft = nextXLeft - ownX;
        var distanceRight = nextXRight - ownX;
    
        // use the Absolute only for comparing which is shorter
        var distanceX = Mathf.Abs(distanceRight) < Mathf.Abs(distanceLeft) ? distanceRight : distanceLeft;
    
        // And finally we want the smallest of both possible distances
        var distanceX = Mathf.Min(distanceLeft, distanceRight);
    
        // Repeat the same for the Y axis
        var nextYDown = ownY;
        var nextYUp = ownY;
        if(playerY < ownY)
        {
            nextYDown = playerY;
            nextYUp = Globals.MAP_LENGHT + playerY;
        } 
        else if (playerY > ownY)
        {
            nextYDown = -Globals.MAP_LENGHT + playerY;
            nextYUp = playerY;
        }
    
        var distanceDown = nextYDown - ownY;
        var distanceUp = nextYUp - ownY;
    
        var distanceY = Mathf.Abs(distanceUp) < Mathf.Abs(distanceDown) ? distanceUp : distanceDown;
    
        // Now you have a directed distance vector for both axis with the 
        // minimal absolute distance from the player
        return new Position(){ X = distanceX, Y = distanceY };
    }
    

    这是它现在如何工作的一点可视化


    附带说明:您可能想要使用 Vector2Int 来代替您的自定义 Position

    【讨论】:

    • 我需要知道方向。如果我使用您的代码,如果玩家位于 (0,0) 位置,则您的瓷砖 (8,8) 函数的结果将是 (2,2),但我需要它是 (-2, - 2)。感谢您花时间查看我的问题,我真的很感激 :)
    • @blacksmith 在底部看到我的小更新 ;) 我不确定方向的标志 .. 所以您可能需要将顺序从 ownX - nextXLeft 等更改为 nextXLeft - ownX,具体取决于结果向量应该指向哪个方向
    • 感谢您的更新!我试过你的代码,但如果玩家的位置是 (0,8),那么图块 (8,0) 的结果是 (-2,-8),它应该是 (-2, 2)。跨度>
    • 哦,在else if (playerY &gt; ownY) 中有一个复制粘贴错字对不起^^ 它当然应该使用playerY,而不是playerX ;)
    • 我认为它有效!现在我有另一个错误,但我认为它与这个问题无关。先生,你有我的支持:)
    猜你喜欢
    • 2020-03-13
    • 2018-12-02
    • 2018-03-23
    • 1970-01-01
    • 2016-11-29
    • 2013-12-16
    • 2011-09-21
    • 1970-01-01
    • 2020-07-30
    相关资源
    最近更新 更多