views:

744

answers:

2

Hi,

When trying to animate objects time/frame based in Silverlight (in contrast to using something like DoubleAnimation or Storyboard, which is not suitable e.g. for fast paced games), for example moving a spaceship in a particular direction every frame, the movement is jumpy and not really smooth. The screen even seems to tear.

There seems to be no difference between CompositionTarget and DistpatcherTimer. I use the following approach (in pseudocode):

Register Handler to Tick-Event of a DispatcherTimer
In each Tick:
Compute the elapsed time from the last frame in milliseconds
Object.X += movementSpeed * ellapsedMilliseconds

This should result in a smooth movement, right? But it doesn't. Here is an example (Controls: WASD and Mouse): Silverlight Game. Although the effect I described is not too prevalent in this sample, I can assure you that even moving a single rectangle over a canvas produces a jumpy animation.

Does someone have an idea how to minimize this. Are there other approaches to to frame based animation exept using Storyboards/DoubleAnimations which could solve this?

Edit: Here a quick and dirty approach, animating a rectangle with minimum code (Controls: A and D) Animation Sample

Xaml:

 <Grid x:Name="LayoutRoot" Background="Black">
    <Canvas Width="1000" Height="400" Background="Blue">
        <Rectangle x:Name="rect" Width="48" Height="48"
                   Fill="White"
                   Canvas.Top="200"
                   Canvas.Left="0"/>
    </Canvas>
</Grid>

C#:

private bool isLeft = false;
    private bool isRight = false;
    private DispatcherTimer timer = new DispatcherTimer();
    private double lastUpdate;

    public Page()
    {
        InitializeComponent();

        timer.Interval = TimeSpan.FromMilliseconds(1);
        timer.Tick += OnTick;

        lastUpdate = Environment.TickCount;
        timer.Start();
    }

    private void OnTick(object sender, EventArgs e)
    {
        double diff = Environment.TickCount - lastUpdate;

        double x = Canvas.GetLeft(rect);

        if (isRight)
            x += 1 * diff;
        else if (isLeft)
            x -= 1 * diff;

        Canvas.SetLeft(rect, x);

        lastUpdate = Environment.TickCount;
    }

    private void UserControl_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.D)
            isRight = true;

        if (e.Key == Key.A)
            isLeft = true;
    }

    private void UserControl_KeyUp(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.D)
            isRight = false;

        if (e.Key == Key.A)
            isLeft = false;
    }

Thanks! Andrej

A: 

Hi Andrej, I have got the same problem. There is one thing that I would do in a different way in the OnTick method: Save the Environment.TickCount once at the beginning of the method so that "lastUpdate" gets exactly the value which was used for the calculation of "diff". However, even with this changed, the jumpy, not smooth animation does not disapear. I hope someone can help us!

Johannes
+1  A: 

Updating the position every 1 millisecond is overkill because silverlight will simply not update the screen that fast. When you modify the position of an object like that (or any other ui update) silverlight marks the framebuffer as dirty and then it will eventually redraw the screen, however it will not redraw the screen more often than the MaxFrameRate.

To set the MaxFrameRate just:

Application.Current.Host.Settings.MaxFrameRate = 60;

This will limit your application to 60 frames per second which should be more than enough.

luke