Archive for the 'Silverlight' category

Making Silverlight Move

Dec 16 2007 Published by under Flash, Silverlight

I got a bit more time to play around with Silverlight. As mentioned earlier, I figured the best way for me to start out would be to do some of the basic things I did in the first few chapters of “Making Things Move” – basically moving a ball around with velocity, add some gravity, bouncing, dragging and throwing, maybe even venture into some easing and springs.

The goal was also to use straight code to do everything. The only tool I’m using for this is the Visual Studio 2008 trial with the Silverlight 1.1 extensions. I created a new C# based Silverlight project and went from there.

This is what I came up with:

http://www.bit-101.com/silverlight/msm01/TestPage.html

The first thing I ran into was the complete lack of any kind of mechanism allowing for iterated code execution – like ActionScript’s enterFrame, or even a timer! I’m still baffled as to why this was left out. No timer??? Anyway, it seems the Silverlight community has come up with a solution of creating a Storyboard object, calling Begin() on it, listening for its Completed event, and then re-calling the Begin() method continuously. It seems like quite a hack to me, but it works.

The second problem I discovered was the verboseness of pretty much any statement used to get or set any property on any created object. Everything requires object.SetValue(propertyName, value) and (typecast)object.GetValue(propertyName). Yuck.

So I set about abstracting some of this stuff so it’s easier to work with. First the Storyboard stuff. At first I was defining the Storyboard in XAML, but realized it can actually be created by pure code as well. This allowed me to abstract it all into what I called a FrameBeacon class. Thsi is largely based on a GameLoop class I found on SilverlightGames101 (very useful site, btw).

My class is more basic than what they have, and could be upgraded as time goes on, but I think is simple enough to see what’s going on. This is created by adding a new Class to your project.

[csharp]using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace MakingSilverlightMove
{
public class FrameBeacon
{
private Canvas _canvas;
private Storyboard _sb;
private DateTime _lastUpdate;
private TimeSpan _elapsed;

public delegate void UpdateDelegate(TimeSpan ElapsedTime);
public event UpdateDelegate Update;

public FrameBeacon(Canvas canvas)
{
_canvas = canvas;
_sb = new Storyboard();
_sb.SetValue(Storyboard.NameProperty, “sb”);
_canvas.Resources.Add(_sb);
_sb.Completed += new EventHandler(_sb_Completed);
_lastUpdate = DateTime.Now;

_sb.Begin();
}

void _sb_Completed(object sender, EventArgs e)
{
_elapsed = DateTime.Now – _lastUpdate;
Update(_elapsed);
_sb.Begin();
_lastUpdate = DateTime.Now;
}
}
}[/csharp]

You can see that the constructor takes a Canvas object, as the Storyboard it creates needs to be added to a canvas’s resources. It then creates a Storyboard, names it, adds it to the canvas’s resources, adds a listener for the Completed event, and starts the Storyboard by calling its Begin method. Note that you could set the Duration property with a TimeSpan formatted like so: “hh:mm:ss.ffff” (hours, minutes, seconds, fractions of second. For instance, to set it to run at 60 fps, you could set duration to “00:00:00.017” as 17 milliseconds is roughly 1/60th of a second. Theoretically, you could get a specific rate of updates, but I’m not sure how accurate that would be. Like most of the other examples I saw out there though, I used a different method of just letting the Storyboard run as fast as it can, measuring the time between updates and adjusting animation based on that. This is a technique I covered in the “Tips and Tricks” section of Making Things Move – it results in a uniform rate of animation independent of frame rate. Though, I admit I was usually too lazy to use this technique myself in ActionScript. 🙂 To do this, you take note of the time at the end of each update and store it in _lastUpdate. Then you you subtract that from the current time at the beginning of each update to get the elapsed time. This is passed when you dispatch the update event. Don’t forget to call Begin() again on the storyboard, or you’ll get one frame, then nothing.

With this class in place, getting an enterFrame type of update is as simple as doing this in the main application class:

[csharp]_frameBeacon = new FrameBeacon(this);
_frameBeacon.Update += new FrameBeacon.UpdateDelegate(_frameBeacon_Update);[/csharp]

And you can now put all your animation code in the _frameBeacon_Update method, which will run repeatedly, like an onEnterFrame function. Also, this method gets passed a TimeSpan object called ElapsedTime. We’ll see how to use that later.

Next up, abstracting some of those nasty GetValue/SetValue methods.

I wanted a ball that I could bounce around the screen by manipulating its X and Y properties, rather than going though all the more verbose methods. While I was at it, I added a bunch more stuff and started a rudimentary particle type class.

I started by going to my project and adding a new item, Silverlight User Control, naming it Ball.xaml. this creates the Ball.xaml file you can see below, plus a Ball.xaml.cs code behind file.

XAML:
[xml]

[/xml]

All I changed here was setting the Width and Height to 1 and x:Name to “ball” so I could reference it within the code.

Finally, I changed the Ball.xaml.cs file little by little til I came up with this:

[csharp]using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace MakingSilverlightMove
{
public class Ball : Control
{
private double _vx;
private double _vy;
private double _gravity;
private Rect _bounds;
private double _bounce;
private double _radius;
FrameworkElement lroot;

public Ball(double radius, String col)
{
_radius = radius;
System.IO.Stream s = this.GetType().Assembly.GetManifestResourceStream(“MakingSilverlightMove.Ball.xaml”);
lroot = this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd());
_vx = 0;
_vy = 0;
_bounds = new Rect(0, 0, 500, 500);
_bounce = -1.0;
Width = radius * 2;
Height = radius * 2;
Ellipse ellipse = new Ellipse();
ellipse.SetValue(Ellipse.WidthProperty, _radius * 2);
ellipse.SetValue(Ellipse.HeightProperty, _radius * 2);
ellipse.SetValue(Canvas.LeftProperty, -_radius);
ellipse.SetValue(Canvas.TopProperty, -_radius);
ellipse.SetValue(Ellipse.FillProperty, col);
Canvas ball = (Canvas)lroot.FindName(“ball”);
ball.Children.Add(ellipse);
}

public Rect Bounds
{
set
{
_bounds = value;
}
get
{
return _bounds;
}
}

public double Bounce
{
set
{
_bounce = value;
}
get
{
return _bounce;
}
}

public double Gravity
{
set
{
_gravity = value;
}
get
{
return _gravity;
}
}

public double VX
{
set
{
_vx = value;
}
get
{
return _vx;
}
}

public double VY
{
set
{
_vy = value;
}
get
{
return _vy;
}
}

public double X
{
set
{
this.SetValue(Canvas.LeftProperty, value);
}
get
{
return (double)this.GetValue(Canvas.LeftProperty);
}
}

public double Y
{
set
{
this.SetValue(Canvas.TopProperty, value);
}
get
{
return (double)this.GetValue(Canvas.TopProperty);
}
}

public void Move(double x, double y)
{
X = x;
Y = y;
}

public void Move(Point p)
{
Move(p.X, p.Y);
}

public void Update(double elapsed)
{
_vy += _gravity;
X += _vx * elapsed;
Y += _vy * elapsed;
if (X + _radius > _bounds.Right)
{
X = _bounds.Right – _radius;
_vx *= _bounce;
}
else if (X – _radius < _bounds.Left) { X = _bounds.Left + _radius; _vx *= _bounce; } if (Y + _radius > _bounds.Bottom)
{
Y = _bounds.Bottom – _radius;
_vy *= _bounce;
}
else if (Y – _radius < _bounds.Top) { Y = _bounds.Top + _radius; _vy *= _bounce; } } } } [/csharp] Note that the constructor takes a radius and color. This is used to create a new Ellipse and add it to the canvas like so: [csharp]Ellipse ellipse = new Ellipse(); ellipse.SetValue(Ellipse.WidthProperty, _radius * 2); ellipse.SetValue(Ellipse.HeightProperty, _radius * 2); ellipse.SetValue(Canvas.LeftProperty, -_radius); ellipse.SetValue(Canvas.TopProperty, -_radius); ellipse.SetValue(Ellipse.FillProperty, col); Canvas ball = (Canvas)lroot.FindName("ball"); ball.Children.Add(ellipse);[/csharp] This code should be relatively understandable even to a newcomer. The rest of the code is mainly adding various methods to manipulate the ball, and getters and setters, the coolest ones of which are: [csharp]public double X { set { this.SetValue(Canvas.LeftProperty, value); } get { return (double)this.GetValue(Canvas.LeftProperty); } }[/csharp] And a similar one for the Y property. These abstract the GetValue/SetValue stuff so you can now just say ball.X, ball.Y. Yum! If you are an ActionScript dude, you might be cringing at the Capital X and Y, and First Caps for Method and Variable Names. But that's the C# convention. when in Rome... The Update method should be very familiar to anyone who's read the first few chapters of Making Things Move. The main difference is that it takes a parameter of the elapsed time since the last update. Note that this is a double, not a TimeSpan. It is actually the elapsed time in terms of seconds, so it will come in as a small fraction. Also, because of this, the _vx and _vy are much higher values, as they are in terms of pixels per second, not pixels per frame. When this is multiplied by the elapsed fraction, it will result in a smaller pixel distance value to move each frame. If there was a longer time since the last update, the elapsed value will be larger, making it move more. If there was less time, the value will be smaller, and the object will move less. Finally, the main application xaml and class. These get created when you create the project. I modified the main xaml to change the dimensions and background color: [xml]

[/xml]

Don’t forget to change the dimensions in the html file too.

Last but not least, the Page.xaml.cs main application class:

[csharp]using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace MakingSilverlightMove
{
public partial class Page : Canvas
{
private Ball ball;
private FrameBeacon _frameBeacon;

public void Page_Loaded(object o, EventArgs e)
{
// Required to initialize variables
InitializeComponent();
ball = new Ball(10, “Green”);
ball.Move(100, 100);
ball.VX = 300;
ball.VY = 120;
ball.Gravity = 15;
ball.Bounce = -0.8;
ball.Bounds = new Rect(0, 0, Width, Height);
Children.Add(ball);

_frameBeacon = new FrameBeacon(this);
_frameBeacon.Update += new FrameBeacon.UpdateDelegate(_frameBeacon_Update);
}

void _frameBeacon_Update(TimeSpan ElapsedTime)
{
ball.Update(ElapsedTime.TotalSeconds);
}
}
}[/csharp]

All I had to do here was create a ball, giving it a radius and color, change a few other properties, and create a FrameBeacon and listen for its Update event. In that handler, I just call Update on the ball, passing in the total elapsed seconds. Simple.

Again, you can see the results here:

http://www.bit-101.com/silverlight/msm01/TestPage.html

OK, this is a pretty simple experiment, but already the Ball and FrameBeacon classes are very useful. I’d probably want to turn Ball into something like Particle, and add a method to get graphics into it rather than always just drawing an ellipse, as well as adding some more useful methods. And upgrade the FrameBeacon class so it can be turned off and on, and maybe even try explicitly setting a frame rate.

Also, on my year and a half old, somewhat low end Compaq Presario, I notice some definite visual stuttering in this animation. However, it runs beautifully on my Mac Book Pro. I’m curious to see what others see.

7 responses so far

Thoughts on Silverlight

Dec 11 2007 Published by under Flash, Silverlight

After getting back from Microsoft, I dug up my old Compaq laptop, dusted it off and installed Vista Ultimate and Expression Studio (full disclosure – these were gifts from MS). Then I installed Silverlight 1.1, the Visual Studio 2008 trial, and the Silverlight 1.1 extensions for VS 2008 and got to work on some tutorials.

Once I got a few basic things working, I started trying out some stuff on my own. I figured the first thing would be to make a red ball that moved around the screen with x and y velocity, then give it gravity, make it bounce, make it draggable, etc. I ran into some things that made me wonder about people who say things like, “Flash is hard to learn. Silverlight is so much easier.” I’m honestly not trying to take a dig at Silverlight here. If someone can explain an easier way of doing things or something I’m missing, please do.

First off, making the ball was pretty easy. I made a canvas with an ellipse. Gave the canvas a name so I can access it in the C# code behind class. It’s there, giving code hints and everything. Great. So far, just like Flex.

Now, I need the equivalent of an “enterFrame” or a Timer… looking…. looking…… hmmm. Nothing like that at all. Time to search the web. I found this:

http://silverlight.net/blogs/jesseliberty/archive/2007/12/09/did-you-know-that-you-can-create-a-timer-using-xaml-animation.aspx

And several other links that recommend the exact same thing. This, as far as I can tell, is the accepted way of doing programmatic animation. Basically, you create a Storyboard object in XAML, and inside that put a DoubleAnimation, giving it a Duration.

Now, a DoubleAnimation, for you Flash folks, is analogous to some stuff in the various ActionScript tween packages. It’s designed to tween a double precision floating point number from one value to another. But it’s only being used as a makeshift timer here. The duration is set as a fraction of a second, like “00:00:00.017” which is about a 60th of a second. You can set up some dummy property to tween, or just leave it as is. When the “tween” is finished, it fires a complete event. In your C# class, you listen for the complete event and this is like your onEnterFrame or onTimer handler in Flash. The problem is, the Storyboard really is complete. It’s not a continuous timer or loop. So in your handler, you have to call Begin() on the storyboard so that it will run again and give you another complete event.

OK, I guess that works, but it seems like a complete hack. It seems like some of the old hacks we used to use back in AS1. Again, I’m not trying to just bash it. This is just my reaction. Having some kind of timer or looping system to repeatedly run code seems like a cornerstone of creating programmatic animation. The fact that developers had to come up with a hack like this seems like a MAJOR omission. Maybe this will be addressed by Silverlight 2.0. I certainly hope so.

The next major thing I ran into was in addressing objects from XAML in C#. For a Flex analogy, say you create an object, any object, say a Button, in MXML. In ActionScript you can address its properties directly, like:

[as]_currentX = myButton.x[/as]

Compare that to what you need to do in Silverlight:

[as]_currentX = (double)Ball.GetValue(Canvas.LeftProperty);[/as]

This is like Flash 4 getProperty / setProperty stuff. Yuck.

And to set a property:

[as])Ball.SetValue(Canvas.TopProperty, 100.0);[/as]

So, whereas you could easily move something in AS3 like so:

[as]ball.x += 10;[/as]

In Silverlight, the equivalent code would be:

[as]Ball.SetValue(Canvas.LeftProperty, (double)Ball.GetValue(Canvas.LeftProperty) + 10.0);[/as]

Please tell me if I am missing something here. My gut feeling is this can’t be right. But every tutorial I see is written like this.

For example, the following code explains how to do drag and drop in Silverlight:

http://msmvps.com/blogs/luisabreu/archive/2007/06/14/drag-n-drop-on-silverlight-alpha.aspx

In ActionScript, 90% of this would boil down to calls to startDrag() and stopDrag().

Now it’s not that I am scared of lower level, more verbose code. But usually a lower level language gives you a lot more power and control over things. I don’t see that that is the case here. I don’t see that the APIs for Silverlight are really lower level than what you have in ActionScript, just more verbose.

Also, I see how I could easily write classes that abstract that kind of stuff. Have a CanvasProxy class or something that has direct x and y properties, and internally manipulates the LeftProperty and TopProperty of a real canvas. I guess my real point is that I’ve seen so often from the Microsoft camp that “Flash is sooo hard to learn, try Silverlight. It’s so much easier.” If that is the intent, then why not give higher level APIs that abstract that stuff and make things simpler for developers?

I feel I need to say it once more – I’m really looking for answers, not just digging on Silverlight. I know you MS guys are reading this! 😉 Let me know.

18 responses so far

« Newer posts