[.net] Game Loop in C#?

Started by
12 comments, last by GizmoSK 19 years, 5 months ago
The only thing you can do to avoid this is have your message processing and rendering in two different threads.
"Voilà! In view, a humble vaudevillian veteran, cast vicariously as both victim and villain by the vicissitudes of Fate. This visage, no mere veneer of vanity, is a vestige of the vox populi, now vacant, vanished. However, this valorous visitation of a bygone vexation stands vivified, and has vowed to vanquish these venal and virulent vermin vanguarding vice and vouchsafing the violently vicious and voracious violation of volition. The only verdict is vengeance; a vendetta held as a votive, not in vain, for the value and veracity of such shall one day vindicate the vigilant and the virtuous. Verily, this vichyssoise of verbiage veers most verbose, so let me simply add that it's my very good honor to meet you and you may call me V.".....V
Advertisement
Here's a console number guess game I wrote a while back which could show you some logic you could use:

using System;using ConsoleExtension;namespace NumberGuess{	/// <summary>	/// Summary description for Class1.	/// </summary>	class NumberGuess	{		/// <summary>		/// The main entry point for the application.		/// </summary>		[STAThread]		static void Main(string[] args)		{			//			// TODO: Add code to start application here			//			ConsoleColor.SetForeGroundColor(ConsoleColor.ForeGroundColor.Blue);			Console.WriteLine("Welcome to Number Guess! \n");			StartGameLoop();		}		public static void StartGameLoop()		{			int iInput, iNumber = 0;			string sInput;			bool bGameIsNotOver = true;			bool bPlayAgain = true;			Random randomClass = new System.Random();			iNumber = randomClass.Next();			iNumber = iNumber % 10 + 1;						while(bGameIsNotOver)			{				ConsoleColor.SetForeGroundColor(ConsoleColor.ForeGroundColor.Green);				Console.WriteLine("Please Guess a Number between 1 and 10");							sInput = Console.ReadLine();				try				{					iInput = Int32.Parse(sInput);										if (iInput < 1 || iInput > 10)					{						ConsoleColor.SetForeGroundColor(ConsoleColor.ForeGroundColor.Red);						Console.WriteLine("Number must be between 1 and 10");					}					else					{						if (iInput == iNumber)						{							ConsoleColor.SetForeGroundColor(ConsoleColor.ForeGroundColor.Yellow);							Console.WriteLine("Congratulations!!  You have won!\n");							bGameIsNotOver = false;						}						if (iInput < iNumber)						{							ConsoleColor.SetForeGroundColor(ConsoleColor.ForeGroundColor.Yellow);							Console.WriteLine("Too low!\n");						}						if (iInput > iNumber)						{							ConsoleColor.SetForeGroundColor(ConsoleColor.ForeGroundColor.Yellow);							Console.WriteLine("Too high!\n");						}					}				}				catch (Exception e)				{					Console.WriteLine(e.Message);				}					}			ConsoleColor.SetForeGroundColor(ConsoleColor.ForeGroundColor.Yellow);			Console.WriteLine("Play again?  (y/n)\n");			while (bPlayAgain)			{				try 				{					sInput = Console.ReadLine();					sInput.ToString();					if (sInput.Equals("y") || sInput.Equals("Y"))					{						StartGameLoop();						bPlayAgain = false;					}					if (sInput.Equals("n") || sInput.Equals("N"))					{						ConsoleColor.SetForeGroundColor(ConsoleColor.ForeGroundColor.Yellow);						Console.WriteLine("GoodBye!");						bPlayAgain = false;					}					else					{						ConsoleColor.SetForeGroundColor(ConsoleColor.ForeGroundColor.Yellow);						Console.WriteLine("I didn't understand.\n");						Console.WriteLine("Play again?  (y/n)\n");					}				}				catch (Exception e)				{					Console.WriteLine(e.Message);				}			}																	}	}}


Or, with forms, things are obviously a bit more complicated. Here's the main code for the same game using a form with buttons for each number:

using System;using System.Drawing;using System.Collections;using System.ComponentModel;using System.Windows.Forms;using System.Data;namespace WinGuess{	/// <summary>	/// Summary description for Form1.	/// </summary>	public class frmNumberGuess : System.Windows.Forms.Form	{		//Variable Declaration		public static int iNumber;		public static Random randomClass;		private System.Windows.Forms.Button cmdButtonOne;		private System.Windows.Forms.Button cmdButtonTwo;		private System.Windows.Forms.Button cmdButtonThree;		private System.Windows.Forms.Button cmdButtonFour;		private System.Windows.Forms.Button cmdButtonFive;		private System.Windows.Forms.Button cmdButtonSix;		private System.Windows.Forms.Button cmdButtonSeven;		private System.Windows.Forms.Button cmdButtonEight;		private System.Windows.Forms.Button cmdButtonNine;		private System.Windows.Forms.Button cmdButtonTen;		private System.Windows.Forms.Button cmdCloseButton;		private System.Windows.Forms.TextBox txtResult;		private System.Windows.Forms.Panel pnlButtonPanel;		private System.Windows.Forms.Button cmdPlayAgain;		private System.Windows.Forms.Label lblTitle;		/// <summary>		/// Required designer variable.		/// </summary>		private System.ComponentModel.Container components = null;		public frmNumberGuess()		{			//			// Required for Windows Form Designer support			//			InitializeComponent();			iNumber = 0;            randomClass	= new System.Random();			StartGame();			//			// TODO: Add any constructor code after InitializeComponent call			//		}		/// <summary>		/// Clean up any resources being used.		/// </summary>		protected override void Dispose( bool disposing )		{			if( disposing )			{				if (components != null) 				{					components.Dispose();				}			}			base.Dispose( disposing );		}		#region Windows Form Designer generated code		/// <summary>		/// Required method for Designer support - do not modify		/// the contents of this method with the code editor.		/// </summary>		private void InitializeComponent()		{			this.cmdButtonOne = new System.Windows.Forms.Button();			this.cmdButtonTwo = new System.Windows.Forms.Button();			this.cmdButtonThree = new System.Windows.Forms.Button();			this.cmdButtonFour = new System.Windows.Forms.Button();			this.cmdButtonFive = new System.Windows.Forms.Button();			this.cmdButtonSix = new System.Windows.Forms.Button();			this.cmdButtonSeven = new System.Windows.Forms.Button();			this.cmdButtonEight = new System.Windows.Forms.Button();			this.cmdButtonNine = new System.Windows.Forms.Button();			this.cmdButtonTen = new System.Windows.Forms.Button();			this.cmdCloseButton = new System.Windows.Forms.Button();			this.txtResult = new System.Windows.Forms.TextBox();			this.pnlButtonPanel = new System.Windows.Forms.Panel();			this.cmdPlayAgain = new System.Windows.Forms.Button();			this.lblTitle = new System.Windows.Forms.Label();			this.pnlButtonPanel.SuspendLayout();			this.SuspendLayout();			// 			// cmdButtonOne			// 			this.cmdButtonOne.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));			this.cmdButtonOne.Location = new System.Drawing.Point(8, 8);			this.cmdButtonOne.Name = "cmdButtonOne";			this.cmdButtonOne.Size = new System.Drawing.Size(48, 48);			this.cmdButtonOne.TabIndex = 1;			this.cmdButtonOne.Text = "1";			this.cmdButtonOne.Click += new System.EventHandler(this.cmdButtonOne_Click);			// 			// cmdButtonTwo			// 			this.cmdButtonTwo.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));			this.cmdButtonTwo.Location = new System.Drawing.Point(64, 8);			this.cmdButtonTwo.Name = "cmdButtonTwo";			this.cmdButtonTwo.Size = new System.Drawing.Size(48, 48);			this.cmdButtonTwo.TabIndex = 2;			this.cmdButtonTwo.Text = "2";			this.cmdButtonTwo.Click += new System.EventHandler(this.cmdButtonTwo_Click);			// 			// cmdButtonThree			// 			this.cmdButtonThree.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));			this.cmdButtonThree.Location = new System.Drawing.Point(120, 8);			this.cmdButtonThree.Name = "cmdButtonThree";			this.cmdButtonThree.Size = new System.Drawing.Size(48, 48);			this.cmdButtonThree.TabIndex = 3;			this.cmdButtonThree.Text = "3";			this.cmdButtonThree.Click += new System.EventHandler(this.cmdButtonThree_Click);			// 			// cmdButtonFour			// 			this.cmdButtonFour.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));			this.cmdButtonFour.Location = new System.Drawing.Point(176, 8);			this.cmdButtonFour.Name = "cmdButtonFour";			this.cmdButtonFour.Size = new System.Drawing.Size(48, 48);			this.cmdButtonFour.TabIndex = 4;			this.cmdButtonFour.Text = "4";			this.cmdButtonFour.Click += new System.EventHandler(this.cmdButtonFour_Click);			// 			// cmdButtonFive			// 			this.cmdButtonFive.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));			this.cmdButtonFive.Location = new System.Drawing.Point(232, 8);			this.cmdButtonFive.Name = "cmdButtonFive";			this.cmdButtonFive.Size = new System.Drawing.Size(48, 48);			this.cmdButtonFive.TabIndex = 5;			this.cmdButtonFive.Text = "5";			this.cmdButtonFive.Click += new System.EventHandler(this.cmdButtonFive_Click);			// 			// cmdButtonSix			// 			this.cmdButtonSix.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));			this.cmdButtonSix.Location = new System.Drawing.Point(8, 64);			this.cmdButtonSix.Name = "cmdButtonSix";			this.cmdButtonSix.Size = new System.Drawing.Size(48, 48);			this.cmdButtonSix.TabIndex = 6;			this.cmdButtonSix.Text = "6";			this.cmdButtonSix.Click += new System.EventHandler(this.cmdButtonSix_Click);			// 			// cmdButtonSeven			// 			this.cmdButtonSeven.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));			this.cmdButtonSeven.Location = new System.Drawing.Point(64, 64);			this.cmdButtonSeven.Name = "cmdButtonSeven";			this.cmdButtonSeven.Size = new System.Drawing.Size(48, 48);			this.cmdButtonSeven.TabIndex = 7;			this.cmdButtonSeven.Text = "7";			this.cmdButtonSeven.Click += new System.EventHandler(this.cmdButtonSeven_Click);			// 			// cmdButtonEight			// 			this.cmdButtonEight.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));			this.cmdButtonEight.Location = new System.Drawing.Point(120, 64);			this.cmdButtonEight.Name = "cmdButtonEight";			this.cmdButtonEight.Size = new System.Drawing.Size(48, 48);			this.cmdButtonEight.TabIndex = 8;			this.cmdButtonEight.Text = "8";			this.cmdButtonEight.Click += new System.EventHandler(this.cmdButtonEight_Click);			// 			// cmdButtonNine			// 			this.cmdButtonNine.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));			this.cmdButtonNine.Location = new System.Drawing.Point(176, 64);			this.cmdButtonNine.Name = "cmdButtonNine";			this.cmdButtonNine.Size = new System.Drawing.Size(48, 48);			this.cmdButtonNine.TabIndex = 9;			this.cmdButtonNine.Text = "9";			this.cmdButtonNine.Click += new System.EventHandler(this.cmdButtonNine_Click);			// 			// cmdButtonTen			// 			this.cmdButtonTen.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));			this.cmdButtonTen.Location = new System.Drawing.Point(232, 64);			this.cmdButtonTen.Name = "cmdButtonTen";			this.cmdButtonTen.Size = new System.Drawing.Size(48, 48);			this.cmdButtonTen.TabIndex = 10;			this.cmdButtonTen.Text = "10";			this.cmdButtonTen.Click += new System.EventHandler(this.cmdButtonTen_Click);			// 			// cmdCloseButton			// 			this.cmdCloseButton.Location = new System.Drawing.Point(192, 176);			this.cmdCloseButton.Name = "cmdCloseButton";			this.cmdCloseButton.Size = new System.Drawing.Size(88, 32);			this.cmdCloseButton.TabIndex = 11;			this.cmdCloseButton.Text = "Close";			this.cmdCloseButton.Click += new System.EventHandler(this.cmdCloseButton_Click);			// 			// txtResult			// 			this.txtResult.Location = new System.Drawing.Point(8, 144);			this.txtResult.Name = "txtResult";			this.txtResult.Size = new System.Drawing.Size(272, 20);			this.txtResult.TabIndex = 12;			this.txtResult.Text = "";			// 			// pnlButtonPanel			// 			this.pnlButtonPanel.Controls.Add(this.cmdButtonSix);			this.pnlButtonPanel.Controls.Add(this.cmdButtonFive);			this.pnlButtonPanel.Controls.Add(this.cmdButtonSeven);			this.pnlButtonPanel.Controls.Add(this.cmdButtonTen);			this.pnlButtonPanel.Controls.Add(this.cmdButtonOne);			this.pnlButtonPanel.Controls.Add(this.cmdButtonTwo);			this.pnlButtonPanel.Controls.Add(this.cmdButtonFour);			this.pnlButtonPanel.Controls.Add(this.cmdButtonNine);			this.pnlButtonPanel.Controls.Add(this.cmdButtonThree);			this.pnlButtonPanel.Controls.Add(this.cmdButtonEight);			this.pnlButtonPanel.Location = new System.Drawing.Point(0, 24);			this.pnlButtonPanel.Name = "pnlButtonPanel";			this.pnlButtonPanel.Size = new System.Drawing.Size(288, 120);			this.pnlButtonPanel.TabIndex = 13;			// 			// cmdPlayAgain			// 			this.cmdPlayAgain.Location = new System.Drawing.Point(88, 176);			this.cmdPlayAgain.Name = "cmdPlayAgain";			this.cmdPlayAgain.Size = new System.Drawing.Size(88, 32);			this.cmdPlayAgain.TabIndex = 14;			this.cmdPlayAgain.Text = "Play Again?";			this.cmdPlayAgain.Visible = false;			this.cmdPlayAgain.Click += new System.EventHandler(this.cmdPlayAgain_Click);			// 			// lblTitle			// 			this.lblTitle.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));			this.lblTitle.Location = new System.Drawing.Point(0, 0);			this.lblTitle.Name = "lblTitle";			this.lblTitle.Size = new System.Drawing.Size(288, 24);			this.lblTitle.TabIndex = 15;			this.lblTitle.Text = "Guess A Number!";			this.lblTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;			// 			// frmNumberGuess			// 			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);			this.ClientSize = new System.Drawing.Size(288, 214);			this.Controls.Add(this.lblTitle);			this.Controls.Add(this.cmdPlayAgain);			this.Controls.Add(this.pnlButtonPanel);			this.Controls.Add(this.txtResult);			this.Controls.Add(this.cmdCloseButton);			this.Name = "frmNumberGuess";			this.Text = "Number Guess";			this.pnlButtonPanel.ResumeLayout(false);			this.ResumeLayout(false);		}		#endregion		/// <summary>		/// The main entry point for the application.		/// </summary>		[STAThread]		static void Main() 		{			Application.Run(new frmNumberGuess());		}		public static void StartGame()		{			iNumber = frmNumberGuess.randomClass.Next();			iNumber = iNumber % 10 + 1;					}					private void cmdButtonOne_Click(object sender, System.EventArgs e)		{			if (iNumber == 1)			{				txtResult.Text = "Congratulations, you've won!";				pnlButtonPanel.Enabled = false;				cmdPlayAgain.Visible = true;			}			else			{				txtResult.Text = "Too low!";				cmdButtonOne.Enabled = false;			}		}		private void cmdButtonTwo_Click(object sender, System.EventArgs e)		{			if (iNumber == 2)			{				txtResult.Text = "Congratulations, you've won!";				pnlButtonPanel.Enabled = false;				cmdPlayAgain.Visible = true;			}			else			{				cmdButtonTwo.Enabled = false;				if (iNumber < 2)				{					txtResult.Text = "Too high!";				}				else				{					txtResult.Text = "Too low!";				}			}		}		private void cmdButtonThree_Click(object sender, System.EventArgs e)		{			if (iNumber == 3)			{				txtResult.Text = "Congratulations, you've won!";				pnlButtonPanel.Enabled = false;				cmdPlayAgain.Visible = true;			}			else			{				cmdButtonThree.Enabled = false;				if (iNumber < 3)				{					txtResult.Text = "Too high!";				}				else				{					txtResult.Text = "Too low!";				}			}		}		private void cmdButtonFour_Click(object sender, System.EventArgs e)		{			if (iNumber == 4)			{				txtResult.Text = "Congratulations, you've won!";				pnlButtonPanel.Enabled = false;				cmdPlayAgain.Visible = true;			}			else			{				cmdButtonFour.Enabled = false;				if (iNumber < 4)				{					txtResult.Text = "Too high!";				}				else				{					txtResult.Text = "Too low!";				}			}		}		private void cmdButtonFive_Click(object sender, System.EventArgs e)		{			if (iNumber == 5)			{				txtResult.Text = "Congratulations, you've won!";				pnlButtonPanel.Enabled = false;				cmdPlayAgain.Visible = true;			}			else			{				cmdButtonFive.Enabled = false;				if (iNumber < 5)				{					txtResult.Text = "Too high!";				}				else				{					txtResult.Text = "Too low!";				}			}		}		private void cmdButtonSix_Click(object sender, System.EventArgs e)		{			if (iNumber == 6)			{				txtResult.Text = "Congratulations, you've won!";				pnlButtonPanel.Enabled = false;				cmdPlayAgain.Visible = true;			}			else			{				cmdButtonSix.Enabled = false;				if (iNumber < 6)				{					txtResult.Text = "Too high!";				}				else				{					txtResult.Text = "Too low!";				}			}		}		private void cmdButtonSeven_Click(object sender, System.EventArgs e)		{			if (iNumber == 7)			{				txtResult.Text = "Congratulations, you've won!";				pnlButtonPanel.Enabled = false;				cmdPlayAgain.Visible = true;			}			else			{				cmdButtonSeven.Enabled = false;				if (iNumber < 7)				{					txtResult.Text = "Too high!";				}				else				{					txtResult.Text = "Too low!";				}			}		}		private void cmdButtonEight_Click(object sender, System.EventArgs e)		{			if (iNumber == 8)			{				txtResult.Text = "Congratulations, you've won!";				pnlButtonPanel.Enabled = false;				cmdPlayAgain.Visible = true;			}			else			{				cmdButtonEight.Enabled = false;				if (iNumber < 8)				{					txtResult.Text = "Too high!";				}				else				{					txtResult.Text = "Too low!";				}			}		}		private void cmdButtonNine_Click(object sender, System.EventArgs e)		{			if (iNumber == 9)			{				txtResult.Text = "Congratulations, you've won!";				pnlButtonPanel.Enabled = false;				cmdPlayAgain.Visible = true;			}			else			{				cmdButtonNine.Enabled = false;				if (iNumber < 9)				{					txtResult.Text = "Too high!";				}				else				{					txtResult.Text = "Too low!";				}			}		}		private void cmdButtonTen_Click(object sender, System.EventArgs e)		{			if (iNumber == 10)			{				txtResult.Text = "Congratulations, you've won!";				pnlButtonPanel.Enabled = false;				cmdPlayAgain.Visible = true;			}			else			{				cmdButtonTen.Enabled = false;				txtResult.Text = "Too high!";			}		}		private void cmdCloseButton_Click(object sender, System.EventArgs e)		{			Application.Exit();		}		private void cmdPlayAgain_Click(object sender, System.EventArgs e)		{			cmdPlayAgain.Visible = false;			pnlButtonPanel.Enabled = true;			txtResult.Text = "New Game Started.";			resetButtons();			StartGame();		}		private void resetButtons()		{			cmdButtonOne.Enabled = true;			cmdButtonTwo.Enabled = true;			cmdButtonThree.Enabled = true;			cmdButtonFour.Enabled = true;			cmdButtonFive.Enabled = true;			cmdButtonSix.Enabled = true;			cmdButtonSeven.Enabled = true;			cmdButtonEight.Enabled = true;			cmdButtonNine.Enabled = true;			cmdButtonTen.Enabled = true;		}	}}


That should give you some ideas.
oh hai
With regard to the paint/invalidate method mentioned above, after benchmarking this appears to be a very slow method of drawing - possibly negating any benefits gained from not using DoEvents.

In my code at the moment, I use DoEvents and it doesn't appear to suck a particularly large amount of memory - nor do I see a large degradation in performance due to promoting things to gen1 and the garbage collector running more frequently for gen0 etc.

Of course, using the Peek/GetMessage way is the fastest, but as another poster mentioned this seems like a step backwards.

Also - will these old win32 functions be supported under native longhorn applications? If not, and you can get good enough performance from DoEvents, then why not use it?

I've been recently encountering a similar issue. An application that I've been working on uses the Application.DoEvents call. DoEvents() raises the following error after several hours:

An unhandled exception of type 'System.ArgumentException' occurred in mscorlib.dll
Additional information: Item has already been added. Key in dictionary: "-1" Key being added: "-1"

Unfortunately this error occurs after approximately 9 hours of running time. The application usually has just been idly running on the machine. (We're into a deep testing phase and this issue just came up out of the blue.)

The callstack at crashtime is equally unhelpful.
HashTable.Insert
HashTable.Add
ComponentManager.System.Windows.Forms.UnsafeMethods + IMsoComponenetManager.FRegisterComponent
ThreadContext.get_ComponentManager
ThreadContext.RunMessageLoopInner
ThreadContext.RunMessageLoop
Application.DoEvents
Mainform.Main

Application.Run doesn't seem to have this problem. The application has a rendering window that the user can interact with and several Windows Forms that provide the UI.

I was just wondering if anyone had a similar experience and could help give me some insight.

I'm about to try the new DX9 sdk way of doing things, but like any good software engineer, I'd like to know what the problem is before jumping off onto another code path. Any thoughts?

This topic is closed to new replies.

Advertisement