2

I'm trying to create a realistic console type game with C#, and what I need it to do is append text like this:

hi
hello
awesome
nice

but instead of it going ALL at once, I want it to do this:

hi
(2 second pause)
hello
(4 second pause)
awesome
(1 second pause)
nice

I have tried this:

delegate void _pause(int time);
    void pause(int time)
    {
        if (txtInput.Text != "")
        {
            txtInput.Clear();
        }

        Stopwatch s = new Stopwatch();
        s.Start();
        while (s.Elapsed < TimeSpan.FromSeconds(time))
        {
        }
        s.Stop();
        return;
    }

And then I can call it like this Invoke(new _pause(pause),3); but that basically freezes the thread! So it pastes all at once anyway, just a delay before the montage of paste!

How can I do a smooth delay type system like I have described?

Oscar Mederos
  • 29,016
  • 22
  • 84
  • 124
Daaksin
  • 834
  • 3
  • 13
  • 28
  • Would using Thread.Sleep(time) not be easier? Rather than use the Stopwatch and then looping to check how much time has passed. – Jammerz858 Feb 21 '13 at 09:35
  • Thread.Sleep(time) actually cuts the process (basically) for that amount of time. Meaning, it will just be an instant paste. – Daaksin Feb 21 '13 at 09:47

2 Answers2

3

You should avoid busy-waiting and instead use a timer such as Task.Delay.

One possible way of doing that is by leveraging C# 4.5's new async feature:

async void converse() {
    txtInput.Text = "hi\n";
    await Task.Delay(2000);
    txtInput.Append("hello\n");
    await Task.Delay(4000);
    txtInput.Append("awesome\n");
    await Task.Delay(1000);
    txtInput.Append("nice\n");
}

By using the async syntax you are instructing the machine to run your code interleaved with other code - so that it can still do screen updates in the meantime, for instance. It automatically returns to the same SynchronizationContext, so in your case since you started on the GUI thread, you'll stay on it; this is important since only that thread may alter your text-box.

To summarize why you want to do it similar to this like this:

  • you avoid CPU-hogging busy waiting what wastes energy.
  • you avoid blocking the GUI thread so your updates appear on time (unlike CPU hogging, and unlike Thread.Sleep, for instance)
  • you don't need to particularly worry about thread scheduling as the async feature does that for you.

If you need to support .NET 2.0, you may want to look at the windows forms Timer. In short, this exposes event handlers you can hook which fire after a delay. However, as the example on the MSDN page illustrates, it's not as easy to use and will take much more manual orchestration.

Additionally, you could use a thread. If you do so, you are responsible for ensuring thread safety: you may NOT execute txtInput.Append on any thread other than the GUI thread:

void startConversation() {
    (new Thread(converse) {IsBackground = true}).Start();
}
void converse() { //called a different thread
    UI(() => txtInput.Text = "hi\n");
    Thread.Sleep(2000);
    UI(() => txtInput.Append("hello\n"));
    Thread.Sleep(4000);
    UI(() => txtInput.Append("awesome\n"));
    Thread.Sleep(1000);
    UI(() => txtInput.Append("nice\n"));
}
void UI(Action action) { this.BeginInvoke(action); }

This model is almost as simple as the async model. Two pitfalls are that you should avoid busy waiting (use Thread.Sleep) and that you must make sure you properly use BeginInvoke for all UI-affecting code. It's possibly even slightly faster than async. However, threads have a fairly large overhead (mostly in terms of memory), so this solution won't scale to large amounts of delayed interaction.

Summary

  • Use async if you can.
  • Alternatively, use a windows forms Timer, which is a hassle.
  • Or use a background thread - but beware of scaling issues and busy waiting.
Eamon Nerbonne
  • 47,023
  • 20
  • 101
  • 166
  • Is there any way to do this with C# 2.0? As I am creating my project in it to enable diversity among the types of users that download it. (I don't want to make them have to download updates and what not) – Daaksin Feb 21 '13 at 09:49
  • 1
    Sure, it's possible - but it's a lot more work, and a lot more error prone. I'd make users download updates rather that rewrite lots of core functionality to make up for missing features. – Eamon Nerbonne Feb 21 '13 at 09:51
  • Apparently there's a nuget package to enable at least partial .NET 4 support: http://blogs.msdn.com/b/bclteam/archive/2012/10/22/using-async-await-without-net-framework-4-5.aspx – Eamon Nerbonne Feb 21 '13 at 09:53
  • Thank you for your help! I found the solution by running it on a different thread! – Daaksin Feb 21 '13 at 10:01
  • @Daaksin: I added a thread-based implementation; including a few pitfalls to watch out for. I'd still suggest keeping it simple (i.e. `async`) until you actually have the luxury of having real users with this limitation. – Eamon Nerbonne Feb 21 '13 at 16:12
  • Incidentally, if you find the answer solves your problem, you could accept it as an answer (the checkmark). – Eamon Nerbonne Feb 21 '13 at 16:13
0

I fixed this issue via this method: Pause execution of a method without locking GUI. C#

It can be easily fixed by running the pause on a seperate thread, therefore not locking the UI. Thanks for everyone's help!

Community
  • 1
  • 1
Daaksin
  • 834
  • 3
  • 13
  • 28