Here’s a little something that I hope to use increasingly in the future : elements of functional programming to facilitate modification of state over time or game loops, without using threads all over the place. It’s nothing new, and there are other solutions (like Nick Gravelyn’s Interpolators and Timers), but I tried to make it as concise and generic as I could.
Here it is, more comments after the listing.
using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.GamerServices; namespace Foo { public class WaitUntil : IGameComponent, IUpdateable { public static Game Game { private get; set; } public static void GuideDisappears(Action onValid) { Game.Components.Add(new WaitUntil(_ => !Guide.IsVisible, onValid)); } public static void TimePassed(float secondsToWait, Action onValid) { Game.Components.Add(new WaitUntil(waited => (waited as TimeKeeper).Elapsed.TotalSeconds > secondsToWait, (elapsed, waited) => (waited as TimeKeeper).Elapsed += elapsed, onValid, new TimeKeeper())); } class TimeKeeper { public TimeSpan Elapsed; } readonly Func<object, bool> condition; readonly Action<TimeSpan, object> whileWaiting; readonly Action onValid; readonly object state; WaitUntil(Func<object, bool> condition, Action onValid) : this(condition, ActionHelper.NullAction, onValid) { } WaitUntil(Func<object, bool> condition, Action<TimeSpan, object> whileWaiting, Action onValid) : this(condition, whileWaiting, onValid, null) { } WaitUntil(Func<object, bool> condition, Action<TimeSpan, object> whileWaiting, Action onValid, object state) { this.condition = condition; this.whileWaiting = whileWaiting; this.onValid = onValid; this.state = state; } public void Update(GameTime gameTime) { if (condition(state)) { onValid(); Game.Components.Remove(this); } else whileWaiting(gameTime.ElapsedGameTime, state); } #region Stuff we don't care about public void Initialize() { } public bool Enabled { get { return true; } } public event EventHandler EnabledChanged; public event EventHandler UpdateOrderChanged; public int UpdateOrder { get { return 0; } } #endregion } public static class ActionHelper { public static void NullAction<T, U>(T t, U u) { } } }
I ended up using basically a GameComponent, which means I need access to a Game instance to add it and remove it from the component collection. I decided to use a static field (that you assign in the Game’s constructor) to avoid passing it every time. It’s very unlikely that the Game instance will change or be destroyed… and I already static-ified it in my ServiceHelper earlier anyway.
I also wrote a couple (okay, two) static factory methods that are slightly fluent-interface-ey.
// (from the context of your Game class implementation) { // Say "OK" when the guide stops being visible, like this? Components.Add(new WaitUntil(_ => !Guide.IsVisible, () => Console.WriteLine("OK!"))); // ...or like this! WaitUntil.GuideDisappears(() => Console.WriteLine("OK!")); // And while we're at it... WaitForTwoSeconds(); } void WaitForTwoSeconds() { Console.WriteLine("Will wait for two seconds..."); // ...recursive timers! WaitUntil.TimePassed(2, WaitForTwoSeconds); }
I’ll probably add new factory methods as the needs arise, and make the class overall more useful, but I feel like it’s a good start.
I used the “GuideDisappears” method when a gamer signs out and I want to show a warning message using the Xbox Guide before going back to a sign-up screen… but since signing out is usually performed from the Guide itself, you have to wait for it to close before doing anything. This seemed like the simplest solution, and it works great.
Hi,
I wanted to say thanks for posting this! It really helped me figure out delegates and lambda functions.
I have been reading your blog entries for a while. They are really great. Hope to see more neet stuff in the future.
Also, I may use something like your WaitUntil class in an engine I’m working on. However, I don’t like the required cast from object inside Func. I guess it could be avoided by using generics on WaitUntil but that imposes other issues.
Hi, thanks for writing!
I actually played with the idea more and got two component classes : a non-generic Waiter and generic Waiter<T> class. The generic version is stateful, the other stateless, and no more casting!
I’d like to finish it up a bit more before posting, but I will in the near future.
Hi, I know it’s been quite a few years, but did you end up releasing your modified version of this? I saw your more recent Unity code, but nothing related to the work you mentioned here.
Just found your website today, and the blog posts on it have been really interesting, thanks for everything!
Oh thanks!
Here’s the code I was referring to. It’s still in the XNA/MonoGame world and I haven’t touched it in years, so your mileage may vary : https://gist.github.com/renaudbedard/3904b9c7e853cf78959c
Edit : A couple of notes caveats after reading the code…
– The “scene” stuff is just a container for active components, like game screens or levels. It could be just the Game.
– There is still no way to cancel an active waiter in that implementation. I thought I had a cancellable version I made in a game jam, but I can’t find it at the moment…?