C#.NET Main Game Loop

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • C#.NET Main Game Loop

      I started on a main loop for my game where I am converting the code provided in the book to C#.NET. Can anyone look at this and tell me if its a good start and what improvement might be needed?


      Source Code

      1. using System;
      2. using System.Collections.Generic;
      3. using System.Linq;
      4. using System.Text;
      5. using System.Threading;
      6. namespace Client.Architecture.Lifetime
      7. {
      8. public class Loop
      9. {
      10. /// <summary>
      11. ///
      12. /// </summary>
      13. private static bool _IsExit = false;
      14. /// <summary>
      15. ///
      16. /// </summary>
      17. public static bool IsExit
      18. {
      19. get
      20. {
      21. return _IsExit;
      22. }
      23. set
      24. {
      25. _IsExit = value;
      26. }
      27. }
      28. /// <summary>
      29. ///
      30. /// </summary>
      31. public Loop()
      32. {
      33. }
      34. /// <summary>
      35. ///
      36. /// </summary>
      37. public void Excute()
      38. {
      39. var ThreadGraphicUserInterface = new Thread(this.GraphicUserInterface);
      40. var ThreadGraphic = new Thread(this.Graphic);
      41. var ThreadHeadsUpDisplay = new Thread(this.HeadsUpDisplay);
      42. var ThreadAudio = new Thread(this.Audio);
      43. var ThreadNetwork = new Thread(this.Network);
      44. ThreadGraphicUserInterface.Start();
      45. ThreadGraphic.Start();
      46. ThreadHeadsUpDisplay.Start();
      47. ThreadAudio.Start();
      48. ThreadNetwork.Start();
      49. }
      50. /// <summary>
      51. /// Render Menu Interface
      52. /// </summary>
      53. public void GraphicUserInterface()
      54. {
      55. while (!IsExit)
      56. {
      57. Console.WriteLine(DateTime.Now.ToShortTimeString());
      58. }
      59. }
      60. /// <summary>
      61. /// Render Game Interface
      62. /// </summary>
      63. public void HeadsUpDisplay()
      64. {
      65. while (!IsExit)
      66. {
      67. Console.WriteLine("HelloWorld");
      68. }
      69. }
      70. /// <summary>
      71. /// Render Game Scene
      72. /// </summary>
      73. public void Graphic()
      74. {
      75. while (!IsExit)
      76. {
      77. System.Diagnostics.Debug.WriteLine("Yay!");
      78. }
      79. }
      80. /// <summary>
      81. ///
      82. /// </summary>
      83. public void Audio()
      84. {
      85. }
      86. /// <summary>
      87. ///
      88. /// </summary>
      89. public void Network()
      90. {
      91. }
      92. }
      93. }
      Display All
    • There's a number of multi-threading aritcles around the internet that talks about a task based threading than a system based threading. That way you can spread your work from multiple systems to a number of threads and your threaded task manager can autobalance by aggregating queued tasks to idle worker threads.

      Try checking out intel threading building blocks if you can use that for your general threading or implementing a task manager.

      I've made a crude simple threaded task manager but its not 100% tested. So far it works for async loading my resources...

      TaskScheduler.h
      TaskScheduler.cpp

      In general, if you really want to use threading, divide your work by tasks not by systems. Its much more easier and may minimized some data synchronizations than trying to put an entire system in a separate thread.

      The post was edited 1 time, last by BrentChua ().

    • I highly recommend reading 'Concurrency in action' by Anthony Williams, it has a much better (and more modern) worker thread pool for performing tasks than what I have found on the internet, the entire book was written to introduce the C++11 native thread support.
      PC - Custom Built
      CPU: 3rd Gen. Intel i7 3770 3.4Ghz
      GPU: ATI Radeon HD 7959 3GB
      RAM: 16GB

      Laptop - Alienware M17x
      CPU: 3rd Gen. Intel i7 - Ivy Bridge
      GPU: NVIDIA GeForce GTX 660M - 2GB GDDR5
      RAM: 8GB Dual Channel DDR3 @ 1600mhz
    • There is a chapter on a worker thread pool which deals with threaded tasks, the idea is this either;

      - Put systems directly onto a thread, which may be more appropriate in some cases

      - Divide complex jobs into tasks, so maybe when updating your particle FX system, split the 5000 particles between 10 jobs, the worker thread pool will send the jobs to available threads and will be processed concurrently

      The second option is much more scalable as when processor core counts increase, your engine can make use of the added available concurrency, rather if you have 4 threads dedicated to different systems, you would have to go in and manually increase the usage of concurrency yourself somehow.
      PC - Custom Built
      CPU: 3rd Gen. Intel i7 3770 3.4Ghz
      GPU: ATI Radeon HD 7959 3GB
      RAM: 16GB

      Laptop - Alienware M17x
      CPU: 3rd Gen. Intel i7 - Ivy Bridge
      GPU: NVIDIA GeForce GTX 660M - 2GB GDDR5
      RAM: 8GB Dual Channel DDR3 @ 1600mhz
    • Do note though that the book is focused on C++ and it's latest standard, however the thread pool should be easy to do in C# as well (or even easier).
      PC - Custom Built
      CPU: 3rd Gen. Intel i7 3770 3.4Ghz
      GPU: ATI Radeon HD 7959 3GB
      RAM: 16GB

      Laptop - Alienware M17x
      CPU: 3rd Gen. Intel i7 - Ivy Bridge
      GPU: NVIDIA GeForce GTX 660M - 2GB GDDR5
      RAM: 8GB Dual Channel DDR3 @ 1600mhz
    • Ok awesome guys feel like I'm on the right road here. This is what I have found for .NET and reading Chapter Nine of the book 'Concurrency in action'.

      I will now divide this up and see what I can do for making a loop out of it. I'll post it when I'm done.


      Source Code

      1. using System;
      2. using System.Collections.Generic;
      3. using System.Linq;
      4. using System.Text;
      5. using System.Threading;
      6. using System.Threading.Tasks;
      7. namespace ConsoleApplication1
      8. {
      9. class Program
      10. {
      11. // Demonstrated features:
      12. // Task ctor()
      13. // Task.Factory
      14. // Task.Wait()
      15. // Task.RunSynchronously()
      16. // Expected results:
      17. // Task t1 (alpha) is created unstarted.
      18. // Task t2 (beta) is created started.
      19. // Task t1's (alpha) start is held until after t2 (beta) is started.
      20. // Both tasks t1 (alpha) and t2 (beta) are potentially executed on threads other than the main thread on multi-core machines.
      21. // Task t3 (gamma) is executed synchronously on the main thread.
      22. // Documentation:
      23. // [URL]http://msdn.microsoft.com/en-us/library/system.threading.tasks.task_members[/URL](VS.100).aspx
      24. static void Main(string[] args)
      25. {
      26. Action<object> action = (object obj) =>
      27. {
      28. Console.WriteLine("Task={0}, obj={1}, Thread={2}", Task.CurrentId, obj.ToString(), Thread.CurrentThread.ManagedThreadId);
      29. };
      30. Task t4 = new Task(new Action(writeConsole));
      31. // Construct an unstarted task
      32. Task t1 = new Task(action, "alpha");
      33. // Cosntruct a started task
      34. Task t2 = Task.Factory.StartNew(action, "beta");
      35. // Block the main thread to demonstate that t2 is executing
      36. t2.Wait();
      37. // Launch t1
      38. t1.Start();
      39. Console.WriteLine("t1 has been launched. (Main Thread={0})", Thread.CurrentThread.ManagedThreadId);
      40. t4.Start();
      41. // Wait for the task to finish.
      42. // You may optionally provide a timeout interval or a cancellation token
      43. // to mitigate situations when the task takes too long to finish.
      44. t1.Wait();
      45. // Construct an unstarted task
      46. Task t3 = new Task(action, "gamma");
      47. // Run it synchronously
      48. t3.RunSynchronously();
      49. // Although the task was run synchrounously, it is a good practice to wait for it which observes for
      50. // exceptions potentially thrown by that task.
      51. t3.Wait();
      52. Task t5 = new Task(() => writeConsole("HelloAgain"));
      53. t5.Start();
      54. Console.ReadLine();
      55. }
      56. static void writeConsole()
      57. {
      58. Console.WriteLine("Task={0}, obj={1}, Thread={2},", Task.CurrentId, "privateMethod", Thread.CurrentThread.ManagedThreadId);
      59. }
      60. static void writeConsole(string s)
      61. {
      62. Console.WriteLine("Task={0}, obj={1}, Thread={2}, " + s, Task.CurrentId, "privateMethod", Thread.CurrentThread.ManagedThreadId);
      63. }
      64. }
      65. }
      Display All
    • Ok so I think this is complete for running the loop. Now the way I see this working is it takes each area of the game engine such as Graphic, Networking etc and put them in a task by themselves then it waits on each and executes the loop all together.

      Source Code

      1. using System;
      2. using System.Collections.Generic;
      3. using System.Linq;
      4. using System.Text;
      5. using System.Threading;
      6. using System.Threading.Tasks;
      7. namespace Client.Architecture.Lifetime
      8. {
      9. public class Loop
      10. {
      11. /// <summary>
      12. ///
      13. /// </summary>
      14. private static bool _IsExit = false;
      15. /// <summary>
      16. ///
      17. /// </summary>
      18. public static bool IsExit
      19. {
      20. get
      21. {
      22. return _IsExit;
      23. }
      24. set
      25. {
      26. _IsExit = value;
      27. }
      28. }
      29. /// <summary>
      30. ///
      31. /// </summary>
      32. public Loop()
      33. {
      34. }
      35. /// <summary>
      36. ///
      37. /// </summary>
      38. public void Excute()
      39. {
      40. Task GraphicUserInterfaceThread = new Task(() => GraphicUserInterface());
      41. Task HeadsUpDisplayThread = new Task(() => HeadsUpDisplay());
      42. Task GraphicThread = new Task(() => Graphic());
      43. Task AudioThread = new Task(() => Audio());
      44. Task NetworkThread = new Task(() => Network());
      45. GraphicUserInterfaceThread.Start();
      46. HeadsUpDisplayThread.Start();
      47. GraphicThread.Start();
      48. AudioThread.Start();
      49. NetworkThread.Start();
      50. GraphicUserInterfaceThread.Wait();
      51. HeadsUpDisplayThread.Wait();
      52. GraphicThread.Wait();
      53. AudioThread.Wait();
      54. NetworkThread.Wait();
      55. }
      56. /// <summary>
      57. /// Render Menu Interface
      58. /// </summary>
      59. public void GraphicUserInterface()
      60. {
      61. while (!IsExit)
      62. {
      63. Console.WriteLine(DateTime.Now.ToShortTimeString());
      64. }
      65. }
      66. /// <summary>
      67. /// Render Game Interface
      68. /// </summary>
      69. public void HeadsUpDisplay()
      70. {
      71. while (!IsExit)
      72. {
      73. Console.WriteLine("HelloWorld");
      74. }
      75. }
      76. /// <summary>
      77. /// Render Game Scene
      78. /// </summary>
      79. public void Graphic()
      80. {
      81. while (!IsExit)
      82. {
      83. System.Diagnostics.Debug.WriteLine("Yay!");
      84. }
      85. }
      86. /// <summary>
      87. ///
      88. /// </summary>
      89. public void Audio()
      90. {
      91. }
      92. /// <summary>
      93. ///
      94. /// </summary>
      95. public void Network()
      96. {
      97. }
      98. }
      99. }
      Display All
    • So after reading more I find that I'm confuse about the process manager. I see post like this one

      mcshaffry.com/GameCode/thread.…cessmanager&hilightuser=0

      But it seem maybe I am confused about the process manager having anything to do with the game loop. Should not the game loop run like the GUI and Networking etc in the loop without a process manager. Another thought would be to put the process manager, in the logic and have work like a list on top of the game loop but I'm not sure why I would want to manage two separate looping code instead of one loop overall one.

      ... Maybe make the main loop like the start of the game render the box start gui etc then use the process manager to run threaded task there after as event drive maybe?

      The post was edited 2 times, last by curator785 ().

    • I think it's overkill to use multiple threads for each of these systems. You're really not gaining anything and you end up losing stability and possibly performance if you're not careful. The overhead of switching between threads and locking threads can sometimes outweigh any benefits they provide. In general, if it's a purely CPU-driven task, a thread is likely not going to help you.

      The huge problem that threads solve is the problem of an idle CPU. For example, when you render something to the screen, the CPU spends most of its time just waiting for the actual render to happen. This is the perfect place for a thread because the OS can detect that the CPU is idle and switch execution to another thread. Another classic example is audio for much the same reason.

      Furthermore, every time you end up blocking or pausing a thread (this includes mutexes and critical sections), you are losing processing time. Threads come with a whole host of problems that very difficult to debug, such as race conditions and deadlocks. You can read about these in the multi-tasking chapter.

      That having been said, it's extremely useful to have the concept of multi-tasking. On The Sims, every Sim who is executing an interaction is running their own tasklet. The AI system as a whole is on its own tasklet, as are many other systems. In total, we might have 50+ tasklets running. These work very much like the process system in GCC. Each tasklet gets its process() function called and yields when it's done. It keeps running in this fashion until something causes the tasklet to exit. This is exactly what the process system does. You get the OnUpdate() function called every frame. When you call one of the exit functions, the process ends.

      This is called cooperative multi-tasking. It gives you a lot of the conceptual advantages of a multi-threaded system without a lot of hindrances. For example, since these aren't actual threads, you can guarantee that two will never be executing at the same time. This helps eliminate race conditions and deadlocks (though they don't completely go away). One thing you lose is the automatic nature of it all. Each process must explicitly yield to other processes.

      In my own engine, I have four real threads and two process managers. The first thread is the main thread, which handles the game logic and most of the run-time code. The second is the render thread, whose entire purpose in life is to consume Renderable objects and draw them to the screen. The third is a loader thread, which consumes a queue of resources to load from the hard drive. The fourth is created by the sound library I use for playing sfx and music. As you can see, each of the extra threads are VERY specific and meant to solve very specific problems (namely CPU idle). Furthermore, I almost never lock any of the threads. I have very few critical sections since most of the threads are self contained.

      The two process managers I have serve slightly different purposes. One is a system process manager and the other is for gameplay (or game logic). The system process manager is basically my main loop. Any systems in the engine that need time from the main loop typically go through here. It updates every frame. The gameplay process manager is for any processes that the game logic needs to spin up. Things like animation, pathfinding, camera updaters, etc. all go here.

      Most things in my game that require an update every frame go into one of those two process managers. There are a few exceptions, but only with things that need to be updated in a very specific order.

      Finally, I'd like to give you a little advice. My impression by your posts is that you're just starting out with trying to make a game. If that's true, I would completely and totally ignore threads. I wouldn't bother using them at all. They will cause you headaches and will provide you with little or no gain whatsoever. Your first games will be relatively simple. The big advantages to using threads don't come out until you start making much more complex games. For example, you probably won't need to have another thread that loads things for you in the background because you can probably just load everything up front. I created a loader thread specifically because I was creating an open-world game where I needed to be constantly streaming assets from the disk. Your first few games should be extremely simple, like clones of classic arcade games, so that won't be necessary.

      Hope that helps.

      -Rez
    • Thank you for your explanation it is clear now to me. Now I just need to figure out how to convert this into C#.NET. I have created game single thread sequential loop application. This is my first Multi Task/Thread game we as a team are making. Which is one of the reason I ordered the book from amazon :). I like it a lot its been one of the best out of my library that shows and explains game creation.

      By chance do you have a UML chart for your game structure?
    • Ok so after doing a lot of research I have come to the following conclusion.

      Threads are for doing a job such as rendering. Where all the thread does is produce the output by a list of object from a resource cache to be displayed on screen.

      So my class for doing this should be to complete this task and only this task.

      Now that bring me to my next question which is if that is so where or how would one add to the thread to do all the game logic. From what I can see you can not switch method on a thread. So there for unless you are taking a thread just for game logic value updates maybe? Such as life bar lost 50 point in the last loop. Which would also be a resource list of some sort?

      Now with that being said I also looked into parallel and schedule task for C#. Both of these are great if you want to share threads with all the logic but in this case i think what we are trying to do with a multithread loop is single out process for different parts of the game.

      Am I making sense to anyone other than myself?
    • Ok here is the code to my concept as I think it should be put together.

      // The Loop

      Source Code

      1. namespace Client.Plateform.Window.Lifetime
      2. {
      3. public class Loop : Client.Plateform.Window.OperatingSystem.Threads
      4. {
      5. /// <summary>
      6. ///
      7. /// </summary>
      8. private static bool _IsExit = false;
      9. /// <summary>
      10. ///
      11. /// </summary>
      12. public static bool IsExit
      13. {
      14. get
      15. {
      16. return _IsExit;
      17. }
      18. set
      19. {
      20. _IsExit = value;
      21. }
      22. }
      23. /// <summary>
      24. ///
      25. /// </summary>
      26. public Loop()
      27. {
      28. }
      29. /// <summary>
      30. ///
      31. /// </summary>
      32. public void Excute()
      33. {
      34. base.RenderThread.Start();
      35. base.LogicThread.Start();
      36. base.ResourceThread.Start();
      37. base.SoundThread.Start();
      38. }
      39. }
      40. }
      Display All


      // Thread

      Source Code

      1. using System;
      2. using System.Threading;
      3. namespace Client.Plateform.Window.OperatingSystem
      4. {
      5. public class Threads
      6. {
      7. #region Global Var
      8. private ThreadStart _RenderThreadStart = null;
      9. private Thread _RenderThread = null;
      10. private ThreadStart _SoundThreadStart = null;
      11. private Thread _SoundThread = null;
      12. private ThreadStart _ResourceThreadStart = null;
      13. private Thread _ResourceThread = null;
      14. private ThreadStart _LogicThreadStart = null;
      15. private Thread _LogicThread = null;
      16. #endregion
      17. #region Accessors
      18. /// <summary>
      19. /// asdfadsf
      20. /// </summary>
      21. public Thread RenderThread
      22. {
      23. get
      24. {
      25. return _RenderThread;
      26. }
      27. }
      28. /// <summary>
      29. /// asdfadsf
      30. /// </summary>
      31. public Thread SoundThread
      32. {
      33. get
      34. {
      35. return _SoundThread;
      36. }
      37. }
      38. /// <summary>
      39. /// asdfadsf
      40. /// </summary>
      41. public Thread ResourceThread
      42. {
      43. get
      44. {
      45. return _ResourceThread;
      46. }
      47. }
      48. /// <summary>
      49. /// asdfadsf
      50. /// </summary>
      51. public Thread LogicThread
      52. {
      53. get
      54. {
      55. return _LogicThread;
      56. }
      57. }
      58. #endregion
      59. #region Constructor
      60. /// <summary>
      61. ///
      62. /// </summary>
      63. public Threads()
      64. {
      65. _RenderThreadStart = new ThreadStart(() => RenderMethod());
      66. _RenderThread = new Thread(_RenderThreadStart);
      67. _SoundThreadStart = new ThreadStart(() => SoundMethod());
      68. _SoundThread = new Thread(_SoundThreadStart);
      69. _ResourceThreadStart = new ThreadStart(() => ResourceMethod());
      70. _ResourceThread = new Thread(_ResourceThreadStart);
      71. _LogicThreadStart = new ThreadStart(() => LogicMethod());
      72. _LogicThread = new Thread(_LogicThreadStart);
      73. }
      74. #endregion
      75. #region Method
      76. /// <summary>
      77. ///
      78. /// </summary>
      79. private void RenderMethod()
      80. {
      81. while (!Plateform.Window.Lifetime.Loop.IsExit)
      82. {
      83. Console.WriteLine("Render Method");
      84. Thread.Sleep(3000);
      85. }
      86. }
      87. /// <summary>
      88. ///
      89. /// </summary>
      90. private void SoundMethod()
      91. {
      92. while (!Plateform.Window.Lifetime.Loop.IsExit)
      93. {
      94. Console.WriteLine("Sound Method");
      95. Thread.Sleep(8000);
      96. }
      97. }
      98. /// <summary>
      99. ///
      100. /// </summary>
      101. private void ResourceMethod()
      102. {
      103. while (!Plateform.Window.Lifetime.Loop.IsExit)
      104. {
      105. Console.WriteLine("Resource Method");
      106. Thread.Sleep(6000);
      107. }
      108. }
      109. /// <summary>
      110. ///
      111. /// </summary>
      112. private void LogicMethod()
      113. {
      114. while (!Plateform.Window.Lifetime.Loop.IsExit)
      115. {
      116. Console.WriteLine("Logic Method");
      117. Thread.Sleep(13000);
      118. }
      119. }
      120. #endregion
      121. }
      122. }
      Display All