假设您有一个 2D 方 block 网格(这是基于 2D 方 block 的游戏),大多数方 block 占据 1 个位置,但是一些较大的“对象”可以填充多个位置。我在数组上使用索引器自动将这些对象“引用”到它们的基础图 block 。因此,如果我在 3,4 处有一个 2x2 对象,并且我访问 4,4,它会自动重定向并在 3,4 处获取图 block 。但是,如果我需要获取确切的磁贴,我可以指定一个参数来绕过此功能。 (GameDev 上的 my old question 对此有更好的解释)
另一种看待它的方式是游戏世界中的门对象,用户可以点击它的任意位置来打开它,但每个单独的部分都可以包含其他属性,例如不同的背景和光照值。
请注意,我只是一个业余爱好者程序员,所以这可能不正确(以及为什么我要征求您的意见)每个“超大”图 block 都会以 X 的形式存储对其基础图 block 的引用, 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
是一个简单地检查图 block 是否很大(大于 1x1)的属性
我已经有适当的逻辑来在放置大图 block 时填充相邻图 block 的引用,并相应地删除它们。
在分析游戏时,我发现瓷砖的获取访问器占用了大量 CPU,每帧获取瓷砖数百次用于照明、渲染、碰撞等。
如何提高这段代码的性能和效率?
基准测试(在英特尔四核 i7 2670QM 上平均进行 30k 次迭代)
Tile t = tiles[100, 100];
- 160 ns 和 175 ns 带 2D 内部阵列
Tile t = tiles[100, 100, true];
- 137 ns 和 264 ns 带 2D 内部阵列(奇数)
100,100
并不是一个大的瓦片,请注意这些瓦片不是很常见。如果你在屏幕上有一个房子,你可以有几 block 大瓷砖(门、 table ),但是有很多泥土/石头/木头。
最佳答案
我建议,对于逻辑上连接在一起的每个正方形集合,您都创建一个类似于以下内容的实例:
private class TileRef { public Tile tile; public int X,Y,Width,Height;}
然后有一个二维或锯齿状的 TileRef 数组。连接在一起的组中的所有方 block 都应该持有对同一 TileRef
对象的引用。这应该允许您非常快速地找到与棋盘上任何方 block 关联的 TileRef
,而无需条件逻辑来处理不同大小的方 block 。
构建一个大小为 1 的图 block 的网格:
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 关联的属性。
关于c# - 使用多维数组索引器的最有效方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18192547/