Silverlight:手把手教你写俄罗斯方块(一)
写在前面:由于之前曾写过Winform版的俄罗斯方块程序,该版本采用的是GDI+绘图技术,原理就是通过计时器,根据逻辑判断不断重新绘制画板,使得游戏以动画的方式在不断进行。在学习了silverlight之后,发现利用同样的原理,很容易将Winform版移植过来,于是就有了该教程。该教程适用于对于C#有一定基础,并且有兴趣学习silverlight的新手朋友们。在这一教程中,我会通过大量代码和示例图来演示,相信只要您跟着教程一步一步去做,就一定能创造出俄罗斯方块这个经典的游戏。本程序部分逻辑及思路参考自网络,如果有对您的权益造成侵害请及时与我联系,我会加以注明或者立即删除该文章。
最终效果图如图所示:
您产生兴趣了吗?如果有的话就随我展开一场silverlight之旅吧。
一.首先打开vs2010,选择“文件”、“新建”、“项目”,建立一个silverlight4程序,在弹出的对话框中,请勾选上“在新网站中承载silverlight程序”,
因为本程序设置了英雄榜的功能,后期需要与服务器通信,获得所有玩家的得分及游戏结束后的提交分数,如果您只需要做一个完全运行在客户端的东西,不需要与服务器通信,可以不选中这项。
二.游戏需要一个画板,我们打开MainPage.xaml,在Grid里面添加一个Canvas作为画板的容器,之所以使用Canvas,是因为Canvas的SetTop和SetLeft方法很容易为其内部的元素进行定位,然后设置它的背景色为黑色,设置宽度为200、设置高度为400,最重要的是设置它的“x:Name”属性,我们设置为“playBoard”。为了美观,我们再在这个Canvas外面添加一个边框Border。为了方便画板Canvas的布局,可以在Grid的下面再添加一个Canvas作为整个页面的父容器,并且为这个容器设置一个渐变背景色,关于渐变背景色,通过Microsoft Blend4可以很轻松的完成,如果您电脑上没有装blend4也没关系,输入少量xaml亦可实现同样效果,由于本系列文章主要讲解俄罗斯方块的程序设计,所以对xaml代码不做过多解释,具体xaml代码如下:
<Grid x:Name="LayoutRoot"> <Canvas> <Canvas.Background> <LinearGradientBrush EndPoint="0.885999977588654,0.851999998092651" StartPoint="0.034000001847744,0.0320000015199184"> <GradientStop Color="Silver" Offset="0"/> <GradientStop Color="#FFEBE3E3" Offset="1"/> </LinearGradientBrush> </Canvas.Background> <Border Width="220" Height="420" Margin="140,70,0,0" Background="#FFFAF0F0" BorderBrush="Black" BorderThickness="2" CornerRadius="10"> <Canvas x:Name="playBoard" Height="400" Width="200" Background="Black" Canvas.Left="65" Canvas.Top="42" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> </Canvas> </Grid>
Ctrl+F5运行结果,游戏画板已经产生了!
三.接下来,右键单击项目,添加一个Silverlight用户控件,我们命名为Rect.xaml。这个控件用来干什么呢?前面说过我们的动画原理是把画板分隔成一个又一个的小矩形,然后通过改变这些矩形的颜色就来实现动画的效果,而这些矩形就相当于是画板的最小单位。例如,当方块在画板上移动了,我们就找到这个方块在画板上移动前的坐标,而这个坐标就是某个小矩形的位置,把这个矩形的颜色改成背景色黑色,然后计算出方块移动后的新坐标,把新坐标位置上的矩形设成这个方块的颜色,看上去就好像方块移动了一样。所以我们建立这样的控件用来填满整个画板。废话不多说,创建好这个控件之后,双击打开Rect.xaml,用Canvas覆盖掉自动生成的Grid代码,然后在Canvas里面添加一个Rectangle,命名为“rect”,为了美观,我们再为这个rect添加一些效果,使它的直角变得圆滑,以及渐变背景色。我们设置它的高度和宽度都为20,这样将来就可以在200*400的画板上,填充10*20个Rect控件,具体xaml代码如下:
<Canvas> <Rectangle x:Name="rect" Stroke="White" Width="20" RadiusX="2" RadiusY="2" Height="20"> <Rectangle.Fill> <RadialGradientBrush RadiusX="1" RadiusY="1" GradientOrigin="0,1"> <RadialGradientBrush.RelativeTransform> <TransformGroup> <RotateTransform Angle="90" CenterX="0.5" CenterY="0.5"/> </TransformGroup> </RadialGradientBrush.RelativeTransform> <GradientStop x:Name="fillColor" Color="#FF2266FF" Offset="1"/> <GradientStop Color="White"/> </RadialGradientBrush> </Rectangle.Fill> </Rectangle> </Canvas>
注意,因为我们需要操作这个控件的背景色,所以为这个渐变色设置了“x:Name”属性为“fillColor”。
四.双击打开Rect.xaml.cs为这个控件添加3个自定义属性,Left,Top,Color。Left和Top是用来定义Rect在画板上的位置,假设画板被我们分隔成了10*20个小矩形,我们设置左上角第一个矩形的坐标为(0,0),向右为X轴递增,向下为Y轴递增,那么这10*20个所有矩形的坐标都可以被定义。对属性稍微做下封装,我们就可以把具体到像素的Canvas.LeftProperty和Canvas.TopProperty转变成在画板中的坐标了。这样一来我们用只关心Rect的坐标,而不用关心Rect在画板中的具体位置了。
public double Left { get { double v = Convert.ToDouble(this.rect.GetValue(Canvas.LeftProperty)); return v / 20; } set { this.rect.SetValue(Canvas.LeftProperty, value * 20); } } public double Top { get { double v = Convert.ToDouble(this.rect.GetValue(Canvas.TopProperty)); return v / 20; } set { this.rect.SetValue(Canvas.TopProperty, value * 20); } }
颜色属性设置为可空类型,这样我们在传一个空值的时候可以直接将这个Rect隐藏掉,而如果这个Rect的Color属性返回空的时候,我们也可以判断出这个地方不存在有方块。
public Color? Color { get { if (this.rect.Visibility == Visibility.Collapsed) return null; else return this.fillColor.Color; } set { if (value == null) this.Visibility = Visibility.Collapsed; else { this.Visibility = Visibility.Visible; this.fillColor.Color = (Color)value; } } }
最后是构造函数,我们要把这个Rect定位到画板中的某个位置时,只用传坐标就好。
public Rect(int x, int y) { // 为初始化变量所必需 InitializeComponent(); this.Left = x; this.Top = y; this.Color = null; }