這是我的第一篇文章,如果我犯了任何錯誤等,我很抱歉。 如果在文章結尾處,您需要更多信息,那麼我會更樂意發佈更多。Xna二維平鋪引擎渲染問題
我一直在研究基於2D平鋪的遊戲一段時間,最近我嘗試了一種不同的方法來渲染平鋪。但是現在我在渲染切片時遇到性能問題。遊戲將其世界分成16 * 256個塊,每個塊都有自己的RenderTarget2D(16 * 256)來保存拼塊數據。我已經將渲染目標稱爲塊緩衝區。
public void updateChunks()
{
int loadMinX = (int)((game.camera.location.X - game.GraphicsDevice.Viewport.Width/2)/Tile.tileWidth/Chunk.chunkWidth);
int loadMaxX = (int)((game.camera.location.X + game.GraphicsDevice.Viewport.Width/2)/Tile.tileWidth/Chunk.chunkWidth);
int minX = (int)(game.player.location.X/(Chunk.chunkWidth * Tile.tileWidth)) - (int)Math.Floor(chunkCount/2f);
int maxX = (int)(game.player.location.X/(Chunk.chunkWidth * Tile.tileWidth)) + (int)Math.Floor(chunkCount/2f);
Chunk currentChunk;
int cursorChunk = game.cursor.chunkLocation;
for(int c = minX; c <= maxX; c++)
{
if(chunkBuffer.ContainsKey(c))
{
currentChunk = chunkBuffer[c];
if(c >= loadMinX && c <= loadMaxX)
{
currentChunk.bufferBuilt = false;
}
if(currentChunk.highlighted == true)
{
currentChunk.highlighted = false;
currentChunk.bufferBuilt = false;
}
if(chunkBuffer.ContainsKey(cursorChunk) && chunkBuffer[cursorChunk] == currentChunk)
{
currentChunk.highlighted = true;
currentChunk.bufferBuilt = false;
}
if(currentChunk.bufferBuilt == false)
{
RebuildChunk(currentChunk);
}
}
}
game.tilesLoaded = chunkBuffer.Count * Chunk.chunkWidth * Chunk.chunkHeight;
}
這個方法每調用一次,就決定哪個塊緩衝區需要重建/重繪。目前,在視口中的塊被重新編輯,並且鼠標在裏面的塊也被重建(因爲塊變爲高亮顏色)。
塊存儲在字典中,並且加載如updateCHunks()方法所示,通過遍歷塊的字典的minX - > minY值來訪問塊。
public void RebuildChunk(Chunk chunk)
{
BuildChunk(chunk);
chunk.bufferBuilt = true;
}
private void BuildChunk(Chunk chunk)
{
int chunkWidth = Chunk.chunkWidth;
int chunkHeight = Chunk.chunkHeight;
int chunkLocation = chunk.id * chunkWidth;
int loadMinX = (int)((game.camera.location.X - game.GraphicsDevice.Viewport.Width/2)/Tile.tileWidth) - 2;
int loadMaxX = (int)((game.camera.location.X + game.GraphicsDevice.Viewport.Width/2)/Tile.tileWidth) + 2;
int loadMinY = (int)((game.camera.location.Y - game.GraphicsDevice.Viewport.Height/2)/Tile.tileHeight) - 2;
int loadMaxY = (int)((game.camera.location.Y + game.GraphicsDevice.Viewport.Height/2)/Tile.tileHeight) + 2;
game.GraphicsDevice.SetRenderTarget(chunk.buffer);
game.GraphicsDevice.Clear(Color.CornflowerBlue);
game.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied);
for(int x = 0; x < chunkWidth; x++)
{
for(int y = 0; y < chunkHeight; y++)
{
if(x + chunkLocation > loadMinX && x + chunkLocation < loadMaxX && y > loadMinY && y < loadMaxY)
{
if(chunk.tiles[x, y].type == 0 && chunk.tiles[x, y].lightSource == false)
{
chunk.tiles[x, y].lightSource = true;
chunk.tiles[x, y].lightComponent = new Light(game, new Point(chunkLocation + x, y), 1.0f, 6);
}
if(chunk.tiles[x, y].lightSource == true)
{
if(chunk.tiles[x, y].lightComponent == null)
{
chunk.tiles[x, y].lightSource = false;
}
else
{
ApplyLighting(chunk, x, y, chunk.tiles[x, y].lightComponent);
}
}
float brightness = chunk.tiles[x, y].brightness;
if(chunk.tiles[x, y].type == 0)
{
if(chunk.highlighted == true && game.cursor.tileLocation.X == x && game.cursor.tileLocation.Y == y)
{
game.spriteBatch.Draw(game.gameContent.airTexture, new Rectangle(x * Tile.tileWidth, y * Tile.tileHeight, Tile.tileWidth, Tile.tileHeight), new Color(0.5f, 0.5f, 0.5f));
}
else
{
game.spriteBatch.Draw(game.gameContent.airTexture, new Rectangle(x * Tile.tileWidth, y * Tile.tileHeight, Tile.tileWidth, Tile.tileHeight), new Color(brightness, brightness, brightness));
}
}
else if(chunk.tiles[x, y].type == 1)
{
if(chunk.highlighted == true && game.cursor.tileLocation.X == x && game.cursor.tileLocation.Y == y)
{
game.spriteBatch.Draw(game.gameContent.dirtTexture, new Rectangle(x * Tile.tileWidth, y * Tile.tileHeight, Tile.tileWidth, Tile.tileHeight), new Color(0.5f, 0.5f, 0.5f));
}
else
{
game.spriteBatch.Draw(game.gameContent.dirtTexture, new Rectangle(x * Tile.tileWidth, y * Tile.tileHeight, Tile.tileWidth, Tile.tileHeight), new Color(brightness, brightness, brightness));
}
}
}
}
}
game.spriteBatch.End();
game.GraphicsDevice.SetRenderTarget(null);
}
RebuildChunk負責建設再次塊和復位內置標誌,這樣CPU不浪費時間重建還沒有被編輯或塊是不是在視口中。
BuildChunk是瓷磚繪製到塊緩衝區的位置。渲染目標被交換到塊緩衝區,然後它通過塊瓷磚循環。
if(x + chunkLocation > loadMinX && x + chunkLocation < loadMaxX && y > loadMinY && y < loadMaxY)
此行檢查瓦片是否在視口中。 如果是,請檢查瓷磚類型,然後爲該瓷磚繪製相應的紋理。那裏也有照明邏輯,但它不影響我的問題。
我主要呈現邏輯是:
protected override void Draw(GameTime gameTime)
{
frameCounter++;
drawScene(gameTime);
GraphicsDevice.Clear(Color.White);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied);
spriteBatch.Draw(white, new Rectangle(0, 0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height), Color.CornflowerBlue);
spriteBatch.Draw(worldBuffer, new Rectangle(0, 0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height), Color.White);
spriteBatch.End();
debugUI.Draw(gameTime);
base.Draw(gameTime);
}
private void drawScene(GameTime gameTime)
{
int loadMinX = (int)((camera.location.X - GraphicsDevice.Viewport.Width/2)/Tile.tileWidth/Chunk.chunkWidth);
int loadMaxX = (int)((camera.location.X + GraphicsDevice.Viewport.Width/2)/Tile.tileWidth/Chunk.chunkWidth);
int minX = (int)(player.location.X/(Chunk.chunkWidth * Tile.tileWidth)) - (int)Math.Floor(worldManager.chunkCount/2f);
int maxX = (int)(player.location.X/(Chunk.chunkWidth * Tile.tileWidth)) + (int)Math.Floor(worldManager.chunkCount/2f);
GraphicsDevice.SetRenderTarget(worldBuffer);
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null, null, camera.GetViewTransformation(GraphicsDevice));
for(int c = minX; c <= maxX; c++)
{
if(c >= loadMinX && c <= loadMaxX)
{
if(worldManager.chunkBuffer.ContainsKey(c))
{
Chunk currentChunk = worldManager.chunkBuffer[c];
if(currentChunk.bufferBuilt == true)
{
spriteBatch.Draw(currentChunk.buffer, new Rectangle(c * Chunk.chunkWidth * Tile.tileWidth, 0, Chunk.chunkWidth * Tile.tileWidth, Chunk.chunkHeight * Tile.tileHeight), Color.White);
}
}
}
}
spriteBatch.Draw(player.material, new Rectangle((int)player.location.X, (int)player.location.Y, 16, 32), Color.White);
spriteBatch.End();
GraphicsDevice.SetRenderTarget(null);
}
最後,我輸入管理器裏面,我有以下:
if(mouseState.LeftButton == ButtonState.Pressed)
{
game.worldManager.RemoveTile(game.cursor.chunkLocation, game.cursor.tileLocation);
}
的呼叫:
public void RemoveTile(int chunkLocation, Point location)
{
if(chunkBuffer.ContainsKey(chunkLocation) && location.X >= 0 && location.X < Chunk.chunkWidth && location.Y >= 0 && location.Y < Chunk.chunkHeight)
{
if(chunkBuffer[chunkLocation].tiles[location.X, location.Y].type != 0)
chunkBuffer[chunkLocation].tiles[location.X, location.Y].type = 0;
}
}
這只是我的測試方法來改變不同瓦片的狀態,在這種情況下,它將類型形式的污垢改變爲空氣以模擬去除一塊瓷磚。
現在的問題。當遊戲第一次運行時很好,並保持在60fps。我把我的角色移動到右邊,用瓷磚填充屏幕寬度,然後挖下來用瓷磚填充屏幕高度。
http://i.imgur.com/NBJ8qRj.png
http://i.imgur.com/N9i8asW.png (注:照明第二圖像中關閉)
遊戲仍在運行良好的第一形象,但是當我開始去除環境中的玩家磚第二張圖片,過了一段時間,幀率從60下降到10以下,下降到2-3幀/秒左右,並且保持在這個速率,並且不會恢復,即使我已經停止點擊/執行任何操作。
的問題發生在全屏幕分辨率和性能分析說,大量的CPU時間在BuildChunk()方法中的抽獎調用中使用:
else
{
game.spriteBatch.Draw(game.gameContent.dirtTexture, new Rectangle(x * Tile.tileWidth, y * Tile.tileHeight, Tile.tileWidth, Tile.tileHeight), new Color(brightness, brightness, brightness));
}
因此可以說,它是通過不斷引起交換紋理繪製?即;從繪製airTexture到dirtTexture等交換?因爲我發現如果我將airTexture改爲dirtTexture,問題也會消失。
如果我再往下挖掘所有的瓷磚,fps恢復到60以下,所以我的猜測是它與在重建階段繪製紋理有關。
如果有人可以查看這段代碼,也許指出它的任何缺陷,將不勝感激,可能的解決方案將是偉大的。
另外,如果有人對瓷磚的這種情況有所瞭解,並且提出建議將是無價的!
Nex時間試圖發佈只有相關的代碼,這個問題是巨大的,我不知道有多少人會閱讀它。 – pinckerman