WPF:命中测试很慢。 [英] WPF: Hit testing is slow.

查看:76
本文介绍了WPF:命中测试很慢。的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在WPF中创建一个游戏。

我的目标是在画布的随机自由位置创建椭圆。

我需要排除由创建的椭圆覆盖的位置和在免费随机位置创建新的省略号。

我尝试过VisualTreeHelper.HitTest和Geometry FillContains,但它们很慢。



我该怎么办?它在WPF中?



代码(固定计时器):

I create a game in WPF.
My goal is to create ellipses at random free positions of a canvas.
I need to exclude positions overlaid by created ellipses and create new ellipses on free random positions.
I tried VisualTreeHelper.HitTest and Geometry FillContains, but they are slow.

How can i do it in WPF?

Code (fixed timer):

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

        this.Loaded += MainWindow_Loaded;
    }

    public Canvas canvas;
    Random random = new Random();
    int add = 5;

    Timer timerEllipses;

    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        canvas = new Canvas();
        canvas.Background = Brushes.Black;
        this.Content = canvas;

        timerEllipses = new Timer();
        timerEllipses.Interval = 1;
        timerEllipses.Start();

        Timer timer = new Timer();
        timer.Interval = 2000;
        timer.Elapsed += timer_Elapsed;
        timer.Start();
    }

    void timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        this.Dispatcher.Invoke((Action)(() =>
        {
            int add = this.add;

            int x = random.Next((int)canvas.ActualWidth);
            int y = random.Next((int)canvas.ActualHeight);

            for (int i1 = 0; i1 < canvas.ActualWidth; i1++)
                for (int i2 = 0; i2 < canvas.ActualHeight; i2++)
                    VisualTreeHelper.HitTest(canvas, new Point(i1, i2)); // Slows down

            Ellipse ellipse = new Ellipse();
            ellipse.Width = 1;
            ellipse.Height = 1;
            ellipse.Stroke = Brushes.White;
            canvas.Children.Add(ellipse);

            timerEllipses.Elapsed += delegate
            {
                this.Dispatcher.Invoke((Action)(() =>
                {
                    ellipse.Width += add;
                    ellipse.Height += add;

                    ellipse.Margin = new Thickness(x - (ellipse.Width / 2), y - (ellipse.Height / 2), 0, 0);

                }));
            };
        }));
    }
}





你可以看到一个单独的线程如何帮助我代码,当我点击测试画布获得免费积分。

代码(单独的线程):



You can see how a separate thread doesn't help in my code when i hit test the canvas to get free points.
Code (separate thread):

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

        this.Loaded += MainWindow_Loaded;
    }

    public Canvas canvas;
    Random random = new Random();

    Ellipse ellipse;
    int add = 5;
    double x = 0;
    double y = 0;

    Stopwatch stopwatch = new Stopwatch();
    public double elapsed = 10;

    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        canvas = new Canvas();
        canvas.Background = Brushes.Black;
        this.Content = canvas;

        ellipse = new Ellipse();
        ellipse.Width = 1;
        ellipse.Height = 1;
        ellipse.Stroke = Brushes.White;
        canvas.Children.Add(ellipse);

        new System.Threading.Thread(Thread1).Start();
    }

    void Thread1()
    {
        stopwatch.Start();

        while (true)
        {
            if (stopwatch.Elapsed.TotalMilliseconds >= elapsed)
            {
                stopwatch.Stop();

                this.Dispatcher.Invoke((Action)(() =>
                {
                    for (int i1 = 0; i1 < canvas.ActualWidth; i1++)
                        for (int i2 = 0; i2 < canvas.ActualHeight; i2++)
                            VisualTreeHelper.HitTest(canvas, new Point(i1, i2)); // Slows down

                    ellipse.Width += add;
                    ellipse.Height += add;

                    ellipse.Margin = new Thickness(x - (ellipse.Width / 2), y - (ellipse.Height / 2), 0, 0);
                }));

                stopwatch.Restart();
            }
        }
    }
}

推荐答案

啊!我知道了。我怎么能看到它?原则上你做的很糟糕:在Invoke下,在每个计时器事件中,你创建一个新的计时器,将它设置为触发其他事件,以及其他疯狂的事情。疯了吧。不要做那样的事情。即使使用计时器,问题也非常简单。我甚至不想讨论它。抛弃一切并写下合理的东西。



例如,线程解决方案将是最简单的。只需创建一个带循环的线程和一些 Thread.Sleep 。仅创建此线程一次,并在所有运行时期间使用它。在每个循环中,进行所有计算,并在调用下只做一件事:纯UI操作:创建椭圆并将其添加到画布(从画布中移除,类似的东西) 。这不简单,只是非常简单。



-SA
Ahhh! I see. How can I ansee it? You are doing awful thing in principle: under Invoke, on each timer event, you create a new timer, set it up to fire other events, and other crazy thing. This is insane. Don't do anything like that. The problem is amazingly simple, even with a timer. I don't even want to discuss it. Just throw out it all and write something reasonable.

For example, thread solution will be the simplest. Just create a thread with a loop and some Thread.Sleep. Create this thread only once and use it during all the runtime. In each loop, do all the calculation and do under Invoke only one thing: pure UI manipulation: creation of ellipse and adding it to canvas (removal from canvas, anything like that). This is not simple, just extremely simple.

—SA


可能这个代码项目文章可能会引起您的兴趣: WPF性能问题 [ ^ ]。
Possibly this Code Project article might interest you: "Solutions for WPF Performance Issue"[^].


做一些事情:

1.不订阅 Timer.Elapsed 多次活动。在事件处理程序中丢弃订阅和第二个 Dispatcher.BeginInvoke ;

2.不要像现在那样测试每个屏幕点。我想,你只需要测试单个随机点。如果这个地方不是空的,那么第二个。不是整个画布。

是的,测试成本很高,特别是如果你每次都为每个像素做这件事

3.尝试使用 DispatcherTimer 而不是计时器。在这种情况下,您将不需要在事件处理程序中 BeginInvoke
do some things:
1. don't subscribe on Timer.Elapsed events many times. Throw away subscription and second Dispatcher.BeginInvoke in event handler;
2. don't test every screen point as you do now. I think, you only need to hit test single random point. And if this place is not empty, the second one. Not whole canvas.
Yes, hittesting is expensive, especially if you do it every time for every pixel
3. try to use DispatcherTimer instead of Timer. In this case you won't need BeginInvoke in event handler at all


这篇关于WPF:命中测试很慢。的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆