Source code: http://silverlightrocks.com/cs/files/folders/silverlight_2_tutorials_source_code/entry337.aspx
So I did this with the old tutorials, and a little differently, this time I'm going to try to keep it a bit cleaner and to follow better namespacing and project layout, etc. One of the goals of this blog is to help you write games faster, and one way to do that is to give you some helper classes that can get you going and you can use in your own games. I have added a class library project to the SpaceRocks solution called BlueRoseGames.Helpers and when it makes sense, I'll add classes there so that you can use them in your own projects.
Anything that I add to that class library or anything else I blog about here you are welcome to use and modify in your own games and redistribute freely. It is all provided "as is", and if you're using it for any other purpose than in your own applications (for example, if you wanted to redistribute it as part of your own library, etc) please contact me for permission.
Ok so now that that's out of the way, the first thing I've added to the Helpers class library is the two game loop implementations (Storyboard and DispatcherTimer) that were covered in the Creating a Game Loop tutorials. They both inherit from an abstract GameLoop class and can be used easily in your own game. First the code...
GameLoop.cs:
1: using System;
2: using System.Windows;
3: using System.Windows.Controls;
4: using System.Windows.Documents;
5: using System.Windows.Ink;
6: using System.Windows.Input;
7: using System.Windows.Media;
8: using System.Windows.Media.Animation;
9: using System.Windows.Shapes;
10:
11: namespace BlueRoseGames.Helpers.Timers
12: {
13: public abstract class GameLoop
14: {
15: protected DateTime lastTick;
16: public delegate void UpdateHandler(TimeSpan elapsed);
17: public event UpdateHandler Update;
18:
19: public void Tick()
20: {
21: DateTime now = DateTime.Now;
22: TimeSpan elapsed = now - lastTick;
23: lastTick = now;
24: if (Update != null) Update(elapsed);
25: }
26:
27: public virtual void Start()
28: {
29: lastTick = DateTime.Now;
30: }
31:
32: public virtual void Stop()
33: {
34: }
35: }
36: }
So what we have are a Start, Stop, and Tick method, and an Update event that provides the elapsed time since the last update. By providing this abstract class, it is easy to swap out the two timers for each other without changing other code.
Now for the Storyboard technique. This class is called StoryboardGameLoop.
StoryboardGameLoop.cs:
1: using System;
2: using System.Windows;
3: using System.Windows.Controls;
4: using System.Windows.Documents;
5: using System.Windows.Ink;
6: using System.Windows.Input;
7: using System.Windows.Media;
8: using System.Windows.Media.Animation;
9: using System.Windows.Shapes;
10:
11: namespace BlueRoseGames.Helpers.Timers
12: {
13: public class StoryboardGameLoop : GameLoop
14: {
15: bool stopped = true;
16: Storyboard gameLoop = new Storyboard();
17:
18: public StoryboardGameLoop(FrameworkElement parent) : this(parent, 0)
19: {
20:
21: }
22:
23: public StoryboardGameLoop(FrameworkElement parent, double milliseconds)
24: {
25: gameLoop.Duration = TimeSpan.FromMilliseconds(milliseconds);
26: gameLoop.SetValue(FrameworkElement.NameProperty, "gameloop");
27: parent.Resources.Add(gameLoop);
28: gameLoop.Completed += new EventHandler(gameLoop_Completed);
29: }
30:
31: public override void Start()
32: {
33: stopped = false;
34: gameLoop.Begin();
35: base.Start();
36: }
37:
38: public override void Stop()
39: {
40: stopped = true;
41: base.Stop();
42: }
43:
44: void gameLoop_Completed(object sender, EventArgs e)
45: {
46: if (stopped) return;
47: base.Tick();
48: (sender as Storyboard).Begin();
49: }
50: }
51: }
The constructor takes a FrameworkElement, since Storyboards need to be added to the Resources collection of a FrameworkElement, and optionally a milliseconds between updates value. This will default to 0 if not specified.
The DispatcherTimerGameLoop is similar.
DispatcherTimerGameLoop.cs:
1: using System;
2: using System.Windows;
3: using System.Windows.Controls;
4: using System.Windows.Documents;
5: using System.Windows.Ink;
6: using System.Windows.Input;
7: using System.Windows.Media;
8: using System.Windows.Media.Animation;
9: using System.Windows.Shapes;
10: using System.Windows.Threading;
11:
12: namespace BlueRoseGames.Helpers.Timers
13: {
14: public class DispatcherTimerGameLoop : GameLoop
15: {
16: DispatcherTimer t = new DispatcherTimer();
17: public DispatcherTimerGameLoop() : this(0)
18: {
19: }
20:
21: public DispatcherTimerGameLoop(double milliseconds)
22: {
23: t.Interval = TimeSpan.FromMilliseconds(milliseconds);
24: t.Tick += new EventHandler(t_Tick);
25: t.Start();
26: }
27:
28: public override void Start()
29: {
30: t.Start();
31: base.Start();
32: }
33:
34: public override void Stop()
35: {
36: t.Stop();
37: base.Stop();
38: }
39:
40: void t_Tick(object sender, EventArgs e)
41: {
42: base.Tick();
43: }
44:
45: }
46: }
Now to use these in our game. The Page class gets a lot simpler. First the constructor:
1: public Page()
2: {
3: InitializeComponent();
4: this.Loaded += new RoutedEventHandler(Page_Loaded);
5: }
For the Storyboard game loop, we need to wait until the page is loaded to start it, so it's better to handle the Loaded event and set up our game loop in there:
1: void Page_Loaded(object sender, RoutedEventArgs e)
2: {
3: StoryboardGameLoop gameLoop = new StoryboardGameLoop(this);
4: // DispatcherTimerGameLoop gameLoop = new DispatcherTimerGameLoop();
5: gameLoop.Update += new GameLoop.UpdateHandler(gameLoop_Update);
6: gameLoop.Start();
7: }
Pretty straightforward stuff. We create our game loop object, wire up its Update event, and start the game loop. If you want to use a DispatcherTimer instead, comment out the game loop declaration and uncomment the other one.
Then all that's left is to handle the Update event:
1: void gameLoop_Update(TimeSpan elapsed)
2: {
3: // do your game loop processing here
4: ship.RotationAngle += 100 * elapsed.TotalSeconds;
5: }
I went through this pretty quickly because the details about how the game loop works have already been covered, and this was basically a refactoring. I hope you get good use out of these helper classes and others that I'll add along the way.
Share this post : |
digg it!
| dotnetkicks it!
| technorati! |