Скажем, у вас есть 2D-сетка плиток (это для игры, основанной на 2D-плитках), большинство плиток занимают 1 место, однако некоторые более крупные «объекты» могут занимать несколько мест. Я использую индексатор в своем массиве, чтобы автоматически «относить» эти объекты к их базовой плитке. Итак, если у меня есть объект 2x2 на 3,4, и я обращаюсь к 4,4, он автоматически перенаправит и получит тайл на 3,4. Однако я могу указать аргумент, чтобы обойти эту функцию в случае, если мне нужно получить точную плитку. (Лучшее объяснение на мой старый вопрос на 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 для плитки потребляет много ресурсов ЦП, получая плитки сотни раз за кадр для освещения, рендеринга, коллизии и т. д.
Как я могу улучшить производительность и эффективность этого кода?
Тестовые тесты (в среднем 30 000 итераций на Intel Quad Core i7 2670QM)
Tile t = tiles[100, 100];
— 160 нс и 175 нс С 2D-внутренним массивом
Tile t = tiles[100, 100, true];
— 137 нс и 264 нс С 2D-внутренним массивом (нечетные)
100,100
между прочим не большая плитка, обратите внимание, что эти плитки не очень распространены. Если бы у вас был дом на экране, вы могли бы иметь несколько больших плиток (двери, столы), но много грязи/камня/дерева.