2

WPFを使用して、WiiリモコンでゲームSimonの非常に単純なバージョンを作成しようとしています。私がこだわっているのは、GUIがシーケンスの表示を完了するまでプログラムがブロックする、ターンベースにする方法です。

これが私がこれまでに持っているコードです(主にここでの答えに基づいています:WPF-シーケンシャルアニメーションの簡単な例):

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)]);
        }

    }
}

ただし、autoSetのwaitOne/setは正しく機能していません。現在、RunNextActionを1回呼び出しますが、その後、waitOneを無期限にブロックします。私は何が間違っているのですか?

編集:質問を言い換えてみましょう。Threadingと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が1回呼び出され、すべての正方形が表示されるまで(独自のスレッドのように見えます)、それ自体が呼び出され続けますが、WindowLoadedメソッドは続行されます。RunNextAction()を呼び出した後、RunNextActionが完全に完了するまで、Window_Loadedをブロックする必要があります。

4

4 に答える 4

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... 
    }
    )
}
于 2009-10-03T06:27:38.433 に答える
0

これは非常に単純に見えるので、私はあなたの質問を誤解しているかもしれません。Window_Loadedイベントで、。以降のすべてを削除しますthread.Start();。、という名前のクラスレベル変数を追加し_ImDoneSimonizing、に初期化しfalseます。ユーザー入力を受け取るメソッドの場合は、次のコードをラップします。

if (_ImDoneSimonizing)
{
    // do whatever
}

アニメーションが終了したら、に設定_ImDoneSimonizingtrueます。

他の2つのポイント:

  1. あなたがサイモンを連れ戻すのはとてもクールです。ツイスター以来の最高のゲーム。
  2. WPFを使用してWii用のゲームを作成できますか?あなたは私の世界を揺るがしました、サー。
于 2009-10-03T02:28:37.790 に答える
0

AutoResetEventは、設計されたとおりに実行し、Set()が呼び出されるまでスレッドの実行をブロックします。これはおそらく、Set()を呼び出していないことを意味します。おそらく、イテレータが期待どおりに機能していない可能性があります(ただし、テストしなくても問題ないように見えます)。

Window_Loadedを別の場所で呼び出していますか?

于 2009-10-03T02:42:01.380 に答える
0

Windowsメッセージキューをポンピングすることでこれを解決できる可能性があります。基本的に、メッセージキューにコールバックを投稿すると、レンダリングが完了するまで呼び出し元のスレッドはブロックされます。これを行う方法を説明する記事は次のとおりです:http://graemehill.ca/wpf-rendering-thread-synchronization

于 2009-10-03T22:18:47.507 に答える