Carrom+ AIO






4.91/5 (11投票s)
为 AIO 优化过的最好、最非传统的虚拟卡罗姆应用程序!
晋级第二轮!
进度
09/19/2013:
- 已完成在线排行榜的实现。
- 创建了一个新的超宽尺寸面板“Wide+”。
- 推出了全新的“木纹”主题!
不断打磨用户界面!
新截图

原始条目
介绍
Carrom+ AIO 是 Windows 桌面版最具创新性和独特性的一款虚拟卡罗姆应用程序。Carrom+ AIO 已针对联想Horizon AIO和 Windows 8 进行了优化。在联想Horizon AIO的桌面和触摸屏/平板模式下玩起来都很有趣。Carrom+ 利用了联想Horizon AIO的独特功能和 Windows 8 的设计原则,打造了一款在设计和性能上都真正独一无二的产品。该游戏为用户提供了令人难以置信的视觉反馈,让用户长时间保持参与和娱乐。Carrom+ 有自己的一套规则,使其比传统的卡罗姆游戏更有趣、更吸引人。 最多可供四名玩家同时游玩。
Carrom+ AIO 是一款游戏,是 Carrom+ 桌面移植的Windows 应用商店应用,由我开发,并使用C# 和 XAML、Silverlight 5 制作。此应用程序针对联想Horizon AIO设备。它将被打包、发布并用作桌面应用程序。
注意:我假设大多数读者都熟悉卡罗姆游戏,因为它是许多家庭都有的经典游戏之一。因此,我不想浪费大家的时间来详细解释游戏的规则和概念。但是,如果您不熟悉,我建议您阅读以下文章:http://en.wikipedia.org/wiki/Carrom
文章中使用的一些术语
卡罗姆棋子 :
放置在棋盘上的棋子。每位玩家的目标是尽可能多地得分。
击球器:
用于击打卡罗姆棋子,使其落入角落里的洞中。这有助于玩家得分。
目标
Carrom+ AIO 试图实现以下四个目标
- 联想 Horizon AIO 既可以作为平板/桌面电脑使用(当它平放时),也可以作为台式机使用(当它支在支架上时)。平放模式非常适合 Carrom+ AIO,因为传统的卡罗姆游戏一直都是这样玩的。但是,如果设备被用作台式机,应用程序就必须提供可比的用户体验。因此,第一个目标是在 AIO 的两种模式下提供同样令人兴奋和有趣的游戏体验。
- 联想 Horizon 是一款巨大的 27 英寸台式电脑,是宽屏的。我们知道传统的卡罗姆游戏是方形的。因此,Carrom+ AIO 的第二个目标是利用这种宽高比,使其对用户来说非常棒。显然,浪费所有可用空间不是一个明智的选择。
- 目前用来发射击球器以击打卡罗姆棋子的机制是由一个箭头控制的,该箭头可以用来设置投篮方向和力度。我一直在研究联想 Horizon 的“击球器”配件,我认为它将是箭头的绝佳替代方案。我还没有找到一种方法可以在捆绑的应用程序之外使用它。这是第三个目标。
- 第四个目标是改进经典的卡罗姆游戏本身,使其能够利用设备和软件的特性,即引入利用目标设备的数字处理能力的新模式。
为什么选择卡罗姆?
根据我对联想 Horizon 的研究,我得出结论,这款设备可以彻底改变我们玩数字棋盘游戏的方式。联想网站上的所有视频都指向同一个方向。空气曲棍球、大富翁等都非常棒。Carrom+ AIO 将是这些游戏中一个很好的补充,因为它遵循相同的理念,是一款通过软件大大增强的经典游戏,并且可以利用设备的触摸功能/尺寸,还能产生现代音效。当我为 Windows 应用商店开发 Carrom+ 时,我总是设想最佳用例是在像 Horizon 这样的设备上,所以当我听说这个比赛和联想 Horizon 时,我的选择对我来说是绝对清晰的!
特点
在这里,我将解释 Carrom+ AIO 如何尝试实现上述所有目标。
1. 管理屏幕方向
由于联想 Horizon AIO 可以用作平板电脑和台式机,因此 Carrom+ AIO 会出现以下两种使用场景(两人游戏):
首先,考虑设备平放,两位玩家面对面坐着,分别坐在设备的任一侧。这种布局类似于真实世界中卡罗姆游戏的玩法。Carrom+ AIO 具有一个平板电脑模式,可以处理这种方向。
其次,考虑设备被用作台式机,并且两名用户位于屏幕前方。在这种情况下,应用程序会旋转整个屏幕,以便第二名玩家也能进行正面射击。行为已在视频和截图中演示。

因此,Carrom+ AIO 考虑了两种屏幕方向,并且在这两种模式下都同样有趣。
视频:
2. 屏幕尺寸/宽高比
联想 Horizon 是一款巨大的 27 英寸台式电脑,是宽屏的,但传统的卡罗姆游戏是方形的。为了实现最大化屏幕尺寸利用率的目标,Carrom+ AIO 具有宽屏模式,可以充分利用设备的全部尺寸。宽屏模式为用户带来了全新的卡罗姆游戏体验,更加有趣。这样,卡罗姆游戏就可以成为一种非常愉快的体验,因为设备的尺寸几乎相当于真实卡罗姆游戏的尺寸,并且带有所有软件增强功能。


视频:
对于可能在平板电脑和其他设备上使用此应用程序的用户,我们提供了可选的方形和4:3 宽高比。用户如果愿意,也可以退出全屏。因此,就屏幕尺寸/分辨率而言,Carrom+ AIO 完全涵盖了其所有需求。
3. 击球器发射机制
目前用来发射击球器以击打卡罗姆棋子的机制是由一个箭头控制的,该箭头可以旋转以设置方向,也可以平移以设置投篮力度。它同样适用于触摸和鼠标。
视频:
我一直在研究联想 Horizon 的“击球器”配件,我认为它将是箭头的绝佳替代方案。我还没有找到一种方法可以在捆绑的应用程序之外使用它。据我所知,我认为它不提供给第三方开发者。
如果有人能分享更多关于这个主题的信息,请告诉我!
4. 软件增强
Carrom+ AIO 是软件驱动的,因此,与真实的卡罗姆游戏相比,可以实现更多功能。Carrom+ AIO 包括现代音效和视觉反馈,为用户带来全新的卡罗姆体验。颜色和主题都面向现代 UI。
Carrom+ AIO 还包括许多非常规的游戏模式,例如以下两种:
- 限时赛:玩家必须在三分钟内将所有卡罗姆棋子全部击入。
- 对战:在这个两人游戏模式中,卡罗姆棋子会以10 种特定的运动模式移动,两名玩家轮流击打所有 10 颗卡罗姆棋子。得分最高的玩家获胜!移动卡罗姆棋子非常规,这种模式极其有趣。
- 速度赛:玩家必须尽快完成比赛,然后将得分上传到在线排行榜。
视频
进展
正如您在视频中看到的,移植已基本完成(在听说 AppInnovationContest’13 后立即开始),我剩下的工作是优化触摸功能,但我认为这会效果很好,因为在 Silverlight 5 中,触摸事件会自动提升为点击事件。不幸的是,我现在没有触摸设备可以验证这一点。
我也很想将“击球器”配件集成到 Carrom+ AIO 中。一旦我拿到设备,这将是我的首要目标!
使用代码
Carrom+ AIO 包含大量代码,其中有些我想保密,但也有很多内容可以分享!让我们看看 Crrom+ AIO 中一些有趣的代码片段:
1. 物理
对于所有碰撞物理,我使用了Farseer 物理引擎。我正在使用的是 Farseer 封装器Physics Helper XAML(可在 CodePlex 上找到)。它非常简洁,适用于 Silverlight、WP7 和 WinRT。
将 Physics Helper XAML 添加到您的项目中
- 从http://physicshelperxaml.codeplex.com/获取
- 右键单击解决方案并选择“添加现有项目”。 在 Physics Helper XAML ZIP 下载中,浏览到 \PhysicsHelperXaml\Farseer Physics Engine SL\Farseer Physics Engine SL.csproj。
- 右键单击解决方案并选择“添加现有项目”。 在 Physics Helper XAML ZIP 下载中,浏览到 \PhysicsHelperXaml\Spritehand.PhysicsHelper.Metro\Spritehand.PhysicsHelper.SL.csproj。
- 从您的主项目中,添加对 Farseer Physics Engine SL 和 Spritehand.PhysicsHelper.SL 项目的引用。
创建 Farseer 对象:
- 在 XAML 页面的顶部添加此命名空间。
xmlns:FarseerHelper="using:Spritehand.FarseerHelper"
- 现在创建一个物理画布,所有启用物理的精灵都将是该画布的子元素。MousePickEnabled 允许用户通过触摸/鼠标操作对象。
<FarseerHelper:PhysicsCanvas Height="768" Width="1366" MousePickEnabled="True">
</ FarseerHelper:PhysicsCanvas >
- 现在您可以按以下方式在画布中创建精灵:
<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 >
- 画布还有各种其他属性,如 GravityHorizontal 和 GravityVertical,可以帮助您做一些令人惊叹的事情。将精灵设置为 Static 使其不可移动,应使用此技术来制作世界边界。
2. 移动击球器
这是很多人问我的问题,所以我想在我的文章中包含它。以下代码将击球器水平移动到用户想要的位置,并在限制范围内:
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’是 farseer 对象,它的 Position 属性允许我们操作它的位置。我们为 mainStriker 订阅了 MouseMove 事件,并在 MouseMove 事件触发时获取其相对于画布的位置。我们将 mainStriker 位置的X 坐标设置为鼠标的X 坐标,从而实现平滑的水平移动。还设置了限制,以免击球器超出边界。
注意:如果玩家需要垂直移动击球器,我们将使用 Y 坐标。
3. 发光效果
这是我做过的最有趣的事情之一,我非常喜欢结果。我需要创建一个发光星,Blend 5 的早期版本没有模糊效果或径向渐变画笔。现在可能更容易重现,但在我构建Windows 应用商店应用时,这是一个问题。我所做的是,我取了许多矩形,并将它们围绕一个单一的底部中心轴旋转,直到它们相交,使得一个矩形和下一个矩形具有重合的右上角和左上角。然后,我应用了线性渐变画笔来获得这个美妙的星形(在新游戏启动时可见)。
代码
<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>
未来计划
- 使新主题可下载。
- 多人游戏版本。
- 新游戏模式。
历史
第一篇文章!
我之前的作品
我是一名学生,我也经营着一家名为 Codlash Technologies Private Limited 的初创公司,该公司位于印度(http://www.codlash.com)
我的 Windows 8 应用可以在以下网址找到:http://www.codlash.com/Products/List
我的 Windows Phone 应用:http://www.windowsphone.com/en-us/search?q=talha%20naqvi
Carrom+ AIO 截图

