Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / XAML

Carrom+ AIO

4.91/5 (11 votes)
20 Sep 2013CPOL9 min read 29.6K  
The best and most unconventional virtual carrom application optimized for AIO!

This article is an entry in our AppInnovation Contest. Articles in this sub-section are not required to be full articles so care should be taken when voting.


Moved to Round 2!


Progress:

09/19/2013:

  • Finished implementing the Online Leaderboard.
  • Created a new extra large wide size board 'Wide+'.
  • Introduced a new 'Wood' theme!

Polishing the UI more and more!

New Screenshots:

Image 1

 Image 2


 Original Entry


Introduction 

Carrom+ AIO is the most innovative and unique virtual carrom application for Windows Desktop. Carrom+ AIO has been optimized for the Lenovo Horizon AIO and Windows 8. It is fun to play on both desktop and tablet/table modes of the Lenovo Horizon AIO. Carrom+ takes advantage of the unique capabilities of the Lenovo Horizon AIO and Windows 8 design principles to create a truly unique product both in design and performance. The game provides user with incredible visual feedback which keeps the user engaged and entertained for a long time. Carrom+ has its own set of rules which make it more interesting and engaging than conventional carrom.  It can be played by upto four players at the same time.

Carrom+ AIO is a Game and is the desktop port of Carrom+ Windows Store app developed by me and has been made using C# and XAML, Silverlight 5. This application targets Lenovo Horizon AIO device. It will be packaged, published and used as a desktop application.

Note: I’ve assumed that most of the readers of this article are familiar with Carrom game since it is one of the classic games available in most homes. That's why I don't want to waste everyone's time by explaining rules and concept of the game in detail. However, if you’re unfamiliar with it I suggest you read the following artile: http://en.wikipedia.org/wiki/Carrom

Some terms used in the article:

Carrom Men : 

The coins that are placed on the board. The goal of each player is to score as many as possible.

Striker:  

The object which is used to hit the carrom men in order to drop them in the holes present in the corners. This helps the player score points.

Goals

Carrom+ AIO attempts to score the following four goals:

  1. Lenovo Horizon AIO can be used as a tablet/table PC when it is lying flat or as a desktop when standing on its hinge. Lying flat orientation is perfect for Carrom+ AIO since conventional carrom has always been played that way. However, if the device is being used as a desktop it is imperative for the application to provide a comparable user experience. Thus, the first goal is to provide equally entertaining and fun experience in both the orientations of the AIO.
  2. Lenovo Horizon is a huge 27” table PC and is widescreen. Now we know that traditional carrom is square in shape. Thus, the second goal for Carrom+ AIO is to utilize this aspect ratio in such a way that is great for the users. Leaving all the extra space available is, obviously, not a smart option.
  3. The current mechanism to launch the striker which hits the carrom men is controlled by an arrow which can be used to set direction and power of the shot. I’ve been looking into the ‘Striker’ accessory of the Lenovo Horizon and I think it would be a great alternative to the arrow. I’ve not yet found a way to utilize it outside the bundled applications. This is the third goal.
  4. The fourth goal is to improve the classic carrom game itself and make it exploit the features of the device and the software i.e, introducing new modes which exploit the digital prowess of the target device.

Why Carrom? 

From what I’ve researched about the Lenovo Horizon I’ve come to the conclusion that this device can revolutionize how we play digital board games. All the videos on the Lenovo website point towards the same thing. Air Hockey, Monopoly etc they’re all great. Carrom+ AIO will be a great addition to all those games since it follows the same theme, it is a conventional game which has been greatly enhanced by the software and can take advantage of the touch capabilities/size of the device and also produce modern sound effects. When I developed Carrom+ for Windows Store I always imagined the best use case to be on something like the Horizon so, when I heard about this competition and the Lenovo Horizon the choice was absolutely clear to me!

Features

Here I’ll explain how Carrom+ AIO attempts to score all of the goals mentioned above:

1. Managing Orientations

Since, the Lenovo Horizon AIO can be used as both a Table PC and a Desktop the following two use cases arise for Carrom+ AIO (Two Players): 

First, consider the device lying flat and the two players sitting facing each other while sitting on either side of the device. This positions resembles how the carrom is played in real world. Carrom+ AIO has a tablet mode which takes care of this orientation.

Second, consider the device being used as a desktop and the two users are in front of the screen. In this case the application rotates the whole frame in order to allow the second player to take a front shot as well. The behaviour is demonstrated in the video and the screenshot.

Image 3

Thus, Carrom+ AIO takes care of both the orientations and is equal fun to play in both the ways.

Video: 

http://youtu.be/rMfEdQdTzms

2. Screen Size/Aspect Ratios

Lenovo Horizon is a huge 27” table PC and is widescreen but traditional carrom is a square one. To achieve the goal of maximum screen size utility Carrom+ AIO has a widescreen mode which takes advantage of the full size of the device. The widescreen mode greets the user with a new experience in Carrom which is much more fun. This way carrom can become a very enjoyable experience since the device size almost resembles the real carrom size and comes with all the software enhancements.  

Image 4

Image 5

Video: 

http://youtu.be/YRG7n1RI3mw

For the users who might use this application on tablets and other devices we have optional Square and 4:3 aspect ratios. The user can also go out of fullscreen if he wants to. Thus, Carrom+ AIO completely covers its bases when it comes to screen sizes/resolutions.  

3. Striker launch mechanism  

The current mechanism to launch the striker which hits the carrom men is controlled by an arrow which can be rotated to set direction and translated to set the power of the shot. It works equally well for both touch and mouse. 

Video: 

http://youtu.be/0u3JENGpZrA

I’ve been looking into the ‘Striker’ accessory of the Lenovo Horizon and I think it would be a great alternative to the arrow. I’ve not yet found a way to utilize it out of the bundled applications. From what I’ve read I believe it is not available to the third party developers.

If anyone has more information to share on this topic please do let me know!

4. Software enhancements

Carrom+ AIO is software powered and therefore, a lot more is possible if compared to real carrom games. Carrom+ AIO includes modern sound effects and visual feedback to bring a whole new carrom experience to the user. The colors and theme is all tageted towards the modern UI.

Image 6 

 Image 7

Carrom+ AIO also includes many unconventional game modes like the following two:

  1. Time trial: Here the player has to score all of the Carrom men in less than three minutes. 
  2. Combat: In this two player game mode the carrom men move in 10 specific motion patterns and the two players gets their chance to score all the 10 carrom men. Whoever scores the most wins! Moving the carrom men is very unconventional and this mode is exceptionally entertaining.
  3. Speed Trial: The player has to finish as fast as possible and then his score is uploaded to the online leaderboard.

Video:

http://youtu.be/NW_5t912h6Q

Progress 

As you can see in the video that the port is almost complete (started as soon as I heard about AppInnovationContest’13), the things I’m left with are optimizing it for touch but I’m assuming that it’ll work out well since in Silverlight 5 the touch events are promoted to click events automatically. Unfortunately, I don’t have a touch device with me right now to verify that.

I would also love to integrate the ‘Striker’ accessory in Carrom+ AIO. As soon as I get the device this will be my primary goal!

Using the code

Carrom+ AIO is a lot of code and there are things I would like to keep private but there is a lot to tell too! Let’s look into some of the interesting code segments of Crrom+ AIO: 

1. Physics 

For all the collision physics I’ve used the Farseer Physics Engine. The farseer wrapper which I'm using is Physics Helper XAML (available on CodePlex). It is very neat and works on Silverlight, WP7 and WinRT.

Adding Physics Helper XAML to your project:

  1. Get it from http://physicshelperxaml.codeplex.com/
  2. Right-click the solution and select "Add Existing Project". Browse to \PhysicsHelperXaml\Farseer Physics Engine SL\Farseer Physics Engine SL.csproj in the Physics Helper XAML ZIP download.
  3. Right-click the solution and select "Add Existing Project". Browse to \PhysicsHelperXaml\Spritehand.PhysicsHelper.Metro\Spritehand.PhysicsHelper.SL.csproj in the Physics Helper XAML ZIP download.
  4. From your main project, add References to the Farseer Physics Engine SL and Spritehand.PhysicsHelper.SL projects. 
Creating Farseer Objects:  

  • Add this namespace on top of your XAML page. 

XML
xmlns:FarseerHelper="using:Spritehand.FarseerHelper"   

  • Now Create a physics canvas, all the physics enabled sprites will be children of this canvas. MousePickEnabled helps the user to manipulate objects with touch/mouse.
XML
<FarseerHelper:PhysicsCanvas Height="768" Width="1366" MousePickEnabled="True">
</ FarseerHelper:PhysicsCanvas >   

  •  Now you can create sprites inside the canvas in the following manner:  
<FarseerHelper:PhysicsCanvas Height="768" Width="1366" MousePickEnabled="True">
    <FarseerHelper:PhysicsSprite Height="100" Width="100" Canvas.Left="311" Canvas.Top="540">
    <Rectangle Fill="#FFAC809A" Height="100" Stroke="Black" Width="100"/>
    </FarseerHelper:PhysicsSprite></ FarseerHelper:PhysicsCanvas >     

  • Canvas has various other properties like GravityHorizontal and GravityVertical which can help you do some amazing stuff.  Making a sprite Static makes it immovable and this technique should be used to make world boundaries.  

2. Moving the striker

This is something a lot of people ask me so I thought about including it in my article here. The following code moves the striker horizontally to where the user wants to take it within limits: 

private void mainStriker_MouseMoved(object sender, MouseEventArgs e)
       {
           var PosM = e.GetPosition(canvasMain);
           if (!manipulator)
               return;
               if (!menValues.gameIsPaused)
               {
                   if (currentSettings.pcType == 1)
                   {
                       if (selPlayerIndex == 0)
                       {
                           mainStriker.Position = new Vector2() { X = (float)(PosM.X), Y = mainStriker.Position.Y };
                           if (mainStriker.Position.X < gameBoundaries.xStrikerLimts.Item1)
                               mainStriker.Position = new Vector2() { X = gameBoundaries.xStrikerLimts.Item1, Y = mainStriker.Position.Y };
                           else if (mainStriker.Position.X > gameBoundaries.xStrikerLimts.Item2)
                               mainStriker.Position = new Vector2() { X = gameBoundaries.xStrikerLimts.Item2, Y = mainStriker.Position.Y };
                       }
                       else if (selPlayerIndex == 1)
                       {
                           mainStriker.Position = new Vector2() { X = (mainStriker.Position.X), Y = (float)(PosM.Y) };
                           if (mainStriker.Position.Y < gameBoundaries.yStrikerLimits.Item1)
                               mainStriker.Position = new Vector2() { X = mainStriker.Position.X, Y = gameBoundaries.yStrikerLimits.Item1 };
                           else if (mainStriker.Position.Y > gameBoundaries.yStrikerLimits.Item2)
                               mainStriker.Position = new Vector2() { X = mainStriker.Position.X, Y = gameBoundaries.yStrikerLimits.Item2 };
                       }
                       else if (selPlayerIndex == 2)
                       {
                           mainStriker.Position = new Vector2() { X = (float)(PosM.X), Y = mainStriker.Position.Y };
                           if (mainStriker.Position.X < gameBoundaries.xStrikerLimts.Item1)
                               mainStriker.Position = new Vector2() { X = gameBoundaries.xStrikerLimts.Item1, Y = mainStriker.Position.Y };
                           else if (mainStriker.Position.X > gameBoundaries.xStrikerLimts.Item2)
                               mainStriker.Position = new Vector2() { X = gameBoundaries.xStrikerLimts.Item2, Y = mainStriker.Position.Y };
                       }
                       else if (selPlayerIndex == 3)
                       {
                           mainStriker.Position = new Vector2() { X = (mainStriker.Position.X), Y = (float)(PosM.Y) };
                           if (mainStriker.Position.Y < gameBoundaries.yStrikerLimits.Item1)
                               mainStriker.Position = new Vector2() { X = mainStriker.Position.X, Y = gameBoundaries.yStrikerLimits.Item1 };
                           else if (mainStriker.Position.Y > gameBoundaries.yStrikerLimits.Item2)
                               mainStriker.Position = new Vector2() { X = mainStriker.Position.X, Y = gameBoundaries.yStrikerLimits.Item2 };

                       }
                   }
                   else
                   {
                       if (selPlayerIndex == 0 || selPlayerIndex == 2)
                       {
                           mainStriker.Position = new Vector2() { X = (float)(PosM.X), Y = mainStriker.Position.Y };
                           if (mainStriker.Position.X < gameBoundaries.xStrikerLimts.Item1)
                               mainStriker.Position = new Vector2() { X = gameBoundaries.xStrikerLimts.Item1, Y = mainStriker.Position.Y };
                           else if (mainStriker.Position.X > gameBoundaries.xStrikerLimts.Item2)
                               mainStriker.Position = new Vector2() { X = gameBoundaries.xStrikerLimts.Item2, Y = mainStriker.Position.Y };
                       }
                       else if (selPlayerIndex == 1 || selPlayerIndex == 3)
                       {
                           mainStriker.Position = new Vector2() { X = (mainStriker.Position.X), Y = (float)(PosM.Y) };
                           if (mainStriker.Position.Y < gameBoundaries.yStrikerLimits.Item1)
                               mainStriker.Position = new Vector2() { X = mainStriker.Position.X, Y = gameBoundaries.yStrikerLimits.Item1 };
                           else if (mainStriker.Position.Y > gameBoundaries.yStrikerLimits.Item2)
                               mainStriker.Position = new Vector2() { X = mainStriker.Position.X, Y = gameBoundaries.yStrikerLimits.Item2 };

                       }
                   }

               }

       }

mainStriker’ here is the farseer object and its Position property allows us to manipulate its position. The MouseMove event is subscribed for the mainStriker and we get its position relative to the canvas when MouseMove event is raised. We set the X-coordinates of the mainStriker position to the X-coordinate of the mouse which gives us a clean horizontal motion. Limits have also been set so that the striker may not go out of bounds. 

Note: If player has to move the Striker vertically we’ll use the Y-coordinates.

3. The Glow Effect  

This is one of the most interesting things I’ve done and I loved the result. I needed to create a glowing star and the early build of Blend 5 did not have blur effect or the RadialGradientBrush. This might be easier to reproduce now but back when I was building the Windows Store app this was a problem. What I did was that I took a lot of rectangles and rotated the about a single bottom centre axis till they intersected such that the rectangle and the following rectangle had their top right and top left corners coinciding. Then, I applied the LinearGradientBrush to get this wonderful star (seen when new game is launched):

Image 8 

Code:

<Viewbox>
     <Canvas x:Name="canvas" Width="200" Height="200">
         <Canvas.Projection>
             <PlaneProjection/>
         </Canvas.Projection>
         <Rectangle x:Name="rectangle" Height="100" Canvas.Left="80" Stroke="#FFFDFBFB" StrokeThickness="0" Width="40">
             <Rectangle.Fill>
                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                     <GradientStop Color="Transparent"/>
                     <GradientStop Color="#FFFBE509" Offset="0.388"/>
                 </LinearGradientBrush>
             </Rectangle.Fill>
         </Rectangle>
         <Rectangle x:Name="rectangle1" Height="100" Canvas.Left="80" Stroke="#FFFDFBFB" StrokeThickness="0" Width="40" RenderTransformOrigin="0.5,1">
             <Rectangle.RenderTransform>
                 <CompositeTransform Rotation="23.27"/>
             </Rectangle.RenderTransform>
             <Rectangle.Fill>
                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                     <GradientStop Color="Transparent"/>
                     <GradientStop Color="#FFFBE509" Offset="0.388"/>
                 </LinearGradientBrush>
             </Rectangle.Fill>
         </Rectangle>
         <Rectangle x:Name="rectangle2" Height="100" Canvas.Left="80" Stroke="#FFFDFBFB" StrokeThickness="0" Width="40" RenderTransformOrigin="0.5,1" UseLayoutRounding="False" d:LayoutRounding="Auto">
             <Rectangle.RenderTransform>
                 <CompositeTransform Rotation="90.316"/>
             </Rectangle.RenderTransform>
             <Rectangle.Fill>
                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                     <GradientStop Color="Transparent"/>
                     <GradientStop Color="#FFFBE509" Offset="0.388"/>
                 </LinearGradientBrush>
             </Rectangle.Fill>
         </Rectangle>
         <Rectangle x:Name="rectangle3" Height="100" Canvas.Left="80" Stroke="#FFFDFBFB" StrokeThickness="0" Width="40" RenderTransformOrigin="0.5,1" UseLayoutRounding="False" d:LayoutRounding="Auto">
             <Rectangle.RenderTransform>
                 <CompositeTransform Rotation="180.09"/>
             </Rectangle.RenderTransform>
             <Rectangle.Fill>
                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                     <GradientStop Color="Transparent"/>
                     <GradientStop Color="#FFFBE509" Offset="0.388"/>
                 </LinearGradientBrush>
             </Rectangle.Fill>
         </Rectangle>
         <Rectangle x:Name="rectangle4" Height="100" Canvas.Left="80" Stroke="#FFFDFBFB" StrokeThickness="0" Width="40" RenderTransformOrigin="0.5,1" UseLayoutRounding="False" d:LayoutRounding="Auto">
             <Rectangle.RenderTransform>
                 <CompositeTransform Rotation="269.729"/>
             </Rectangle.RenderTransform>
             <Rectangle.Fill>
                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                     <GradientStop Color="Transparent"/>
                     <GradientStop Color="#FFFBE509" Offset="0.388"/>
                 </LinearGradientBrush>
             </Rectangle.Fill>
         </Rectangle>
         <Rectangle x:Name="rectangle5" Height="100" Canvas.Left="80" Stroke="#FFFDFBFB" StrokeThickness="0" Width="40" RenderTransformOrigin="0.5,1" UseLayoutRounding="False" d:LayoutRounding="Auto">
             <Rectangle.RenderTransform>
                 <CompositeTransform Rotation="293.077"/>
             </Rectangle.RenderTransform>
             <Rectangle.Fill>
                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                     <GradientStop Color="Transparent"/>
                     <GradientStop Color="#FFFBE509" Offset="0.388"/>
                 </LinearGradientBrush>
             </Rectangle.Fill>
         </Rectangle>
         <Rectangle x:Name="rectangle6" Height="100" Canvas.Left="80" Stroke="#FFFDFBFB" StrokeThickness="0" Width="40" RenderTransformOrigin="0.5,1" UseLayoutRounding="False" d:LayoutRounding="Auto">
             <Rectangle.RenderTransform>
                 <CompositeTransform Rotation="316.648"/>
             </Rectangle.RenderTransform>
             <Rectangle.Fill>
                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                     <GradientStop Color="Transparent"/>
                     <GradientStop Color="#FFFBE509" Offset="0.388"/>
                 </LinearGradientBrush>
             </Rectangle.Fill>
         </Rectangle>
         <Rectangle x:Name="rectangle7" Height="100" Canvas.Left="80" Stroke="#FFFDFBFB" StrokeThickness="0" Width="40" RenderTransformOrigin="0.5,1" UseLayoutRounding="False" d:LayoutRounding="Auto">
             <Rectangle.RenderTransform>
                 <CompositeTransform Rotation="338.608"/>
             </Rectangle.RenderTransform>
             <Rectangle.Fill>
                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                     <GradientStop Color="Transparent"/>
                     <GradientStop Color="#FFFBE509" Offset="0.388"/>
                 </LinearGradientBrush>
             </Rectangle.Fill>
         </Rectangle>
         <Rectangle x:Name="rectangle8" Height="100" Canvas.Left="80" Stroke="#FFFDFBFB" StrokeThickness="0" Width="40" RenderTransformOrigin="0.5,1" UseLayoutRounding="False" d:LayoutRounding="Auto">
             <Rectangle.RenderTransform>
                 <CompositeTransform Rotation="46.644"/>
             </Rectangle.RenderTransform>
             <Rectangle.Fill>
                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                     <GradientStop Color="Transparent"/>
                     <GradientStop Color="#FFFBE509" Offset="0.388"/>
                 </LinearGradientBrush>
             </Rectangle.Fill>
         </Rectangle>
         <Rectangle x:Name="rectangle9" Height="100" Canvas.Left="80" Stroke="#FFFDFBFB" StrokeThickness="0" Width="40" RenderTransformOrigin="0.5,1" UseLayoutRounding="False" d:LayoutRounding="Auto">
             <Rectangle.RenderTransform>
                 <CompositeTransform Rotation="68.242"/>
             </Rectangle.RenderTransform>
             <Rectangle.Fill>
                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                     <GradientStop Color="Transparent"/>
                     <GradientStop Color="#FFFBE509" Offset="0.388"/>
                 </LinearGradientBrush>
             </Rectangle.Fill>
         </Rectangle>
         <Rectangle x:Name="rectangle10" Height="100" Canvas.Left="80" Stroke="#FFFDFBFB" StrokeThickness="0" Width="40" RenderTransformOrigin="0.5,1" UseLayoutRounding="False" d:LayoutRounding="Auto">
             <Rectangle.RenderTransform>
                 <CompositeTransform Rotation="112.477"/>
             </Rectangle.RenderTransform>
             <Rectangle.Fill>
                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                     <GradientStop Color="Transparent"/>
                     <GradientStop Color="#FFFBE509" Offset="0.388"/>
                 </LinearGradientBrush>
             </Rectangle.Fill>
         </Rectangle>
         <Rectangle x:Name="rectangle11" Height="100" Canvas.Left="80" Stroke="#FFFDFBFB" StrokeThickness="0" Width="40" RenderTransformOrigin="0.5,1" UseLayoutRounding="False" d:LayoutRounding="Auto">
             <Rectangle.RenderTransform>
                 <CompositeTransform Rotation="135.318"/>
             </Rectangle.RenderTransform>
             <Rectangle.Fill>
                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                     <GradientStop Color="Transparent"/>
                     <GradientStop Color="#FFFBE509" Offset="0.388"/>
                 </LinearGradientBrush>
             </Rectangle.Fill>
         </Rectangle>
         <Rectangle x:Name="rectangle12" Height="100" Canvas.Left="80" Stroke="#FFFDFBFB" StrokeThickness="0" Width="40" RenderTransformOrigin="0.5,1" UseLayoutRounding="False" d:LayoutRounding="Auto">
             <Rectangle.RenderTransform>
                 <CompositeTransform Rotation="157.589"/>
             </Rectangle.RenderTransform>
             <Rectangle.Fill>
                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                     <GradientStop Color="Transparent"/>
                     <GradientStop Color="#FFFBE509" Offset="0.388"/>
                 </LinearGradientBrush>
             </Rectangle.Fill>
         </Rectangle>
         <Rectangle x:Name="rectangle13" Height="100" Canvas.Left="80" Stroke="#FFFDFBFB" StrokeThickness="0" Width="40" RenderTransformOrigin="0.5,1" UseLayoutRounding="False" d:LayoutRounding="Auto">
             <Rectangle.RenderTransform>
                 <CompositeTransform Rotation="202.784"/>
             </Rectangle.RenderTransform>
             <Rectangle.Fill>
                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                     <GradientStop Color="Transparent"/>
                     <GradientStop Color="#FFFBE509" Offset="0.388"/>
                 </LinearGradientBrush>
             </Rectangle.Fill>
         </Rectangle>
         <Rectangle x:Name="rectangle14" Height="100" Canvas.Left="80" Stroke="#FFFDFBFB" StrokeThickness="0" Width="40" RenderTransformOrigin="0.5,1" UseLayoutRounding="False" d:LayoutRounding="Auto">
             <Rectangle.RenderTransform>
                 <CompositeTransform Rotation="225.475"/>
             </Rectangle.RenderTransform>
             <Rectangle.Fill>
                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                     <GradientStop Color="Transparent"/>
                     <GradientStop Color="#FFFBE509" Offset="0.388"/>
                 </LinearGradientBrush>
             </Rectangle.Fill>
         </Rectangle>
         <Rectangle x:Name="rectangle15" Height="100" Canvas.Left="80" Stroke="#FFFDFBFB" StrokeThickness="0" Width="40" RenderTransformOrigin="0.5,1" UseLayoutRounding="False" d:LayoutRounding="Auto">
             <Rectangle.RenderTransform>
                 <CompositeTransform Rotation="248.166"/>
             </Rectangle.RenderTransform>
             <Rectangle.Fill>
                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                     <GradientStop Color="Transparent"/>
                     <GradientStop Color="#FFFBE509" Offset="0.388"/>
                 </LinearGradientBrush>
             </Rectangle.Fill>
         </Rectangle>
     </Canvas>
 </Viewbox>

Future Plans

  1. Making new themes downloadable.
  2. Multiplayer version.
  3. New game modes.

History

First post!

My Previous Work

I'm student and I also happen to run a startup name Codlash Technologies Private Limited based in India (http://www.codlash.com)  

My Windows 8 apps can be seen at: http://www.codlash.com/Products/List

My Windows phone apps: http://www.windowsphone.com/en-us/search?q=talha%20naqvi

Screenshots of Carrom+ AIO

Image 9

 

Image 10

 

Image 11 

 

Image 12 

 

Image 13 

 

Image 14 


License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)