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.