Hi, I'm trying to create a very simple version of the game Simon with the WiiMote, using WPF. What I'm stuck on is how to make it turned-based, where the program blocks until the GUI is done displaying a sequence.
Here's the code I have so far (mostly based on an answer here: http://stackoverflow.com/questions/1511870/wpf-sequential-animation-simple-example):
public partial class Window1 : Window
{
public enum SimonSquare { BLUE = 1, GREEN = 3, RED = 5, YELLOW = 7 };
List<int> _correctSequence;
int _currentLevel = 1;
Random random = new Random();
Wiimote _wiiMote;
List<int> _squaresEntered;
private IEnumerator<Action> _actions;
Rectangle blueRect;
Rectangle redRect;
Rectangle greenRect;
Rectangle yellowRect;
AutoResetEvent autoEvent;
public Window1()
{
InitializeComponent();
blueRect = new Rectangle() { Fill =
System.Windows.Media.Brushes.Blue, Name = "Blue"};
redRect = new Rectangle() { Fill =
System.Windows.Media.Brushes.Red, Name = "Red" };
greenRect = new Rectangle() { Fill =
System.Windows.Media.Brushes.Green, Name = "Green" };
yellowRect = new Rectangle() { Fill =
System.Windows.Media.Brushes.Yellow, Name = "Yellow" };
UniformGrid1.Children.Add(new Rectangle() { Fill =
System.Windows.Media.Brushes.LightGray });
UniformGrid1.Children.Add(blueRect);
UniformGrid1.Children.Add(new Rectangle() { Fill =
System.Windows.Media.Brushes.LightGray });
UniformGrid1.Children.Add(redRect);
UniformGrid1.Children.Add(new Rectangle() { Fill =
System.Windows.Media.Brushes.LightGray });
UniformGrid1.Children.Add(greenRect);
UniformGrid1.Children.Add(new Rectangle() { Fill =
System.Windows.Media.Brushes.LightGray });
UniformGrid1.Children.Add(yellowRect);
UniformGrid1.Children.Add(new Rectangle() { Fill =
System.Windows.Media.Brushes.LightGray });
//connectWiiRemote();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_actions = AnimationSequence().GetEnumerator();
autoEvent = new AutoResetEvent(false);
Thread thread = new Thread(RunNextAction);
thread.Start();
autoEvent.WaitOne(); // need to block here somehow!
int x = 5;
}
IEnumerable<Action> AnimationSequence()
{
getSequence();
foreach(int square in _correctSequence)
{
if(square == (int) SimonSquare.BLUE)
yield return () => animateCell(blueRect, Colors.Blue);
else if(square == (int) SimonSquare.RED)
yield return () => animateCell(redRect, Colors.Red);
else if (square == (int)SimonSquare.GREEN)
yield return () => animateCell(greenRect, Colors.Green);
else if (square == (int)SimonSquare.YELLOW)
yield return () => animateCell(yellowRect, Colors.Yellow);
}
}
private void animateCell(Rectangle rectangle, Color fromColor)
{
this.Dispatcher.BeginInvoke(new Action(delegate
{
Color toColor = Colors.White;
ColorAnimation ani = new ColorAnimation(toColor,
new Duration(TimeSpan.FromMilliseconds(300)));
ani.AutoReverse = true;
SolidColorBrush newBrush = new SolidColorBrush(fromColor);
ani.BeginTime = TimeSpan.FromSeconds(2);
rectangle.Fill = newBrush;
ani.Completed += (s, e) => RunNextAction();
newBrush.BeginAnimation(SolidColorBrush.ColorProperty, ani);
}));
}
private void RunNextAction()
{
if (_actions.MoveNext())
_actions.Current();
else
{
autoEvent.Set();
_currentLevel++;
}
}
private void getSequence()
{
_correctSequence = new List<int>();
int[] values =
Enum.GetValues(typeof(SimonSquare)).Cast<int>().ToArray();
for (int i = 0; i < _currentLevel + 2; i++)
{
_correctSequence.Add(values[random.Next(values.Length)]);
}
}
}
However, autoSet's waitOne/set aren't working correctly. It currently calls RunNextAction once, but then blocks on waitOne indefinitely. What am I doing wrong?
EDIT: Let me try to rephrase the question. If I take out the Threading and AutoResetEvent, in Window_Loaded I have:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_actions = AnimationSequence().GetEnumerator();
RunNextAction(); // shows all of the flashing squares
// need to wait here until the flashing squares are all shown
// process player's events etc.
}
When I run the above code, it'll call RunNextAction once, which will keep calling itself until all of the squares are shown (it seems like on its own thread), BUT the WindowLoaded method keeps going. After I call RunNextAction(), I need Window_Loaded to block until RunNextAction is completely done.