Saturday, September 10, 2011

Writing an Extensible Application - Part 1

Hi All,


In the next posts I will try to examine some simple ways to make your application extensible. Most of the applications we use in our daily life have some form of extensibility. For example the Visual Studio has add ins, extensions, packages and other forms of extensibility which allow you integrate your own functionality to an existing application. For the purpose of our discussion I will define extensibility of an application simply as adding new application functionality without having to recompile any part of the application.

I will demonstrate briefly several simple ways to make your application extensible. To this end I have prepared a demo project (in fact, this post will discuss only this demo project).

The Balls Simulation

The demo project might be familiar to some of my readers since I take it from the new employee training program of my previous job. The application contains a rectangular area called "field". The user adds entities to that field called "balls" and they move inside the field. The balls may collide with each other during the movement within the field and they have to react accordingly. The balls may also collide with the walls and react accordingly.

A screenshot from my demo application
The most important part of writing an extensible application is to decide how the user will be able to extend it. In the case of my application the user will be able to extend the ball behavior using three aspects:

  • Movement - Determines how the ball is going to move around the field.
  • Drawing - Determines how the ball is going to be drawn on the filed.
  • Collision - Determines how the ball will react to a collision with another ball.
Now that I decided that these three items are my extensibility points I need to decide on the design of the application. I chose to implement it using the strategy design pattern. Each of the three elements mentioned above will have a strategy object which will be assigned to a ball. (The ball will be composed of the three element.) Now that this is decided I can define a dll that will be given to the extension writers, clients, which will contain the required interfaces. You don't want to give your clients a bulky dll that will contain all the classes and the logic but a thin layer which will not confuse them but will allow to write a complete extension set.

The interfaces I defined are:
  1. public interface IMover
  2. {
  3.   void Tick(IBall ball);
  4.   void Initialize(IBall ball);
  5.   void AtWalls(IBall ball, HashSet<WallType> wallTypes);
  6. }
 
  1. public interface IColider
  2. {
  3.   void AfterCollision(IBall ball, IBall other);
  4.   void BeforeCollision(IBall ball, IBall other);
  5.   void Collision(IBall ball, IBall other);
  6. }
 
  1. public interface IDrawer
  2. {
  3.   void Tick(IBall ball);
  4.   void Initialize(IBall ball);
  5. }
 
At this point I want to dedicate a short passage on coding games. The basic design of a game contains a game timer that paces the game. The game timer activates as fast as possible but sometimes it is limited to a specific frame rate - FPS (Frames Per Second). Each time the timer activates is called a game "tick". In each tick the game manager iterates on all the entities within the game (called "sprites") and calls a method that updates their inner parameters. Then the game manager iterates on all the sprites again and draws them on the screen.

As you can see I am using a 3 method collision handling. This method will not cover all the collision cases but it is good enough for many basic games including this demo application. The rules are simple: When two balls collide then the three collision methods are called one by one on each ball with an alternating order (see the code snippet next). On the BeforeCollision method the current ball can gather and data it needs from the other ball but it is not allowed to change its own data. On the Collision method the ball may change any of its parameters based on the data collected in the BeforeCollision method. On the AfterCollision method each ball may set any parameters on the other ball but not on itself.


Putting every thing mention above into code, results in the following Tick method of the timer:





  1. #region Handle Movement
  2. foreach (Ball ball in _balls)
  3. {
  4.   ball.Move();
  5.  
  6.   HashSet<WallType> hitWalls = new HashSet<WallType>();
  7.  
  8.   if (ball.Left <= 0)
  9.     hitWalls.Add(WallType.Left);
  10.  
  11.   if (ball.Top <= 0)
  12.     hitWalls.Add(WallType.Top);
  13.  
  14.   if (ball.Right >= MainCanvas.ActualWidth)
  15.     hitWalls.Add(WallType.Right);
  16.  
  17.   if (ball.Bottom >= MainCanvas.ActualHeight)
  18.     hitWalls.Add(WallType.Bottom);
  19.  
  20.   if (hitWalls.Count > 0)
  21.     ball.AtWalls(hitWalls);
  22. }
  23.  
  24. #endregion
  25.  
  26. #region Handle Collision
  27. for (int i = 0; i < _balls.Count; i++)
  28. {
  29.   for (int j = i + 1; j < _balls.Count; j++)
  30.   {
  31.     if (IsColliding(_balls[i], _balls[j]))
  32.     {
  33.       _balls[i].BeforeCollision(_balls[j]);
  34.       _balls[j].BeforeCollision(_balls[i]);
  35.       _balls[i].Collision(_balls[j]);
  36.       _balls[j].Collision(_balls[i]);
  37.       _balls[i].AfterCollision(_balls[j]);
  38.       _balls[j].AfterCollision(_balls[i]);
  39.     }
  40.   }
  41. }
  42. #endregion


(Note that in this first version the Drawer is not used but you will see it in play in the next post)


The main object of the game is the Ball class. It is exposed to the client as an interface and the client may interact with this class through the interface. All the Ball logic is managed within the class itself, feel free to browse the code there after downloading the demo application.


The application UI is quite simple. The field is on the left side. On the right side there are three combo boxes which will allow you to select the relevant strategy objects and a create button that generates a ball with the selected strategy objects. Both the Mover and the Drawer have Initialize methods that are called once the ball is created to allow the client position and draw the ball for the first time. Note that for this initial stage of the application the combo boxes are not used (since there is no extensibility yet) and the create button creates a small blue ball that just moves around and bounces off walls and other balls.


I encourage you to try out the code to get the feel of it before diving into the extensibility part in the next posts.


You can download the code from my SkyDrive here:


 


Thank you for reading,


Boris.


 


P.S.


Thanks to David Elentok you can see that the formatting is improved a little. I hope to improve it even more in future posts.

1 comment:

  1. Nice post, brings back old memories,
    Thanks for the mention :-)

    ReplyDelete