2013-10-10 1 views
2

Я повсюду искал обходной путь к этой проблеме (я могу просто быть слепым, чтобы увидеть, как лежат решения). Моя игра в настоящее время отображает tilemap на экране и не будет отображать фрагменты, которые на самом деле не находятся на границах экрана. Однако каждая плитка имеет 16x16 пикселей, что означает 8100 плиток для рисования, если каждый пиксель на экране содержит плитку с разрешением 1920x1080.Как нарисовать тысячи плиток, не убивая FPS

Рисование того, что много плиток каждый цикл действительно убивает мой FPS. Если я использую разрешение 800x600, то мой FPS будет ~ 20, а в 1920x1080 он будет работать примерно на 3-5 FPS. Это действительно сводит меня с ума.

Я попытался нарезать резьбу и использовать async-задачи, но они просто мерцают на экране. Наверное, я просто неправильно его кодирую.

Вот код чертежа, который я использую в настоящее время.

 // Get top-left tile-X 
     Vector topLeft = new Vector(Screen.Camera.X/16 - 1, 
      Screen.Camera.Y/16 - 1); 
     Vector bottomRight = new Vector(topLeft.X + (Screen.Width/16) + 2, 
      topLeft.Y + (Screen.Height/16) + 2); 

     // Iterate sections 
     foreach (WorldSection section in Sections) 
     { 
      // Continue if out of bounds 
      if (section.X + ((Screen.Width/16) + 2) < (int)topLeft.X || 
       section.X >= bottomRight.X) 
       continue; 

      // Draw all tiles within the screen range 
      for (int x = topLeft.X; x < bottomRight.X; x++) 
       for (int y = topLeft.Y; y < bottomRight.Y; y++) 
        if (section.Blocks[x - section.X, y] != '0') 
         DrawBlock(section.Blocks[x - section.X, y], 
          x + section.X, y); 
     } 

От 8 до 12 секций. Каждый фрагмент представлен объектом char в двумерном массиве.

метод Draw блок:

public void DrawBlock(char block, int x int y) 
    { 
     // Get the source rectangle 
     Rectangle source = new Rectangle(Index(block) % Columns * FrameWidth, 
      Index(block)/Columns * FrameHeight, FrameWidth, FrameHeight); 

     // Get position 
     Vector2 position = new Vector2(x, y); 

     // Draw the block 
     Game.spriteBatch.Draw(Frameset, position * new Vector2(FrameWidth, FrameHeight) - Screen.Camera, source, Color.White); 
    } 

Метод Индекс() просто возвращает индекс кадра плитки, соответствующего полукокса.

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

+0

Код, который вы вложили, выглядит нормально, я бы искал проблемы с производительностью в DrawBlock() или Разделах. Пожалуйста, разместите эти части своего кода. – borkovski

+0

Как насчет одного призывного вызова для каждого раздела? Если секция представляет собой сетку ячеек, скажите (3x3) вместо 9 вызовов, нарисуйте их один раз в подходящее изображение и только рисуйте это. Вы даже можете заранее создать эти разделы на лету, кешировать их и рисовать. Если кеш станет большим, отбросьте старые разделы и снова постройте новые. – dowhilefor

+0

очень незначительный по разделу.Blocks [x - section.X, y], вызываемый дважды и (при отсутствии побочных эффектов), вы можете лучше хранить это в переменной и повторно использовать. Defo post DrawBlock, а также как и где вы измеряете свой FPS? – tolanj

ответ

0

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

Когда вы приближаетесь к границам текущей предварительно обработанной области, она будет перерабатывать куски на основе вашей новой позиции в мире. Обработка занимает примерно 100 миллисекунд, поэтому при загрузке новых областей игрок будет ощущать небольшое замедление на эту длительность. Мне это не очень нравится, но, по крайней мере, FPS сейчас 60 лет.

Вот мой процессор кусок:

public bool ProcessChunk(int x, int y) 
    { 
     // Create render target 
     using (RenderTarget2D target = new RenderTarget2D(Game.CurrentDevice, 16 * 48, 16 * 48, 
      false, SurfaceFormat.Color, DepthFormat.Depth24)) 
     { 

      // Set render target 
      Game.CurrentDevice.SetRenderTarget(target); 

      // Clear back buffer 
      Game.CurrentDevice.Clear(Color.Black * 0f); 

      // Begin drawing 
      Game.spriteBatch.Begin(SpriteSortMode.Texture, BlendState.AlphaBlend); 

      // Get block coordinates 
      int bx = x * 48, 
       by = y * 48; 

      // Draw blocks 
      int count = 0; 
      foreach (WorldSection section in Sections) 
      { 
       // Continue if section is out of chunk bounds 
       if (section.X >= bx + 48) continue; 

       // Draw all tiles within the screen range 
       for (int ax = 0; ax < 48; ax++) 
        for (int ay = 0; ay < 48; ay++) 
        { 
         // Get the block character 
         char b = section.Blocks[ax + bx - section.X, ay + by]; 

         // Draw the block unless it's an empty block 
         if (b != '0') 
         { 
          Processor.Blocks[b.ToString()].DrawBlock(new Vector2(ax, ay), true); 
          count++; 
         } 
        } 
      } 

      // End drawing 
      Game.spriteBatch.End(); 

      // Clear target 
      target.GraphicsDevice.SetRenderTarget(null); 

      // Set texture 
      if (count > 0) 
      { 
       // Create texture 
       Chunks[x, y] = new Texture2D(Game.CurrentDevice, target.Width, target.Height, true, target.Format); 

       // Set data 
       Color[] data = new Color[target.Width * target.Height]; 
       target.GetData<Color>(data); 
       Chunks[x, y].SetData<Color>(data); 

       // Return true 
       return true; 
      } 
     } 

     // Return false 
     return false; 
    } 

Если есть какие-либо предложения о том, как этот подход может быть улучшен, я не буду грустить их услышать!

Спасибо за помощь, данную здесь!

+0

А как насчет повторного использования объектов RenderTarget2D, а не для создания нового? делает ли это 100 мс вниз? –

+0

Также попробуйте запустить его с помощью foreach (раздел WorldSection в разделах). Чтобы узнать, сколько из этих 100ms вызвано переключением rendertarget и сколько из-за фактического рисования. –

+0

Использование только одного объекта RenderTarget2D сократило время процесса на несколько миллисекунд, но едва заметное. Избавление от цикла цикла в мире не имело заметного эффекта, возможно, потому что каждый раз, когда он обрабатывает около 20 или около того кусков, где между 8-12 секциями проходит цикл. Не много работы, кроме фактического рендеринга. – Sarkilas