[C#] Optimierung für 1000+ Objekte

  • C#

Es gibt 7 Antworten in diesem Thema. Der letzte Beitrag () ist von Artentus.

    [C#] Optimierung für 1000+ Objekte

    Guten Abend Leute,

    ich stehe gerade vor einem Problem, dass ich nach stundenlangem Versuchen immer noch nicht bewältigen konnte.

    Undzwar arbeite z.Z. an einer Art Partikel-Anwendung, jedenfalls werden sehr sehr viele Objekte erzeugt (je mehr desto besser eben :D).
    Das Problem dabei liegt an der derzeit möglichen Menge, die etwa bei 1000 liegt und schon einen Kern voll auslastet.
    Danach sinken die FPS enorm schnell.
    Es handelt sich dabei um eine MonoGame/ OpenGL C# Anwendung.
    Das Komische: Eine ähnliche Anwendung habe ich zuvor mit XNA erstellt und dort hatte ich noch 60 FPS bei über 20 000 Objekten.
    Leider sehe ich keinen Unterschied, zu mal die XNA Anwendung sogar mehr Abfragen besitzt.

    Der Code: (kann man hier im Forum auch C#-Code highlighten?)

    Main-Klasse
    Spoiler anzeigen

    Quellcode

    1. #region Using Statements
    2. using System;
    3. using System.IO;
    4. using System.Collections.Generic;
    5. using Microsoft.Xna.Framework;
    6. using Microsoft.Xna.Framework.Content;
    7. using Microsoft.Xna.Framework.Graphics;
    8. using Microsoft.Xna.Framework.Input;
    9. using Microsoft.Xna.Framework.Storage;
    10. using Microsoft.Xna.Framework.GamerServices;
    11. using System.Collections;
    12. #endregion
    13. namespace Vorlage
    14. {
    15. public class Game1 : Game
    16. {
    17. GraphicsDeviceManager graphics;
    18. SpriteBatch spriteBatch;
    19. Texture2D ballText;
    20. SpriteFont font;
    21. MouseState mouseState;
    22. MouseState oldMouseState;
    23. KeyboardState keyboardState;
    24. KeyboardState oldKeyboardState;
    25. Double Elapsed = 0;
    26. int apm;
    27. bool isElapsed = false;
    28. String Key = "";
    29. int i = 0;
    30. Vector2 position = new Vector2(10,20);
    31. Vector2 velocity = new Vector2(3,0);
    32. //private Ball[] balls = new Ball[0];
    33. List<Ball> balls = new List<Ball>();
    34. public Game1()
    35. : base()
    36. {
    37. graphics = new GraphicsDeviceManager(this);
    38. Content.RootDirectory = "Content";
    39. }
    40. protected override void Initialize()
    41. {
    42. graphics.PreferredBackBufferWidth = 1280;
    43. graphics.PreferredBackBufferHeight = 720;
    44. graphics.IsFullScreen = false;
    45. graphics.PreferMultiSampling = true;
    46. // IsFixedTimeStep = false;
    47. graphics.ApplyChanges();
    48. apm = 30;
    49. base.Initialize();
    50. }
    51. protected override void LoadContent()
    52. {
    53. spriteBatch = new SpriteBatch(GraphicsDevice);
    54. ballText = Content.Load<Texture2D>("white");
    55. font = Content.Load<SpriteFont>("Font");
    56. }
    57. protected override void UnloadContent()
    58. {
    59. //Content.Unload();
    60. }
    61. protected override void Update(GameTime gameTime)
    62. {
    63. // EXIT
    64. if (Keyboard.GetState().IsKeyDown(Keys.Escape))
    65. Exit();
    66. // INPUT REFRESH
    67. Elapsed += gameTime.ElapsedGameTime.TotalMilliseconds;
    68. if (Elapsed >= (1000/apm))
    69. {
    70. Elapsed = 0;
    71. mouseState = Mouse.GetState();
    72. keyboardState = Keyboard.GetState();
    73. isElapsed = true;
    74. }
    75. // EINSTELLUNGEN
    76. Key = PressedLetter(keyboardState, oldKeyboardState, Key);
    77. // ERSTER PLATZ FÜR NEUEN PARTIKEL
    78. // i = FindIndex(ref balls);
    79. switch (Key)
    80. {
    81. case "mouse" :
    82. if (( mouseState.LeftButton == ButtonState.Pressed))
    83. {
    84. Vector2 mousePos = new Vector2(mouseState.X, mouseState.Y);
    85. //balls[i]= new Ball(ballText, mousePos);
    86. balls.Add(new Ball(ballText, mousePos));
    87. }
    88. break;
    89. case "auto":
    90. if (this.velocity.X > 0 && position.X >= graphics.PreferredBackBufferWidth - ballText.Width - 20) { velocity.X = -velocity.X; }
    91. if (this.velocity.X < 0 && position.X <= 0 + 20) { velocity.X = -velocity.X; }
    92. position += velocity;
    93. balls[i] = new Ball(ballText, position);
    94. break;
    95. case "manual":
    96. if (keyboardState.IsKeyDown(Keys.Right))
    97. {
    98. position.X += 3;
    99. }
    100. else if (keyboardState.IsKeyDown(Keys.Left))
    101. {
    102. position.X -= 3;
    103. }
    104. // balls[i] = new Ball(ballText, position);
    105. balls.Add(new Ball(ballText, position));
    106. break;
    107. case "random":
    108. Random rnd = new Random();
    109. if (velocity.X < 0)
    110. {
    111. velocity.X = -rnd.Next(0, 5);
    112. }
    113. else { velocity.X = rnd.Next(0, 5); }
    114. position += velocity;
    115. // balls[i] = new Ball(ballText, position);
    116. balls.Add(new Ball(ballText, position));
    117. break;
    118. default:
    119. break;
    120. }
    121. // UPDATE
    122. foreach (Ball b in balls)
    123. {
    124. if (!( b == null))
    125. {
    126. b.Update(graphics.PreferredBackBufferHeight, graphics.PreferredBackBufferWidth, mouseState, isElapsed);
    127. }
    128. }
    129. // DELETE OLD ONES
    130. for (int j = 0; j < balls.Count; j++)
    131. {
    132. // if ((!(balls[j] == null)) && balls[j].exist == false) { balls[j] = null; }
    133. if (balls[j].exist == false) { balls.RemoveAt(j); }
    134. }
    135. // OLD INPUT SAVE
    136. if (isElapsed)
    137. {
    138. isElapsed = false;
    139. oldMouseState = mouseState;
    140. oldKeyboardState = keyboardState;
    141. }
    142. base.Update(gameTime);
    143. }
    144. protected override void Draw(GameTime gameTime)
    145. {
    146. GraphicsDevice.Clear(Color.Black);
    147. spriteBatch.Begin();
    148. foreach (Ball b in balls)
    149. {
    150. if (!(b == null))
    151. {
    152. b.Draw(this.spriteBatch, font, balls.Count - 1);
    153. }
    154. }
    155. spriteBatch.End();
    156. base.Draw(gameTime);
    157. }
    158. private string PressedLetter(KeyboardState State,KeyboardState oldState,String oldKey)
    159. {
    160. if (State.IsKeyDown(Keys.F1) && oldState.IsKeyDown(Keys.F1))
    161. {
    162. return "mouse";
    163. }
    164. else if (State.IsKeyDown(Keys.F2) && oldState.IsKeyDown(Keys.F2))
    165. {
    166. return "auto";
    167. }
    168. else if (State.IsKeyDown(Keys.F3) && oldState.IsKeyDown(Keys.F3))
    169. {
    170. return "manual";
    171. }
    172. else if (State.IsKeyDown(Keys.F4) && oldState.IsKeyDown(Keys.F4))
    173. {
    174. return "random";
    175. }
    176. else
    177. {
    178. String key = oldKey;
    179. return key;
    180. }
    181. }
    182. }


    Ball-Klasse
    Spoiler anzeigen

    Quellcode

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using Microsoft.Xna.Framework;
    6. using Microsoft.Xna.Framework.Content;
    7. using Microsoft.Xna.Framework.Graphics;
    8. using Microsoft.Xna.Framework.Input;
    9. using Microsoft.Xna.Framework.Storage;
    10. using Microsoft.Xna.Framework.GamerServices;
    11. namespace Vorlage
    12. {
    13. class Ball
    14. {
    15. private Texture2D texture;
    16. private Vector2 velocity;
    17. private Vector2 position;
    18. public Boolean exist = true;
    19. private Byte r = 255;
    20. private Byte g, b;
    21. private Color color;
    22. Random rnd = new Random();
    23. public Ball(Texture2D texture, Vector2 position)
    24. {
    25. this.texture = texture;
    26. this.position = position;
    27. this.velocity.Y = 0.1f;
    28. }
    29. private void Fall(int height)
    30. {
    31. //Falling and Bouncing
    32. velocity.Y += 0.1f;
    33. // REFRESH POSITION
    34. // this.position += this.velocity;
    35. if (this.position.Y + this.velocity.Y + this.texture.Height >= height)
    36. {
    37. this.position.Y = height - this.texture.Height;
    38. this.velocity.Y = -this.velocity.Y + 0.2f * this.velocity.Y;
    39. }
    40. if ((Math.Abs(this.velocity.Y) <= 0.05f) && (this.position.Y + this.texture.Height >= height*0.95))
    41. { this.exist = false; }
    42. }
    43. private void Swarm(MouseState mouseState,int height)
    44. {
    45. float fak = 20f;
    46. if (this.position.Y > mouseState.Y)
    47. {
    48. this.velocity.Y -= (rnd.Next(1,10)/fak);
    49. }
    50. else
    51. {
    52. this.velocity.Y += (rnd.Next(1, 10) / fak);
    53. }
    54. if (this.position.X > mouseState.X)
    55. {
    56. this.velocity.X -= (rnd.Next(1, 10) / fak);
    57. }
    58. else
    59. {
    60. this.velocity.X += (rnd.Next(1, 10) / fak);
    61. }
    62. // SO SCREAM AND SHOUT AND (DON'T) LET IT ALL OUT
    63. if (this.position.Y + this.velocity.Y + this.texture.Height >= height)
    64. {
    65. this.position.Y = height - this.texture.Height;
    66. this.velocity.Y = -this.velocity.Y*0.3f;
    67. }
    68. }
    69. public void Update(int height,int width,MouseState mouseState,bool isElapsed)
    70. {
    71. if (mouseState.RightButton == ButtonState.Pressed)
    72. { // NORMAL PHYSICS
    73. Swarm(mouseState,height);
    74. }
    75. else
    76. { // SWARM TO MOUSE
    77. Fall(height);
    78. }
    79. // Color UPDATE
    80. Update_Colors();
    81. // RIGHT SIDE
    82. if (this.position.X + this.velocity.X + this.texture.Width >= width)
    83. {
    84. this.position.X = width - this.texture.Width;
    85. this.velocity.X = -this.velocity.X*0.3f;
    86. }
    87. // LEFT SIDE
    88. if (this.position.X + this.velocity.X <= 0)
    89. {
    90. this.position.X = 0;
    91. this.velocity.X = -this.velocity.X*0.3f;
    92. }
    93. // TOP
    94. if (this.position.Y + this.velocity.Y <= 0)
    95. {
    96. this.position.Y = 0;
    97. this.velocity.Y = -this.velocity.Y*0.3f;
    98. }
    99. this.position += this.velocity;
    100. }
    101. // COLOR
    102. public void Update_Colors()
    103. {
    104. if (r == 255 & g < 255 & g >= 0 & b == 0)
    105. {
    106. g += 1;
    107. }
    108. else if (g == 255 & b == 0 & r <= 255 & r > 0)
    109. {
    110. r -= 1;
    111. }
    112. else if (r == 0 & g == 255 & b < 255 & b >= 0)
    113. {
    114. b += 1;
    115. }
    116. else if (r == 0 & b == 255 & g > 0 & g <= 255)
    117. {
    118. g -= 1;
    119. }
    120. else if (g == 0 & b == 255 & r >= 0 & r < 255)
    121. {
    122. r += 1;
    123. }
    124. else
    125. {
    126. b -= 1;
    127. }
    128. color = new Color(r, g, b);
    129. }
    130. public void Draw(SpriteBatch spriteBatch,SpriteFont font,Int32 number)
    131. {
    132. spriteBatch.Draw(this.texture, this.position, color);
    133. spriteBatch.DrawString(font, Convert.ToString(number), new Vector2(2, 2), Color.White);
    134. }
    135. }
    136. }


    Das "Auskommentierte" sind Ansätze für eine Array-Variante, jedoch komme ich auch da nicht weiter, weils glücklichweise keine dynamischen Arrays in C# gibt :cursing:

    Ich hoffe ihr könnt mir weiterhelfen :thumbsup:

    ovexator schrieb:

    weils glücklichweise keine dynamischen Arrays in C# gibt

    Ich nehme mal an, du meinst ReDim. Darüber kannst du dich wirklich glücklich schätzen. Dieses Schlüsselwort ist der reinste Crap, man sollte auch in VB immer eine List(Of T) verwenden.

    Und zum Highlighten von C# kannst du den C-Codetag verwenden.
    foreach (Ball b in balls)
    {
    if (!( b == null))
    {
    b.Update(graphics.PreferredBackBufferHeight, graphics.PreferredBackBufferWidth, mouseState, isElapsed);
    }

    }
    // DELETE OLD ONES

    for (int j = 0; j < balls.Count; j++)
    {
    // if ((!(balls[j] == null)) && balls[j].exist == false) { balls[j] = null; }
    if (balls[j].exist == false) { balls.RemoveAt(j); }
    }

    mach da z.B. mal eine schleife drauß. Und dann weiß ich nicht wie das spriteBatch intern aufgebaut ist. Soweit ich weiß ist auf den heutigen Grakas ein 3D Draw schneller als 2D(da es das eigt. nichtmehr wirklich gibt). Deshalb implementier dir doch dein eigenes spritebatch, welches eben ein Quad mit Textur zeichnet und das schön mit HardwareInstancing.
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Ist der "delete old ones"-Code nicht Falsch? Ich meine du durchläufst die Liste von 0 bis balls.Count und löschst unterwegs ein paar Bälle. Da verschieben sich doch die Indizes nach einem RemoveAt und nachfolgende Bälle die gelöscht werden müssten bleiben uU in der Liste.
    Oh das stimmt, hat durch die 60 Updates pro Sekunde aber keine sichtbaren Fehler hervorgerufen.
    Ich habe das jetzt mal umgeändert und auf 30 Überprüfungen pro Sekunde beschränkt.
    Macht eigentlich keinen wirklichen Unterschied. Bis ca. 1100 läuft es flüssig.
    Die Ursache muss irgendwo anders liegen.
    Spoiler anzeigen

    Quellcode

    1. if (isElapsed)
    2. {
    3. for (int j = balls.Count - 1; j >= 0; j--)
    4. {
    5. if (balls[j].exist == false) { balls.RemoveAt(j); }
    6. }
    7. }


    Ich habe mal von einem Programm gehört, dass zeitaufwändige Funktionen/Prozeduren herausfindet. Weiß einer wie das heißt?

    @jvbsl
    Das mit dem Hardware-Instancing werde ich auf jeden Fall versuchen. Danke

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ovexator“ ()