2015-09-24 80 views
-1

我正在用MonoGame編寫2D益智遊戲。我剛剛將第一個移動精靈添加到程序中,發現我的速度達到了10fps,爲什麼我還沒有弄明白。我不知道我能否提供足夠的信息來獲得幫助,但我認爲這值得一試。作爲參考,移動物體是球。MonoGame中的低幀率

主要更新程序:

protected override void Update(GameTime gameTime) 
{ 
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) 
     Exit(); 

    MouseState newState = Mouse.GetState(); 

    if (newState.LeftButton == ButtonState.Pressed && oldState.LeftButton == ButtonState.Released) 
    { 
     MouseLeftClicked(newState.X, newState.Y); 
    } 

    oldState = newState; 

    if (gameState == GameState.Playing) 
    { 
     try 
     { 
      foreach (Ball ball in ballList) 
      { 
       ball.Update(gameTime); 
      } 
     } 
     catch(NullReferenceException) 
     { 
     } 
    } 
    // TODO: Add your update logic here 

    base.Update(gameTime); 
} 

球更新程序:

public void Update(GameTime gameTime) 
{ 
    this.pos += this.motion * (float)gameTime.ElapsedGameTime.TotalSeconds; 
} 

正選賽常規:

protected override void Draw(GameTime gameTime) 
{ 
    GraphicsDevice.Clear(Color.CornflowerBlue); 

    // TODO: Add your drawing code here 
    spriteBatch.Begin(); 

    for (int x = 0; x < 8; x++) 
    { 
     for (int y = 0; y < 6; y++) 
     { 
      if (gameGrid[x, y] != null) 
      { 
       spriteBatch.Draw(backgroundTile, gameGrid[x, y].rect, Color.White); 
      } 
     } 
    } 


    //Draw menu 
    if (gameState == GameState.StartMenu) 
    { 
     spriteBatch.Draw(startButton, orbPosition, Color.White); 
    } 

    //Draw game while playing 
    if (gameState == GameState.Playing) 
    { 
     for (int x = 0; x < 8; x++) 
     { 
      for (int y = 0; y < 6; y++) 
      { 
       try 
       { 
        gameGrid[x, y].pipe.Draw(spriteBatch); 
       } 
       catch (NullReferenceException) 
       { 
        continue; 
       } 
      } 
     } 
     foreach (Wheel wheel in wheelList) 
     { 
      wheel.Draw(spriteBatch); 
     } 

     foreach (Ball ball in ballList) 
     { 
      ball.Draw(spriteBatch); 
     } 
    } 

    spriteBatch.End(); 

    base.Draw(gameTime); 
} 

皮球劃常規:

public void Draw(SpriteBatch spriteBatch) 
{ 
    int sprite = (int)color; 

    Rectangle sourceRect = new Rectangle(sprite * spriteSize, 0, spriteSize, spriteSize); 
    Rectangle ballRect = new Rectangle((int)this.pos.X, (int)this.pos.Y, spriteSize * scale, spriteSize * scale); 

    //spriteBatch.Begin(); 

    spriteBatch.Draw(this.texture, ballRect, sourceRect, Color.White); 

    //spriteBatch.End(); 
} 
+0

您是否嘗試運行探查器並查看瓶頸在哪裏?默認情況下,我相信fps是60. –

+0

我以前沒有聽說過探查器 - 它是什麼,我如何運行它? –

+0

配置文件執行程序分析,如程序佔用多少空間,函數調用需要多長時間等。簡單的谷歌搜索將返回大量可用的分析器,並且大多數插入Visual Studio。 –

回答

3

我測試了你的代碼,它在提供紋理時不會滯後。我不得不稍微修改你的代碼,以便使它工作,因爲你省略了部分代碼。我認爲省略的部分可能是負責任的,但不太可能。我不知道你在測試什麼樣的機器,但是我會提供一些建議以解決你在本地遇到的問題。

當編寫高性能的代碼,忘了你知道的「面向對象行爲」的一切,並認爲數據。面向數據的設計就是將數據集中在一起,然後一次處理所有數據。這要快得多。在很多情況下,對於遊戲設計,當有一個時,有很多。利用這個優勢,並通過整個陣列並直接在現場採取行動。

如果可能,避免嵌套異常和迭代循環。在其發生時例外的是昂貴的,當它是非常例外他們應該只被使用,或者如果它是極不尋常的情況下,實際上確實發生,並且希望通過拋出異常來處理這個「邊緣」的情況下強迫代碼的消費者處理它。

for (int x = 0; x < 8; x++) 
    { 
     for (int y = 0; y < 6; y++) 
     { 
      try 
      { 
       gameGrid[x, y].pipe.Draw(spriteBatch); 
      } 
      catch (NullReferenceException) 
      { 
       continue; 
      } 
     } 
    } 

捕捉Null引用您嵌套在兩個for循環中的異常可能是一個壞主意。如果你需要拋出其中的一個,並且允許你下一步考慮爲什麼如果你想保持代碼原樣,那麼拋出是必須的。如果它在那裏捕獲gameGrid或管道中的容器,請考慮構造函數應始終將項目置於有效狀態,並且該列表應始終爲「完整」。如果一個元素停止存在,它不應該在列表中。否則,如果一個失敗意味着所有失敗,則將try塊移到外面。這是比較常見的。

分析您的應用程序是一種機制,可以幫助您找到比預期更慢的事情,有時甚至可以使用爲什麼。以下是關於如何在Visual Studio中執行此操作的參考。 Beginners Guide to Performance Profiling以及Walkthrough: Profiling Applications

這就是說,沒有這些會減慢你的應用程序到那種程度,你所描述。因此,我建議您編輯您的問題,幷包含可能是原因的代碼的其他相關部分。我在下面附上了一個從你的小例子構建的樣本,做了一些小的修改,以便以更極端的方式模擬你的環境。這個例子繪製了100個矩形,每個矩形都是50x50,它們都重新縮放並像應用程序一樣移動。這些是100個磁貼,如果你自己的速度很慢,那麼如果你正在使用visual studio或試着從Mono Game's official website或你最新的圖形卡驅動程序中獲取最新的二進制文件,那麼你絕對應該關注上面的剖析器主題。

/// <summary> 
/// This is the main type for your game 
/// </summary> 
public class Game1 : Game 
{ 
    GraphicsDeviceManager graphics; 
    SpriteBatch spriteBatch; 

    public Game1() 
    { 
     graphics = new GraphicsDeviceManager(this); 
     Content.RootDirectory = "Content"; 
    } 

    public IList<Ball> Balls { get; private set; } 

    /// <summary> 
    /// LoadContent will be called once per game and is the place to load 
    /// all of your content. 
    /// </summary> 
    protected override void LoadContent() 
    { 
     // Create a new SpriteBatch, which can be used to draw textures. 
     spriteBatch = new SpriteBatch(GraphicsDevice); 
     var rand = new Random(); 
     Balls = new List<Ball>(5); 
     for(var iii = 0; iii < 100; ++iii) 
      Balls.Add(new Ball(GraphicsDevice, new Vector2(rand.Next(50, 500), rand.Next(50, 500)))); 
    } 

    /// <summary> 
    /// UnloadContent will be called once per game and is the place to unload 
    /// all content. 
    /// </summary> 
    protected override void UnloadContent() 
    { 
    } 

    /// <summary> 
    /// Allows the game to run logic such as updating the world, 
    /// checking for collisions, gathering input, and playing audio. 
    /// </summary> 
    /// <param name="gameTime">Provides a snapshot of timing values.</param> 
    protected override void Update(GameTime gameTime) 
    { 
     if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) 
      Exit(); 

     foreach (var ball in Balls) 
      ball.Update(gameTime); 

     base.Update(gameTime); 
    } 

    /// <summary> 
    /// This is called when the game should draw itself. 
    /// </summary> 
    /// <param name="gameTime">Provides a snapshot of timing values.</param> 
    protected override void Draw(GameTime gameTime) 
    { 
     GraphicsDevice.Clear(Color.CornflowerBlue); 

     spriteBatch.Begin(); 

     foreach (var ball in Balls) 
      ball.Draw(spriteBatch); 

     spriteBatch.End(); 

     base.Draw(gameTime); 
    } 
} 

public class Ball 
{ 
    public Texture2D Texture { get; } 
    public Vector2 Position { get; private set; } 
    public double Scale { get; private set; } 

    public Ball(GraphicsDevice gd, Vector2 initialPosition) 
    { 
     Texture = new Texture2D(gd, 50, 50); 
     Position = initialPosition; 
     var data = new Color[100*100]; 
     for (var iii = 0; iii < data.Length; ++iii) data[iii] = Color.YellowGreen; 
     Texture.SetData(data); 
    } 

    private bool _increaseScale, _increaseX, _increaseY; 

    public void Update(GameTime gameTime) 
    { 
     if (Scale < 1) 
      _increaseScale = true; 
     else if (Scale > 4) 
      _increaseScale = false; 

     if (Position.X < 50) 
      _increaseX = true; 
     else if (Position.X > 500) 
      _increaseX = false; 

     if (Position.Y < 50) 
      _increaseY = true; 
     else if (Position.Y > 500) 
      _increaseY = false; 

     Scale += (_increaseScale ? 1.5 : -1.5) * gameTime.ElapsedGameTime.TotalSeconds; 
     Position += new Vector2((float)((_increaseX ? 100 : -100)*gameTime.ElapsedGameTime.TotalSeconds), (float)((_increaseY ? 100 : -100)*gameTime.ElapsedGameTime.TotalSeconds)); 
    } 

    public void Draw(SpriteBatch spriteBatch) 
    { 
     var source = new Rectangle(0, 0, Texture.Height, Texture.Width); 
     var dest = new Rectangle((int)Position.X, (int)Position.Y, Texture.Width * (int)Scale, Texture.Height* (int)Scale); 
     spriteBatch.Draw(Texture, dest, source, Color.White); 
    } 
} 
+0

遊戲網格是組成主播放屏幕的8x6陣列。每個圖塊可以有一個關聯或不關聯的管道對象。 'try ... catch'用於遍歷切片,如果它存在,則繪製相關的管道,如果該切片沒有,則不會崩潰。我沒有意識到這在計算上是昂貴的;我會嘗試用適當的管道列表替換它,看看是否修復它。如果沒有,我會嘗試探查器的事情。 我還使用了bog標準數組,而不是您在示例程序中使用的IList東西 - 那裏有什麼區別? –

+0

IList 是一個接口,允許您發送ArrayLists等。我用它來給我的代碼的消費者自由地實現他們自己的列表並以不同的方式處理它。我看到一個用戶採用的一個應用程序是編寫一個「SelfRemoveList」,其中一個決定其自身狀態的項目可以從列表中自行失效,而無需知道它在列表中。除了實施自己之外,他們可能會通過比列表更多,所以就是這樣。如果我知道我出於某種原因需要列表,我將其定義爲這樣。 –

+0

刪除'try..catch'以支持正確的管道陣列可以立即解決問題。謝謝! –