C# on idle

Started by
9 comments, last by Ratmil 14 years, 8 months ago
Hi. I am developing a C# game using GDI+ (it´s a very simple board game). I am using a timer for animations, but that´s pretty bad. Somewhere I heard about an OnIdle event that´s called when the program is, well, idle. But I can´t find on MSDN. Is there something like that in .NET? Or do I have to use a thread? Thanks.
Advertisement
Read Me!
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
It's the Application.Idle event, available from the Windows Forms API.

Note that it's fired when the application becomes idle, not when the application is idle. You generally want to spin in a loop in the idle handler making a call to a P/Invoke'd PeekMessage() to see if you should stop.
Thanks!!!
Using PickMessage would make my game windows dependant. I tried something like this:

public partial class GameForm : Form
{
public GameForm()
{
InitializeComponent();
gameApp = new GameApp(this);
lastUpdate = DateTime.Now;
closing = false;
timerMain.Enabled = true;
}

private void timerMain_Tick(object sender, EventArgs e)
{
timerMain.Enabled = false; // Disable timer at first call
while (!closing)
{
DateTime currentTime = DateTime.Now;
TimeSpan ellapsed = currentTime - lastUpdate;
lastUpdate = currentTime;
gameApp.Play(ellapsed);
Application.DoEvents();
}
}
}

What do you think?
Your game is Windows-dependent by virtue of using Windows.Forms to begin with. Granted you might have a shot with the Mono implementation, but honestly I'd just suggest writing at least partially separate code per OS.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
You are right, the game is Windows-dependent from the moment I created the project as Windows application.
So, it won´t run on Mono, right?
Anyway, my solution isn´t that bad, is it?
It might run on Mono, it might not. Could depend on versions. Cross platform support doesn't happen by magic; you have to test it.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Mono definitely won't like you invoking User32.dll. And I don't like Tom Miller's solution; he breaks platform independence for a performance boost that has questionable significance.

Considering that DirectX is a Windows API, it doesn't surprise me that his solution is in the DirectX SDK.

Mono's support for Windows.Forms is lousy (just like everything Mono has support for), but it does have support for it, and with a bit of twisting you can get it working (if you avoid using Application.DoEvents() at all costs!). You don't have any chance at getting a User32.dll pinvoke working though, I suggest using this method.

http://www.csharp-home.com/index/tiki-read_article.php?articleId=146&page=2

You won't even notice the difference.
I'm using this:
/// <summary>Control that automatically updates itself when the application is idle</summary>public partial class AutoRefreshingControl : Control {    /// <summary>Initializes a new auto-updating control</summary>    public AutoRefreshingPageControl() {        this.refreshPageDelegate = new MethodInvoker(refreshPage);        this.onApplicationIdleDelegate = new EventHandler(onApplicationIdle);    }    /// <summary>    ///   Wird aufgerufen wenn die Seite sich automatisch aktualisieren kann    /// </summary>    protected virtual void OnAutoRefresh() { }    /// <summary>Immediately releases all resource owned by the instance</summary>    /// <param name="calledManually">    ///   Whether Dispose() is being called manually (not by the GC)    /// </param>    protected override void Dispose(bool calledManually) {        if (calledManually) {            if(this.autoRefreshEnabled) {                stopAutoRefresh();            }        }        base.Dispose(calledManually);    }    /// <summary>Whether automatic refreshing is currently enabled</summary>    protected bool AutoRefreshEnabled {        get {            return this.autoRefreshEnabled;        }        set {            if(value == this.autoRefreshEnabled) {              return;            }            if(value) {                startAutoRefresh();            } else {                stopAutoRefresh();            }        }    }    /// <summary>Called when the control is created</summary>    protected override void OnCreateControl() {        if(this.autoRefreshEnabled) {            if(this.autoRefreshPostponed) {                this.autoRefreshPostponed = false;                startAutoRefresh();            }        }    }    /// <summary>Begins the auto-refresh cycle</summary>    private void startAutoRefresh() {        if(Created) {            // The ignition uses a BeginInvoke() call - the application will when send itself            // a window message, meaning it won't be idle during the first call to            // refreshPage(). That means we can use the Application.Idle event for the            // next call and vice versa.            // Since we're not looping in Application.Idle, multiple controls can update            // themselves using the application's idle time.            this.refreshPageAsyncResult = BeginInvoke(this.refreshPageDelegate);            Application.Idle += this.onApplicationIdleDelegate;        } else {            // Control hasn't been created yet. Ignition needs to be deferred until the            // Control has been created.            this.autoRefreshPostponed = true;        }        this.autoRefreshEnabled = true;    }    /// <summary>Stoppt die automatische Steuerelementaktualisierung</summary>    private void stopAutoRefresh() {        Application.Idle -= this.onApplicationIdleDelegate;        // Also reset autoRefreshPostponed zurücksetzen. While a second ignition would be        // caught, this is way cleaner because no useless code execution happens in the first place.        this.autoRefreshPostponed = false;        this.autoRefreshEnabled = false;    }    /// <summary>Runs the automatic control update</summary>    private void refreshPage() {        // Was this call caused by BeginInvoke()? If so, we need to call        // EndInvoke() and leave the next call up to the Application.Idle event        if(this.refreshPageAsyncResult != null) {            EndInvoke(this.refreshPageAsyncResult);            this.refreshPageAsyncResult = null;        } else {            this.refreshPageAsyncResult = BeginInvoke(this.refreshPageDelegate);        }        // Perform the automatic refresh        OnAutoRefresh();    }    /// <summary>Called when the application becomes idle</summary>    /// <param name="sender">Not used</param>    /// <param name="arguments">Not used</param>    private void onApplicationIdle(object sender, EventArgs arguments) {        refreshPage();    }    /// <summary>Delegate for the refreshPage() method</summary>    private MethodInvoker refreshPageDelegate;    /// <summary>Delegate for the onApplicationIdle() method</summary>    private EventHandler onApplicationIdleDelegate;    /// <summary>AsyncResult returned by the updatePage() call in progress.</summary>    private volatile IAsyncResult refreshPageAsyncResult;    /// <summary>Whether automatic refreshing is currently enabled</summary>    private volatile bool autoRefreshEnabled;    /// <summary>    ///   Is set when the ignition of the auto refresh cycle wasn't possible because    ///   the control hadn't been created yet.    /// </summary>    private volatile bool autoRefreshPostponed;}


It uses Application.Idle together with BeginInvoke() to allow for any number of controls making use of idle refresh (looping in Application.Idle with PeekMessage() works too, but will halt any other controls using the same technique - as I found out, XNA does it this way ;))
Professional C++ and .NET developer trying to break into indie game development.
Follow my progress: http://blog.nuclex-games.com/ or Twitter - Topics: Ogre3D, Blender, game architecture tips & code snippets.

This topic is closed to new replies.

Advertisement