Мне нужен контейнер, который может ссылаться на 70 миллионов + наборы объектов texture2d / vector2 - PullRequest
2 голосов
/ 08 июня 2011

Я пытаюсь найти способ хранения ссылок примерно на 70 миллионов наборов texture2d / vector2. Чтобы пояснить немного, мне нужно иметь возможность использовать около 100 текстур и затем назначать им значение XY вектора 2 для карты тайлов, которая будет иметь около 70 миллионов плиток. Мне просто нужно ссылаться на то, какой texture2d идет с каким vector2. Я буду динамически / процедурно генерировать наборы плиток / координат. Мне просто нужен метод их хранения сейчас, который не взорвет мой баран. Я пытался использовать словарь с vector2 в качестве ключа и texture2d в качестве значения, но это убило меня из-за исключения OutOfMemoryException. Тогда я попробовал Wintellect Power Collections и использовал их MultiDictionary. Но даже если бы я назначил texture2d в качестве единственного ключа и использовал vector2 в качестве 70 миллионов значений для этого ключа, он все равно был уничтожен с тем же исключением.

Так что я не знаю, как мне действовать сейчас. Мне просто нужно хранить ссылки для последующего доступа. Я не пытаюсь нарисовать их на экране или что-то в этом роде, поэтому не могу понять, почему для словаря требуется 700 Мб оперативной памяти. В последствии я только что понял, что позволяю этим словарям самим изменять свой размер. Может ли это быть проблемой?

Надеюсь, это было достаточно конкретно, я не спал всю ночь, поэтому я не буду беспокоиться о том, чтобы очистить и опубликовать какой-либо код прямо сейчас. Если вы считаете, что мой код - виновник, а не мои методы, дайте мне знать, и я опубликую его для вас. Ждем ваших ответов.

РЕДАКТИРОВАТЬ: Хорошо, вот код, с которым я работаю сейчас. Я немного прибрался, но знаю, что это не самый красивый код. Если вы видите какие-либо очевидные проблемы или даже неэффективность, я буду рад услышать о них. Только не бей меня плохо, так как я впервые работаю с c #:)

70 миллионов плиток вступают в игру, когда WorldWidth & WorldHeight установлены на 8400 каждая.

class MapMaker
{
    SpriteSheet spriteSheetMap;
    SpriteSheet spriteSheet1;
    SpriteSheet spriteSheet2;
    SpriteSheet spriteSheet3;
    SpriteSheet spriteSheet4;
    SpriteSheet spriteSheet5;
    SpriteSheet spriteSheet6;
    SpriteSheet spriteSheet7;
    SpriteSheet spriteSheet8;
    SpriteSheet spriteSheet9;
    SpriteSheet spriteSheet10;
    SpriteSheet spriteSheet11;
    SpriteSheet spriteSheet12;
    SpriteSheet spriteSheet13;
    SpriteSheet spriteSheet14;
    SpriteSheet spriteSheet15;
    SpriteSheet spriteSheet16;
    SpriteSheet spriteSheet17;
    SpriteSheet spriteSheet18;
    SpriteSheet spriteSheet19;
    SpriteSheet spriteSheet20;
    SpriteSheet spriteSheet21;
    SpriteSheet spriteSheet22;
    SpriteSheet spriteSheet23;

    Random rnd = new Random();

    int WorldWidth = 250;
    int WorldHeight = 250;
    List<int> sprites = new List<int>();
    int[][] TheGrid = new int[10][];
    int posX = 0, posY = 0, gridX = 0, gridY = 0;
    Dictionary<int, Texture2D> TileStorage = new Dictionary<int, Texture2D>();
    Dictionary<Vector2, Texture2D> SineSaver = new Dictionary<Vector2, Texture2D>();
    public void loadTiles(ContentManager Content)
    {
        spriteSheetMap = new SpriteSheet();

        spriteSheet1 = new SpriteSheet();
        spriteSheet2 = new SpriteSheet();
        spriteSheet3 = new SpriteSheet();
        spriteSheet4 = new SpriteSheet();
        spriteSheet5 = new SpriteSheet();
        spriteSheet6 = new SpriteSheet();
        spriteSheet7 = new SpriteSheet();
        spriteSheet8 = new SpriteSheet();
        spriteSheet9 = new SpriteSheet();
        spriteSheet10 = new SpriteSheet();
        spriteSheet11 = new SpriteSheet();
        spriteSheet12 = new SpriteSheet();
        spriteSheet13 = new SpriteSheet();
        spriteSheet14 = new SpriteSheet();
        spriteSheet15 = new SpriteSheet();
        spriteSheet16 = new SpriteSheet();
        spriteSheet17 = new SpriteSheet();
        spriteSheet18 = new SpriteSheet();
        spriteSheet19 = new SpriteSheet();
        spriteSheet20 = new SpriteSheet();
        spriteSheet21 = new SpriteSheet();
        spriteSheet22 = new SpriteSheet();
        spriteSheet23 = new SpriteSheet();

        spriteSheetMap.Map = Content.Load<Dictionary<string, Rectangle>>("Tiles/Map");
        TileStorage.Add(0, spriteSheet1.Sheet = Content.Load<Texture2D>("test2"));
        TileStorage.Add(1, spriteSheet1.Sheet = Content.Load<Texture2D>("Tiles/Amethyst"));
        TileStorage.Add(2, spriteSheet2.Sheet = Content.Load<Texture2D>("Tiles/Amethyst_N"));
        TileStorage.Add(3, spriteSheet3.Sheet = Content.Load<Texture2D>("Tiles/Aquamarine"));
        TileStorage.Add(4, spriteSheet4.Sheet = Content.Load<Texture2D>("Tiles/Aquamarine_N"));
        TileStorage.Add(5, spriteSheet5.Sheet = Content.Load<Texture2D>("Tiles/Citrine"));
        TileStorage.Add(6, spriteSheet6.Sheet = Content.Load<Texture2D>("Tiles/Citrine_N"));
        TileStorage.Add(7, spriteSheet7.Sheet = Content.Load<Texture2D>("Tiles/Diamond"));
        TileStorage.Add(8, spriteSheet8.Sheet = Content.Load<Texture2D>("Tiles/Diamond_N"));
        TileStorage.Add(9, spriteSheet9.Sheet = Content.Load<Texture2D>("Tiles/Dirt1"));
        TileStorage.Add(10, spriteSheet10.Sheet = Content.Load<Texture2D>("Tiles/Dirt2"));
        TileStorage.Add(11, spriteSheet11.Sheet = Content.Load<Texture2D>("Tiles/Emerald"));
        TileStorage.Add(12, spriteSheet12.Sheet = Content.Load<Texture2D>("Tiles/Emerald_N"));
        TileStorage.Add(13, spriteSheet13.Sheet = Content.Load<Texture2D>("Tiles/Peridot"));
        TileStorage.Add(14, spriteSheet14.Sheet = Content.Load<Texture2D>("Tiles/Peridot_N"));
        TileStorage.Add(15, spriteSheet15.Sheet = Content.Load<Texture2D>("Tiles/Ruby"));
        TileStorage.Add(16, spriteSheet16.Sheet = Content.Load<Texture2D>("Tiles/Ruby_N"));
        TileStorage.Add(17, spriteSheet17.Sheet = Content.Load<Texture2D>("Tiles/Sand"));
        TileStorage.Add(18, spriteSheet18.Sheet = Content.Load<Texture2D>("Tiles/Sapphire"));
        TileStorage.Add(19, spriteSheet19.Sheet = Content.Load<Texture2D>("Tiles/Stone1"));
        TileStorage.Add(20, spriteSheet20.Sheet = Content.Load<Texture2D>("Tiles/Stone2"));
        TileStorage.Add(21, spriteSheet21.Sheet = Content.Load<Texture2D>("Tiles/Stone3"));
        TileStorage.Add(22, spriteSheet22.Sheet = Content.Load<Texture2D>("Tiles/Topaz"));
        TileStorage.Add(23, spriteSheet23.Sheet = Content.Load<Texture2D>("Tiles/Topaz_N"));

        CreateMapKey();
    }

    private void CreateMapKey()
    {
        TheGrid[0] = new int[] { 0, 3, 14, 25, 36, 47, 58, 69, 80, 91 };
        TheGrid[1] = new int[] { 12, 4, 15, 26, 37, 48, 59, 70, 81, 92 };
        TheGrid[2] = new int[] { 23, 5, 16, 27, 38, 49, 60, 71, 82, 93 };
        TheGrid[3] = new int[] { 34, 6, 17, 28, 39, 50, 61, 72, 83, 94 };
        TheGrid[4] = new int[] { 45, 7, 18, 29, 40, 51, 62, 73, 84, 95 };
        TheGrid[5] = new int[] { 56, 8, 19, 30, 41, 52, 63, 74, 85, 96 };
        TheGrid[6] = new int[] { 67, 9, 20, 31, 42, 53, 64, 75, 86, 97 };
        TheGrid[7] = new int[] { 78, 10, 21, 32, 43, 54, 65, 76, 87, 98 };
        TheGrid[8] = new int[] { 89, 11, 22, 33, 44, 55, 66, 77, 88, 99 };
        TheGrid[9] = new int[] { 1, 13, 24, 35, 46, 57, 68, 79, 90, 2 };

        BaseTileset();
    }

    private void BaseTileset()
    {
        int hillLocation = 300, hillWidth = 120, hillHeight = 10;
        for (int i = 0; i < WorldHeight * WorldWidth; i++)
        {
            if (i % WorldHeight * 5 == 0)
            {
                hillLocation += rnd.Next(-40, 40);
                hillWidth += rnd.Next(-10, 10);
                if (hillWidth == 0) { hillWidth = 1; }
                hillHeight += rnd.Next(-5, 5);
            }
            Vector2 position = new Vector2(posX, posY);
            Texture2D tile = TileStorage[9];
            double sine = hillLocation + Math.Sin(posX / hillWidth) * hillHeight;
            double cosine = hillLocation + Math.Cos(posX / hillWidth) * hillHeight / 2;
            if (posY <= sine || posY < cosine)
            {
                tile = null;
            }
            if (tile != null)
            {
                SineSaver.Add(position, tile);
                sprites.Add(TheGrid[gridY][gridX]);

            }

            posY += 20;
            if (posY > (WorldHeight - 1) * 20) { posY = 0; posX += 20; }
            gridX = posX / 20 % 10;
            gridY = posY / 20 % 10;
        }

    }

    public void DrawLevel(SpriteBatch spriteBatch, GraphicsDeviceManager graphics)
    {
        spriteBatch.Begin();
        int i = 0;
        foreach (KeyValuePair<Vector2, Texture2D> entry in SineSaver)
        {
            spriteBatch.Draw(entry.Value, entry.Key, spriteSheetMap[sprites[i]], Color.White);
            i++;
        }
        spriteBatch.End();
    }
}

Ответы [ 4 ]

7 голосов
/ 08 июня 2011

Я бы порекомендовал посмотреть на шаблон веса (http://en.wikipedia.org/wiki/Flyweight_pattern)

. Если вы знаете ширину и высоту каждой плитки, вы можете рассчитать векторное положение каждой плитки - она ​​детерминирована на основе индекса, поэтомуего не нужно хранить. Единственные данные, которые необходимы для каждой плитки, - это одно целое для индекса и идентификатор типа, который может составлять всего один байт.

70 миллионов * (4 + 1) байт = 333,786011 мегабайт

РЕДАКТИРОВАТЬ 1: Немного доработать ..

Допустим, у нас есть сетка плиток 3х3 - мы знаем, что в ней 9 плитокитого, поэтому мы присваиваем каждой плитке индекс 0-8 ..

Tile[] tiles = new Tile[9]

for (int i = 0; i < 9; i++)
    tiles[i].Index = i;

Зная, что каждая строка сетки имеет 3 плитки по ширине, а каждый столбец - 3 плитки вниз, мы можем использовать операторы по модулю и делениюстрока и столбец для любого индекса плитки ...

Tile tile = getSomeTile();

int column = tile.Index % 3; // = column 1
int row = tile.Index \ 3; // = row 1 - tile 4 is in the middle :)

зная ширину и высоту каждой плитки (скажем, 10 пикселей), мы можем теперь вычислить точное положение плитки 4:

Vector2 position = new Vector2
{
    X = 10f * column,
    Y = 10f * row
};

РЕДАКТИРОВАТЬ 2: В ответ на коммnt ...

Ваш объект Tile должен содержать идентификатор типа, например:

struct Tile
{
    int Index; // The tiles index in the map [0 - 70 million].
    byte TileTypeId; // An identifier for a tile type.
}

class TileType // This is the flyweight object..
{
    Texture2D Texture; // Gets the texture reference for the tile type...

    // any other information about the tile ie. is it collidable? is it water? etc..
}

Тогда, когда дело доходит до рисования плиток ...

Tile tile = tiles[someIndex];

TileType type = tileTypes[tile.TileTypeId];  // tileTypes could be a dictionary...

Vector2 position = this.CalculateTilePosition(tile.Index); // Calculate the position as above...

spriteBatch.Draw(type.Texture, position);

РЕДАКТИРОВАТЬ 3: В ответ на комментарий сериализации ...

Сериализация сетки тайлов должна быть довольно простой, это просто длинная последовательность байтов.Предполагая, что плитки хранятся в порядке, мы уже знаем их порядковый номер, поэтому их не нужно хранить в файле.

Вернуться к примеру сетки 3x3:

Example binary data file:
0xF4 0x15 0x5A 0xB5 0x00 0x61 0xEE 0xA3 0x39

BinaryReader reader = OpenSomeBinaryFile();

for (int i = 0; i < (3 * 3); i++)
    tiles[i] = new Tile { Index = i, TileTypeId= reader.ReadByte() };

// easy to optimize by reading bigger binary chunks (say an Int64) and bit shifting to get the individual bytes...

// If you happen to have reasonbly large series of tiles with the same type id (byte), the file will be quite well suited to compression on disk, which is a bonus :)
1 голос
/ 08 июня 2011

Чтобы избежать огромных проблем с памятью (70 миллионов плиток даже при 1 байте на плитку составляет 70 МБ), вы можете рассмотреть возможность реализации потоковой передачи. По своей сути это что-то вроде WorldChunk, который содержит плитки NxN и ссылку на другой WorldChunk (либо прямая ссылка, либо просто идентификатор или строка для его идентификации). Каждый из этих блоков может иметь методы OnEnterFrame и OnExitFrame.

В методе OnEnterFrame вы можете вызывать загрузку из файла для всех связанных кусков, которая будет заполнять их данными из ваших игровых файлов, а с помощью OnExit вы будете уничтожать их. В этом случае вы будете хранить только небольшой набор фрагментов, подмножество вашего 8400x8400 мира. 3х3 или 5х5 - хороший выбор. В идеале эти куски должны заполнить экран или чуть больше, чем экран.

Концепция псевдокода следует:

class WorldChunk
{
    String MyName;
    String[8] LinkedWorldChunkFileNames; 
    Tile[32,32] Tiles;
    void OnEnterFrame()
    {
        LoadWorldChunkFromName(MyName);
        foreach(name in LinkedWorldChunkFileNames)
        {
            LoadWorldChunkFromName(name);
        }
    }

    void LoadWorldChunkFromName(string name)
    {
        string fileData = LoadFromFile(name); //this should probably be done earlier, 
                                              //when a neighbor loads it should load
                                              // offscreen nieghbors
                                              //files then parse them on its own enter                                        
                                              //frame
        Tiles = ParseToTiles(fileData); //your own file parsing here
    }

    void OnExitFrame()
    {
        Tiles.Clear();
    }
}

Я еще не реализовал ничего подобного, но я тоже начинаю задумываться, потому что мне нужно сделать что-то очень похожее в 3d.

0 голосов
/ 08 июня 2011

Так что я не знаю, есть ли у ваших спрайт-листов несколько плиток или что-то в этом роде ... но вы можете сделать что-то вроде:

class GameTile 
{
    private Vector2D _vec;
    //These integers should be sized to fit your needs
    //int8 = up to 256 tilesets
    //int16 = up to 65536 tiles in a set
    private int8 _tileSet;
    private int16 _tileIndex;

    //This is the tiles position in the world
    public Vector2D Position { get { return _vec; } }
    //This is the index of the tileset in the array of all tilesets
    public int8 CurrentTileset { get { return _tileSet; } }
    //This is the flattened index (row * width + column) 
    //of the tile in the tileset
    public int16 CurrentTile { get { return _tileIndex; } }        
}
0 голосов
/ 08 июня 2011

Почему бы вам просто не создать класс, который расширяет Vector2, но принимает в конструкторе дополнительную переменную, которая сообщает, какой Texture2D принадлежит ему?Таким образом, все будет храниться в самих объектах, что весьма эффективно.Таким образом, вам, вероятно, не нужна какая-либо другая структура данных!

Редактировать: Или даже просто создать массив всех возможных текстур и просто присвоить индекс своему пользовательскому объекту.Таким образом, вам не нужно каждый раз копировать весь объект Texture2D!

Edit2: Поскольку (как я должен был знать и на меня указывал) вы не можете распространяться наVector2, я бы сделал что-то вроде этого:

 public class TextureVector2D
 {
     public Vector2D vector;
     public int textureIndex;

     public TextureVector2D( Vector2D v, int tI )
     {
          vector = v;
          textureIndex = tI;
      }
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...