8

以下は、WPF でのコンウェイのライフ ゲームの (非常に素朴な) 実装です。ただのデモです...

xaml:

<Window x:Class="wpf_conway_life_2013_05_19.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="500" Width="500">
    <Grid>
        <Canvas Name="canvas"
            Width="auto"
            Height="auto" 
            HorizontalAlignment="Stretch"  
            VerticalAlignment="Stretch">
        </Canvas>
    </Grid>
</Window>

コードビハインド:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace wpf_conway_life_2013_05_19
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            var random = new Random();

            var data = new int[100, 100];

            var dataB = new int[100, 100];

            Func<int, int, int> at = (x, y) =>
                {
                    if (x < 0) x = 100 + x;
                    if (x >= 100) x = x % 100;
                    if (y < 0) y = 100 + y;
                    if (y >= 100) y = y % 100;

                    return data[x, y];
                };

            for (var x = 0; x < 100; x++)
                for (var y = 0; y < 100; y++)
                    data[x, y] = random.Next(2);

            var rectangles = new Rectangle[100, 100];

            for (var x = 0; x < 100; x++)
                for (var y = 0; y < 100; y++)
                {
                    rectangles[x, y] = new Rectangle();

                    canvas.Children.Add(rectangles[x, y]);
                }

            canvas.SizeChanged += (s, e) =>
                {
                    for (var x = 0; x < 100; x++)
                    {
                        for (var y = 0; y < 100; y++)
                        {
                            rectangles[x, y].Width = canvas.ActualWidth / 100;
                            rectangles[x, y].Height = canvas.ActualHeight / 100;

                            Canvas.SetLeft(rectangles[x, y], (canvas.ActualWidth / 100) * x);
                            Canvas.SetTop(rectangles[x, y], (canvas.ActualHeight / 100) * y);
                        }
                    }
                };

            Action macroStep = () =>
                {
                    dataB = new int[100, 100];

                    for (var x = 0; x < 100; x++)
                    {
                        for (var y = 0; y < 100; y++)
                        {
                            var neighbors = 0;

                            for (var i = -1; i <= 1; i++)
                                for (var j = -1; j <= 1; j++)
                                    if (i == 0 && j == 0)
                                        continue;
                                    else
                                        neighbors += at(x + i, y + j);

                            dataB[x, y] = data[x, y];

                            if (neighbors < 2) dataB[x, y] = 0;
                            if (neighbors == 3) dataB[x, y] = 1;
                            if (neighbors > 3) dataB[x, y] = 0;

                            rectangles[x, y].Fill = dataB[x, y] == 0 ? new SolidColorBrush(new Color()) : Brushes.Black;
                        }
                    }

                    data = dataB;
                };

            var timer = new DispatcherTimer();

            timer.Tick += (s, e) => macroStep();

            timer.Start();
        }
    }
}

外観は次のとおりです。

ここに画像の説明を入力

私が置き換えるnew SolidColorBrush(new Color())Brushes.White、プログラムの実行がはるかに遅くなります。なんで?

2010 Express を使用して Windows 7 64 ビットでテストしています。

4

2 に答える 2

1

アルファ値がゼロであるためnew Color()、完全に透明であるため、WPF がレンダリングする必要がないことを意味します。一方、白色のアルファは 255 であり、レンダリングする必要がある完全な白色であることを意味します。

于 2013-05-20T19:13:16.153 に答える
0

を使用することに特別なことは何もありませんBrushes.White

macroStep イベント ハンドラーの外部で独自のローカル ブラシを定義してフリーズすると、Brushes.White を使用した場合とまったく同じように動作します。最初に凍結しないと、動作がはるかに悪くなります。

最適なパフォーマンスは、ループの前に、macroStep への各呼び出しの開始時に一度ブラシを作成し、その後フリーズすることです。最も内側のループ内に新しいブラシを作成すると、速度が大幅に低下します。

また、動作の悪いコードのタイマーの間隔を長くすると、パフォーマンスの問題が実際に修正されます。私の推測では、毎回レンダリングが終了した後にバックグラウンド スレッドで発生する何らかのリソースのクリーンアップがあり、それはブラシの内部に関連付けられていますが、右に曲がっているため、クリーンアップを行うことができなくなります。次の反復でブラシを使用します。これを実証するために、ブラシのプールを作成し、毎回異なるブラシを使用します。

SolidColorBrush[] brushes = new SolidColorBrush[2];
for (int i = 0; i < brushes.Length; i++)
{
    var brush = new SolidColorBrush(new Color());
    brush.Freeze();
    brushes[i] = brush;
}
int brushIx = 0;

Action macroStep = () =>
{
    dataB = new int[100, 100];
    var brush = brushes[brushIx++ % brushes.Length];
...
    rectangles[x, y].Fill = dataB[x, y] == 0
        ? brush
        : Brushes.Black;
    data = dataB;
};

ブラシの数を 1 に設定すると、 を使用した場合と同じ動作になりBrushes.Whiteます。ただし、2 以上に設定すると、期待どおりのパフォーマンスが得られます。

于 2013-06-04T02:49:23.790 に答える