在本章,我们将开始创建能在屏幕上移动的2D卡通造型,并学习在2D空间中控制它们的行为:我们将:
1、预先准备一个利用了Silverlight 3性能提升优势的图形增强功能的应用程序项目
2、学会在多种多样的艺术素材上进行硬件加速变换
3、2D空间中的图形动画
4、了解位置、分辨率和尺寸
创建一个图形加速viewport
在Silverlight中我们如何利用目前的硬件来加速生成屏幕上的实时图形呢?
WPF应用程序利用了现代GPU(图形处理器,也被称为图形加速卡)提供的强大功能,不幸的是,Silverlight 3没有完全充分地利用GPU。但是它为某些操作提供了使用GPU的可能。在特定情况下,我们能使用Silverlight 3提供的硬件加速能力来提高游戏的性能。
硬件加速
现在,我们将在屏幕上显示出更多的ghost,但是这次要做一些变换处理并使用硬件加速:
1、打开前一章的“SilverlightMonster”项目
2、打开“SilverlightMonsterTestPage.aspx”文件中的ASPX代码。
3、找到下面的代码:
<object data="data:application/x-silverlight-2,"
4.、在这个项目中,前两行代码如下:
<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
<param name="source" value="ClientBin/SilverlightMonster.xap"/>
5、在上面的代码下插入如下代码(打开GPU加速功能):
<param name="EnableGPUAcceleration" value="true" />
使用GPU变换媒体
我们打算对本应用程序做一些调整。这次,我们将显示八个精灵(ghost)。我们将对一些额外的参数进行配置以便允许某些操作进行硬件加速。并且我们将缩放、旋转和移动这些精灵,同时我们将调整它们的透明度:
1、继续SilverlightMonster项目
2、打开Ghost.xaml的XAML代码。移除下面的内容标记,因为我想使用Canvas来代替Grid:
<Grid x:Name="LayoutRoot">
</Grid>
3、更改主Canvas的原名称“Layer_3”为“LayoutRoot”。
4、在该主Canvas中的首行添加如下内容:
<Canvas.CacheMode>
<BitmapCache/>
</Canvas.CacheMode>
5、在主Canvas中的首行添加如下内容:
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="scaleGhost" ScaleX="1.5" ScaleY="1.5"/>
<RotateTransform x:Name="rotateGhost" Angle="0"/>
</TransformGroup>
</Canvas.RenderTransform>
6、开MainPage.xaml的XAML代码中添加七个Ghost用户控件。把它们放到第一个ghost的下面:
<SilverlightMonster:Ghost Canvas.Left="50" Canvas.Top="50" x:Name="ghost2"/>
<SilverlightMonster:Ghost Canvas.Left="100" Canvas.Top="100" x:Name="ghost3"/>
<SilverlightMonster:Ghost Canvas.Left="150" Canvas.Top="150" x:Name="ghost4"/>
<SilverlightMonster:Ghost Canvas.Left="200" Canvas.Top="200" x:Name="ghost5"/>
<SilverlightMonster:Ghost Canvas.Left="250" Canvas.Top="250" x:Name="ghost6"/>
<SilverlightMonster:Ghost Canvas.Left="300" Canvas.Top="300" x:Name="ghost7"/>
<SilverlightMonster:Ghost Canvas.Left="350" Canvas.Top="350" x:Name="ghost8"/>
7、你可在预览窗口中看到如下的预览效果:
缓存呈现内容
我们为ghost的CacheMode属性指定了BitmapCache值,这个属性决定了在可能有GPU时是否对呈现内容进行缓存。它的默认值为null,意思是呈现内容不会被缓存。
设置该值为BitmapCache将通知Silverlight把该元素作为一个位图(bitmap)进行缓存。因此GPU将可在该内容之上进行如下三种操作:
变换(Transform):如缩放、旋转、倾斜等等
混合(Blend):指进行透明度设置
裁剪(Clip):如裁剪一个矩形等
然而,把元素作为位图进行缓存的话,我们将失去矢量图形的一些优点。该元素将在GPU内存中转换为一个光栅图形,这是该矢量图形的一个快照。在这里需要进行权衡(指是否进行缓存)。
在同一时间移动许多图形
1、继续SilverlightMonster项目
2、在解决方案资源管理器中展开MainPage.xaml,然后打开MainPage.xaml.cs文件。
3、在MainPage部分类中添加下面的私有变量:
private List<Ghost> _ghosts;
4、在构造器的“InitializeComponent();”代码下面添加下面的代码:
// Create the new list
_ghosts = new List<Ghost>();
// Add each Ghost instance previously created through XAML code
_ghosts.Add(ghost);
_ghosts.Add(ghost2);
_ghosts.Add(ghost3);
_ghosts.Add(ghost4);
_ghosts.Add(ghost5);
_ghosts.Add(ghost6);
_ghosts.Add(ghost7);
_ghosts.Add(ghost8);
5.、使用下面的代码替换MouseMove事件处理程序中的代码(没使用“foreach (Ghost oneghost in _ghosts)”的原因是出于性能的考虑,for循环更快,而我们必须尽可能快地对列表中所有的Ghost进行更新):
6、.编译并运行本解决方案。默认的Web浏览器将呈现八个跟随鼠标指针进行移动、缩放、旋转和更改它们的透明度的ghost。Web浏览器将使用一些GPU硬件加速能力,但是这些ghost将变得稍微有些模糊,就像下面的快照一样:
检测GPU加速
假如你(对GPU加速)有任何疑虑的时候,则可能改变一个参数并检测Silverlight是否正为某些UI元素进行位图缓存。
1、继续SilverlightMonster项目
2、在解决方案资源管理器中打开SilverlightMonsterTestPage.aspx的ASPX代码
3、移到下面的代码下面:
<object data="data:application/x-silverlight-2,"
4、在“<param name="source" value="ClientBin/SilverlightMonster.xap"/>”代码的下面插入如下代码:
<param name="EnableCacheVisualization" value="true" />
5、打开Ghost.xaml的XAML代码
6、像下面一样注释掉代码中的对应内容
<!-- Disable cache mode
<Canvas.CacheMode>
<BitmapCache/>
</Canvas.CacheMode>
-->
7.、译并运行本解决方案,效果图如下:
理解GPU加速的局限性
在Silverlight 3中的GUP加速是基于原始图形(矢量或者光栅图像)的快照进行的。为此,当我们需要使用GPU加速来缩放矢量图形时,它将会生成像素图像。这和DirectX和OpenGL中的加速不一样,它不是完全意义上的GPU加速。
因此,它的使用取决于我们制作的游戏的类型和运行Silverlight应用程序的电脑自身的处理能力。
我们必须对性能的改进进行权衡,对视觉效果进行测试后才能决断。
创建封装良好的面向对象的卡通造型
Silverlight 3提供了优越的动画功能。但是,游戏需要有比单独的动画更进一步的精确性和协调能力。我们需要对拥有各式各样的行为不同的卡通造型进行控制。做到这一点的最好的办法是把面向对象的封装良好的卡通人物与Silverlight的显示输出能力结合在一起。一个良好的面向对象的设计能创造出一个简单却又强大的游戏框架。
这不是简简单单几步就能完成的任务。因此我们将从一些简单的动画开始进行封装,并在游戏中抽象概括它们的行为,然后创建类。
创建容纳图像的用户控件类
首先我们将把基于光栅的外星人图片转换成用户控件,然后我们将控制它们的移动行为:
1、创建一个新的基于C#的Silverlight应用程序项目。项目的名称为“SilverlightInvaders2D”。
2、新建一个名称为images的文件夹。
3、把“\Invaders\GAME_PNGS_RESIZED”文件夹中的所有PNG图片拷贝到该images文件夹中。
4、根据下面表格的内容为每一个图片新建对应的用户控件:
图片文件名 | 用户控件名 |
ALIEN_01_01.PNG | BlueAlien |
ALIEN_02_01.PNG | RedAlien |
ALIEN_03_01.PNG | GreenAlien |
SHIP_01_01.PNG | Ship |
TENT_01_01.PNG | Tent |
5、打开每个用户控件的XAML代码,移除下面的代码:
Width="400" Height="300"
6、把Grid的定义替换为Canvas,然后添加相应的Image元素,如下面的代码所示:
<Canvas x:Name="LayoutRoot" Background="Transparent">
<Image Source="images/ALIEN_01_01.png" x:Name="imgSprite01" />
</Canvas>
7、创建好每一个用户控件后,可以在IDE中进行预览,如下图所示:
显示精灵图片
1、继续SilverlightInvaders2D项目
2.、打开MainPage.xaml的XAML代码,然后用下面的代码替换所有现有代码。
<UserControl x:Class="SilverlightInvaders2D.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="1366" d:DesignHeight="768"
xmlns:SilverlightInvaders2D="clr-namespace:SilverlightInvaders2D">
<Canvas x:Name="LayoutRoot" Background="White">
<!-- A button to start moving the sprites -->
<Button x:Name="btnStartGame" Content="Start the game!"
Canvas.Left="200" Canvas.Top="20"
Width="200" Height="30" Click="btnStartGame_Click">
</Button>
<!-- Code to show the User Controls (the sprites) -->
<SilverlightInvaders2D:GreenAlien x:Name="ucGreenAlien1" Canvas.Left="50" Canvas.Top="10"/>
<SilverlightInvaders2D:BlueAlien x:Name="ucBlueAlien1" Canvas.Left="50" Canvas.Top="70"/>
<SilverlightInvaders2D:BlueAlien x:Name="ucBlueAlien2" Canvas.Left="50" Canvas.Top="130"/>
<SilverlightInvaders2D:RedAlien x:Name="ucRedAlien1" Canvas.Left="50" Canvas.Top="190"/>
<SilverlightInvaders2D:RedAlien x:Name="ucRedAlien2" Canvas.Left="50" Canvas.Top="250"/>
</Canvas>
</UserControl>
3、打开MainPage.xaml.cs文件,在类中添加下面的私有成员:
// Holds the green alien's location in the Canvas
private Point _GreenAlien1Location = new Point(0, 0);
// Holds the speed for X and Y movement
private Point _GreenAlien1Speed = new Point(250, 0);
// The upper left corner for the animation
private Point _UpperLeftCorner = new Point(0, 0);
// The bottom right corner for the animation
private Point _BottomRightCorner = new Point(0, 0);
// The aliens' row height
private double _RowHeight = 60;
// Holds the time when the method finished rendering a frame
private DateTime _LastTick;
4、添加下面的在呈现每一帧时更新精灵位置的事件处理程序代码:
5、最后,添加下面的点击按钮时的事件处理程序代码:
6、编译并运行本解决方案,运行效果图如下:
在屏幕上显示精灵
在Silverlight 3中,我们必须对一些重要问题进行精确地控制。其中最重要的是时间管理。
默认情况下,Silverlight 3每秒输出60帧图像。这意味着代码必须尽可能快地执行每一个帧的输出以避免漏掉一些帧的显示任务以便生成一个平滑流畅的动画效果。
对于创建其它使用矢量图形的程序来说,其实现步骤和我们前面的例子的步骤几乎完全相同。你可以使用基于矢量图形的动画精灵来替换前面的光栅图像。
在你完成了许多精灵的创建任务后你会觉得代码不容易维护。此时需要对代码进行重构,这就要使用一个定义了绝大部分公共动画行为的新类来对精灵进行包装。
总结
我们在本章了解了许多关于GPU硬件加速、2D矢量、分辨率、精灵和动画方面的知识。具体来说,我们进行了一下配置以便利用Silverlight 3D的GPU加速能力;我们使用了数码艺术素材来创建了一些精灵并以动画的方式进行显示;我们理解了在Silverlight 3中硬件加速的优点和缺点。
现在我们将把多个精灵和漂亮的背景组合在一起,这将是下一章的话题。
代码下载:本章源代码