2013-08-30 58 views
3

在我的游戏好了,所以基本上,这个世界是做出来的瓷砖和地砖被分割成块(50 * 50瓷砖和瓷砖是16×16),所以整个世界ISN” t加载到内存中。这些块被保存到文件,并只需要加载的人(在屏幕上的人与1关闭,屏幕画面的每一侧(上,左,下,右)),以及其他被卸载等。这一切工作精细然而,当加载实际上包含瓷砖块游戏逻辑冻结了相当多的(要知道加载文件?),基本上我的问题是我怎么会让我目前的方法更快,所以这是几乎没有明显的滞后?如何加载/卸载瓦块快(C#)

基本上每个块只包含字节的2D阵列,含有该瓦片型而不是实际的瓷砖类,而字节数组被保存到稍后被加载的文件。 我的块加载/保存功能:

节能:

public void SaveChunk(int cx, int cy) 
    { 

     String chunkname = ""; 
     chunkname = WorldChunks[cx, cy].X + "_" + WorldChunks[cx, cy].Y; 

     FileStream fileStream = File.Create(Main.ChunkPath + "\\Chunks\\" + chunkname + ".chnk"); 
     StreamWriter writer = new StreamWriter(fileStream); 
     String json = JsonConvert.SerializeObject(WorldChunks[cx, cy].myTiles, Formatting.None); 
     writer.Write(json); 
     writer.Close(); 
    } 

加载:

public void LoadChunk(int xx, int yy) 
    { 

     string chunkname = xx + "_" + yy; 
     if (File.Exists(Main.ChunkPath + "\\Chunks\\" + chunkname + ".chnk")) 
     { 
      //If the chunk is not loaded, Create a new chunk and begin loading it's tiles. 
      if (WorldChunks[xx, yy] == null) 
      { 
       WorldChunks[xx, yy] = new Chunk(xx, yy); 
      } 

      System.Diagnostics.Debug.WriteLine("Loading chunk [" + xx + "," + yy + "]"); 
      Stream stream = File.Open(Main.ChunkPath + "\\Chunks\\" + chunkname + ".chnk", FileMode.Open); 
      StreamReader reader = new StreamReader(stream); 
      JsonSerializerSettings settings = new JsonSerializerSettings(); 
      settings.TypeNameHandling = TypeNameHandling.Objects; 
      WorldChunks[xx, yy].myTiles = JsonConvert.DeserializeObject<byte[,]>(reader.ReadToEnd(), settings); 
      stream.Close(); 
      System.Diagnostics.Debug.WriteLine("Chunk loaded.. [" + xx + "," + yy + "]"); 
      int x, y; 
      for (x = 0; x < Main.ChunkSizeX; x++) 
      { 
       for (y = 0; y < Main.ChunkSizeY; y++) 
       { 
        byte _Type = WorldChunks[xx, yy].myTiles[x, y]; 
        int tx = (xx * ChunkSizeX) + x; 
        int ty = (yy * ChunkSizeY) + y; 
        if (_Type > 0) 
        { 
         if (Tiles[tx, ty] == null && tx < MaxTilesX && ty < MaxTilesY) 
         { 
          Tiles[x + (xx * ChunkSizeX), (yy * ChunkSizeY) + y] = new Tile(); 
          Tiles[(xx * ChunkSizeX) + x, (yy * ChunkSizeY) + y].Type = _Type; 
          if (ty > GROUND_LEVEL[tx] + 1) 
          { 
           //System.Diagnostics.Debug.Write("Below level:" + x + "|" + tx); 
           Tiles[tx, ty].HasBG = true; 
          } 

         } 
         else continue; 
        } 

       } 
      } 

     } 

    } 

我试图改变周围的CHUNKSIZE,我试过25×16×35x35等等等等..但仍然无济于事。 那么我怎样才能使这些功能更快?任何其他提示/建议将非常感谢!谢谢!

编辑:从我的代码去秒表和其他测试的瓶颈是从文件实际加载我甚至取消了瓷砖的摆放,它仍然锁定了游戏逻辑,但是我意识到最大的明显延迟是当我之间的块和它必须加载更多的块比平常像这样: Picturing showing when my game logic takes the biggest hit http://img854.imageshack.us/img854/8031/wcs2.png

现在我可能是块实际上比那大,但同样的事情是可能的。

我的保存功能简单地保存每个瓷砖1个字节,我的文件大小每块只有2.5kb左右,因为每个瓷块有2,500个瓷砖,每个瓷砖1个字节(只保存瓷砖类型)。

所以我没想到的保存和加载它们将是多大的影响,我有一个异步FILESTREAM以及更早的设置,但表现得几乎相同。

这里是我的加载/卸载大块代码:

public void CheckChunks() 
    { 
     int StartX = (int)Math.Floor(ScreenPosition.X/(ChunkSizeX * 16)) - 2; 
     int StartY = (int)Math.Floor(ScreenPosition.Y/(ChunkSizeY * 16)) - 2; 

     if (StartX < 0) StartX = 0; 
     if (StartY < 0) StartY = 0; 

     int EndX = (int)Math.Floor((ScreenPosition.X + GraphicsDevice.Viewport.Width)/(ChunkSizeX * 16)) + 2; 
     int EndY = (int)Math.Floor((ScreenPosition.Y + GraphicsDevice.Viewport.Height)/(ChunkSizeY * 16)) + 2; 
     if (EndX > MaxChunksX) EndX = MaxChunksX; 
     if (EndY > MaxChunksY) EndY = MaxChunksY; 

     for (int x = StartX; x < EndX; x++) 
     { 
      for (int y = StartY; y < EndY; y++) 
      { 
       if (WorldChunks[x, y] == null) 
       { 

        LoadChunk(x, y, true); 

        if (WorldChunks[x, y] != null) 
        { 

         LoadedChunks.Add(WorldChunks[x, y]); 
        } 
        break; 
       } 

      } 
     } 

      foreach (Chunk cc in LoadedChunks) 
      { 

       if (cc.X < StartX || cc.Y < StartY || cc.X >EndX || cc.Y > EndY) 
       { 
        System.Diagnostics.Debug.WriteLine("Chunk to be unloaded.."); 
        ChunkBuffer.Add(cc); 
        UnloadChunk(cc); 

       } 
      } 

      foreach (Chunk c in ChunkBuffer) 
      { 
       LoadedChunks.Remove(c); 
      } 
     ChunkBuffer.Clear(); 

    } 

如何游戏一样的Minecraft负荷等方面迅速卸下自己的块?

+0

为了加快速度,您需要知道代码中的速度缓慢。配置文件(或至少用'秒表'测量时间)并从那里开始。 –

+0

谢谢你,但我忘了提及我已经使用秒表,甚至删除了部件,以查看瓶颈在哪里。它是从文件实际加载的,但我不确定它是否与反序列化或从文件中读取(很可能是阅读)。我会做进一步的测试,看看它究竟在哪里。 – Brassx

+0

我也在制作类似的游戏。我可以问一下你的“世界块”是什么,我正在猜测一堂课?另外,你如何跟踪哪些块被加载/卸载?我为此使用了一个List,但我正在寻找其他人从事类似工作的看法,以了解他们是如何做到的。谢谢! – user9993

回答

2

磁盘操作通常会过于沉重而不明显影响帧率一个游戏框架内执行。您的磁盘操作可以在单独的线程中运行。在加载过程中,您需要确保内存已被适当锁定。如果玩家碰巧在下一个块完成加载之前到达块的边缘,则这仍然可能导致可见的延迟。如果玩家在大块的底部角落,然后同时向下和向下移动到卸载的对角块中,该怎么办?这可以通过加载4个相邻的块以及4个对角块来解决,并且确保块大到足以使得玩家不会很快到达边缘。如果您不熟悉多线程,则需要进行一些研究。

http://www.yoda.arachsys.com/csharp/threads/

另一种可能的方法是你的游戏世界分割成多个地图和加载一个整体映射到内存中,而不是更小的块。这需要花费更长时间才能加载,但只需要在玩家在地图之间移动时执行,因此通常对玩家体验影响较小。如有必要,可以在这里使用加载屏幕。

+0

感谢您的回复!我一定会考虑多线程!我之前已经为这些块设置了异步加载,但是在使用它时没有提及性能提升,也许我做错了?但是,任何想法像Minecraft这样的游戏如何在没有游戏逻辑的重大中断的情况下如此顺利地加载它们的块(我假设它使用Async在不同的线程上运行加载函数?) – Brassx

+0

很难说,但我的猜测是资源被主线程锁定了。当前块的内存是否与相邻块分开? – rdans

+0

文件总数有多大?如果它只是一个二维数组ID,可以加载整个文件吗? – rdans

1

LoadChunk只做两件事情,磁盘IO和反序列化。你可以尝试独立加速。根据您加载的数据量,只需添加BufferedStream即可。完全不同的选择是使用异步I/O。处理起来有点难,但它不会阻塞你的UI线程,你会得到一些简单的并行性。

我不希望JSON反序列化是最快的,您应该将它的性能与您自己的反序列化例程进行比较。

16 x 16的瓷砖看起来很小,有没有理由不让它们变大? Tile越大,调用LoadChunk的次数越少。一旦你尽可能地加快了加载速度,你将会有一个好主意,何时加载下一个离屏平铺。例如。如果需要3个“蜱虫”来加载瓦片,当玩家在边缘的3个方格内时,应该加载它。

SaveChunk可以移动到ThreadPool线程或重写为使用异步IO。