使用多维数组索引器的最有效方法

本文关键字:有效 方法 索引 数组 | 更新日期: 2023-09-27 18:27:18

假设你有一个2D瓷砖网格(这是一个基于2D瓷砖的游戏),大多数瓷砖占据一个位置,但一些较大的"对象"可以填充多个位置。我在数组上使用一个索引器来自动将这些对象"引用"到它们的基础磁贴。因此,如果我在3,4处有一个2x2对象,并且我访问4,4,它将自动重定向并在3,4获得平铺。但是,如果需要获得确切的tile,我可以指定一个参数来绕过此功能。(更好地解释我在GameDev上的老问题)

另一种看待它的方式是游戏世界中的门对象,用户可以单击它的任何位置来打开它,但每个单独的部分都可以包含其他属性,如不同的背景和照明值。

注意,我只是一个业余程序员,所以这可能是不对的(这也是我寻求您建议的原因)每个"超大"瓦片都会以X,Y位置的形式存储对其基础瓦片的引用(这应该是对内存中实际对象的引用吗?)

public class TileWrapper
{
    public int Width = 0;
    public int Height = 0;
    private Tile[] tiles; //Backing Store
    public TileWrapper()
    {
        tiles = new Tile[Width * Height]; 
    }
    public TileWrapper(int width, int height)
    {
        Width = width;
        Height = height;
        tiles = new Tile[Width * Height];
    }
    /// <summary>
    /// Accessor for tiles
    /// </summary>
    /// <param name="x">X Position</param>
    /// <param name="y">Y Position</param>
    /// <param name="overide">Bool to "override" the get, if true, it wont get the reference tile and will bypass the checks</param>
    public Tile this[int x, int y, bool override = false]
    {
        get 
        {
             //If we do not want to bypass the checks, AND the current tile is > than 1x1
             if (!override && tiles[y * Width + x].IsLarge)
                return tiles[tiles[y * Width + x].refY * Width + tiles[y * Width + x].refX]; //Use the reference positions to get the main position of the tile
             //If we want to bypass the checks or the tile wasn't large, get the absolute position
             else 
                 return tiles[y * Width + x];
        }
        set 
        {
             //Same thing for SET
             if (!override && tiles[y * Width + x].IsLarge) //Set base tile if the large tile has a reference
                 tiles[tiles[y * Width + x].refY * Width + tiles[y * Width + x].refX] = value;
             else  //Set absolute tile
                  tiles[y * Width + x] = value;
        }
    }

很抱歉,如果2D到1D的转换有点难以阅读,但经过一些测试,内部使用1D阵列似乎会更快。

IsLarge是一个简单地检查瓦片是否大(大于1x1)的属性

我已经有了适当的逻辑,可以在放置大瓷砖时填充相邻瓷砖的引用,并相应地删除它们。

在分析游戏时,我发现瓦片的get访问器消耗了大量CPU,每帧获取瓦片数百次,用于照明、渲染、碰撞等。

如何提高此代码的性能和效率?

基准(英特尔四核i7 2670QM上平均30k次迭代)

Tile t = tiles[100, 100];-160ns和175ns与2D内部阵列

Tile t = tiles[100, 100, true];-137ns和264ns与2D内部阵列(奇数)

顺便说一句,100,100不是一个大的瓦片,请注意这些瓦片不是很常见。我在屏幕上看到了你的房子,你可以有一些大瓷砖(门、桌子),但有很多泥土/石头/木头。

使用多维数组索引器的最有效方法

我建议为逻辑连接在一起的每个正方形集合创建一个类似的实例

private class TileRef { public Tile tile; public int X,Y,Width,Height;}

然后使用TileRef的二维数组或锯齿状数组。连接在一起的组中的所有正方形都应包含对同一TileRef对象的引用。这将使您能够非常快速地找到与板上任何正方形关联的TileRef,而不需要条件逻辑来处理不同大小的瓦片。

构建一块瓷砖大小的网格:

TileRef[,] tiles;
TileMapper(int xsize, int ysize)
{
  tiles = new TileRef[xsize,ysize];
  for (int x=0; x < xsize; x++)
    for (int y=0; y < xsize; y++)
    {
      var thisRef = new TileRef();
      thisRef.X = x;
      thisRef.Y = y;
      thisRef.Width = 1;
      thisRef.Height = 1;
      thisRef.Tile = new Tile(); // Make a default tile instance somehow
      tiles[x][y] = thisRef;
    }
}
To join a bunch of squares together into a blob:
public JoinSquares(int x, int y, int width, int height)
{
      var thisRef = new TileRef();
      thisRef.X = x;
      thisRef.Y = y;
      thisRef.Width = 1;
      thisRef.Height = 1;
      thisRef.Tile = new Tile(); // Make a default tile instance somehow
      for (i=0; i<width; i++)
        for (j=0; j<height; j++)
          tiles[x+i,y+j] = thisRef;
}
public SeparateSquares(int x, int y)
{
      var oldRef = tiles[x,y];
      x=oldref.X;
      y=oldref.Y;
      var width=oldref.Width;
      var height=oldref.Height;
      for (i=0; i<width; i++)
        for (j=0; j<height; j++)
        {
          var thisRef = new TileRef();
          thisRef.X = x+i;
          thisRef.Y = y+j;
          thisRef.Width = 1;
          thisRef.Height = 1;
          thisRef.Tile = new Tile(); // Make a default tile instance somehow
          tiles[x+i,y+j] = thisRef;
        }
}
To change the `Tile` associated with a "blob", simply
public Tile this[int x, int y]
{
    get 
    {
        return tiles[x,y].Tile;
    }
    set
    {
        tiles[x,y].Tile = value;
    }
}

循环需要将正方形连接在一起或分开,但不能简单地通过更改其Tile属性来更改与blob关联的属性。