开发者

Is there any way to make rendering lots of moving objects on a Canvas faster (C# w/ WPF)

I'm making a particle game with (So far, only water), and the Draw() method inside an element is quite slow with a few going at once. I assume it's because it's calculating so much every second, but I'm still quite unhappy with it, considering they're just basic 2D shapes. How can I make it more efficient?

Here's my code:

(Loop) (Ran every 50 milisec开发者_JAVA技巧onds) (Note, I just included Forms for the timer, I'm using WPF.)

    void TimeKeeper_Tick(object sender, EventArgs e)
    {
        foreach (Element ele in Elements)
        {
            ele.Draw();
        }
    }

And draw method. Pos is a "Vector2" (Class I made to save me time):

    public void Draw()
    {
        Pos.Add(3, 10);

        if (Pos.Y > (int)canvas.ActualHeight)
        {
            Pos.Set(Pos.X, (int)shape.RenderSize.Width);
        }

        if (Pos.X+shape.RenderSize.Width > (int)canvas.ActualWidth)
        {
            Pos.Set(0, Pos.Y+(int)shape.RenderSize.Height);
        }

        shape.SetValue(Canvas.LeftProperty, (double)Pos.X);
        shape.SetValue(Canvas.TopProperty, (double)Pos.Y);

        if (canvas.Children.IndexOf(shape) != null)
        {
            canvas.Children.Remove(shape);
        }

        canvas.Children.Add(shape);
    }


Overriding Canvas.OnRender in a Canvas descendant is your best bet for lots of moving shapes.

WPF optimizes for static shapes. If you move all those static shapes around, you go through all the overhead of this optimization without benefitting from it (as the shapes move).

The code looks something like this:

public delegate void RenderEventHandler(CanvasRender sender, DrawingContext dc);

public class CanvasRender : Canvas
{
    public event RenderEventHandler Render;

    public CanvasRender()
    {
    }

    protected override void OnRender(DrawingContext dc)
    {
        if (Render != null)
            Render(this, dc);
        base.OnRender(dc);
    }
}

You then use this class instead of Canvas, and assign a handler to the Render event. In that event, you redraw your entire visual using explicit calls to the DrawingContext.

It may sound like you're doing much more work this way, but this actually is way more efficient than creating all the moving objects and then moving them around. Especially if those objects are lines whose angle/length changes over time, as WPF can't pre-render them and the pre-rendered shape when it moves.


If you want it to be really fast, you have to use some advanced techniques. Particularly these are so called Procedural Animations and animations using Writable Bitmaps.

Here is an excellent blog post describing those techniques (with an example of the particle effect): http://blogs.claritycon.com/blog/2011/03/30/advanced-animation-animating-15000-visuals-in-silverlight-2/. It is about Silverlight, but because Silverlight is a subset of WPF the techniques are the same.


A lot depends on what you consider 'slow'

If you move the shapes every 50 ms you have to move them more each interval to make them go faster.

But adding a timer like you did is not the best way to create animations in WPF.

There are a couple of options:

  1. specify a speed for each shape (something like: 10 units per second), then with each update of the screen move the shapes according to the time that passed since the last time it has been moved and its speed. So you need to keep track of the time elapsed too.

  2. Create Storyboards, they have their own timers (timelines) so all you have to do is set the start and the end position and specify the duration and finally Play the animation.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜