2009-10-03 50 views
2

我想創建一個非常簡單的遊戲Simon與WiiMote,使用WPF。我堅持的是如何使它基於轉向,程序阻塞,直到GUI完成顯示一個序列。C# - 如何阻止GUI或事件

這裏是我迄今(主要是基於一個答案在這裏: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)]); 
     } 

    } 
} 

然而,因爲自動設置的了WaitOne /組無法正常工作。它目前調用RunNextAction一次,但是無限期地阻塞waitOne。我究竟做錯了什麼?

編輯: 讓我試着重述這個問題。如果我拿出線程和的AutoResetEvent,在Window_Loaded我:

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. 
    } 

當我運行上面的代碼,它會調用一次RunNextAction,這將讓自稱,直到所有的方塊都顯示(似乎就像它自己的線程一樣),但WindowLoaded方法繼續下去。在我調用RunNextAction()之後,我需要Window_Loaded阻塞,直到RunNextAction完成。

+0

你的意思是說你想要輸入在播放動畫時不被接受? – RCIX 2009-10-03 00:43:23

+0

@RCIX是的,有點。由於RunActionOnce是遞歸類型的,我需要最後一次調用它(即顯示最後一個方塊)來通知調用者(Window_Loaded),以便它可以處理玩家的動作。 – 2009-10-03 01:08:59

回答

1

您不應該在調度程序線程上調用WaitOne!

您在調度程序線程本身調用WaitOne,調度程序線程是WPF APP的主線程,如果您阻止它,任何調用Dispatcher.BeginInvoke或Invoke的調用將永遠不會調用,並且將無限期地等待。

相反,更好的方法是將您的動畫移動到另一個名爲「AnimationDialog」的窗口並將其加載爲模態對話框。

private void window_loaded(object sender, EventArgs e){ 

    AnimationDialog dialog = new AnimationDialog(); 
    dialog.Owner = this; 
    dialog.ShowDialog(); // this will wait here 

} 

在AnimationDialog窗口...

private void Window_Loaded(object sender, EventArgs e){ 
    StartAnimationThread(); 
    // dont do any wait or block here at all... 
} 

// in your end of animation call "EndAnimation()" 

private void EndAnimation(){ 
    Dispatcher.BeginInvoke()((Action)delegate(){ 
     this.DialogResult = true; 
     // this will stop this dialog and 
     // and execute the parent window's 
     // code where showdialog was called... 
    } 
    ) 
} 
0

我可能會誤解你的問題,因爲這看起來很簡單。在您的Window_Loaded事件中,刪除thread.Start();後的所有內容。添加一個名爲_ImDoneSimonizing的類級變量,並將其初始化爲false。對於接收用戶輸入的任何方法,把這個包繞着代碼:

if (_ImDoneSimonizing) 
{ 
    // do whatever 
} 

當動畫完成後,設置_ImDoneSimonizingtrue。其他

兩點:

  1. 非常酷,你帶回西蒙。自Twister以來的最佳遊戲。
  2. 你可以使用WPF爲Wii創建遊戲嗎?先生,你震動了我的世界。
+1

這不是Wii本身,它實際上是一個學生項目。 WiiMote可以與藍牙適配器進行通話,並且這裏有一個非常好的用於處理WiiMote事件的C#庫:http://blogs.msdn.com/coding4fun/archive/2007/03/14/1879033.aspx。卡內基梅隆大學的研究員Johnny Lee提出了一些非常巧妙的方法來擴展WiiMote的使用,並且在http://johnnylee.net/projects/wii上有一些漂亮的視頻。 – 2009-10-03 02:51:56

+1

@David:我反正撒謊 - Simon吸了一口氣。 :) – MusiGenesis 2009-10-03 03:04:03

+0

@David:我已經在YouTube上看到了Johnny Lee的WiiMote項目,他們看起來非常酷。祝你好運! – Kredns 2009-10-03 03:13:31

0

AutoResetEvent正在執行它設計的任務,阻塞線程執行直到調用Set()。這可能意味着你沒有調用Set()。也許你的迭代器沒有按你期望的方式工作(雖然沒有測試它看起來沒問題)。

你是否在其他地方調用Window_Loaded?

+0

不,Window_Loaded應該只調用一次。也許問題是使用Dispatcher調用RunNextAction? – 2009-10-03 03:23:23