C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三十六)地图自定义切片与导出
做为提升游戏性能的一个重要环节就是地图的优化,作为基于Web的游戏来说,可以通过将地图切成若干同尺寸的片后,根据主角的位置进行时时的按需加载。举个简单例子,好比一幅20000*20000的地图,我们将之以400*400像素为一个地图片单位切成2500片,假若游戏窗口尺寸为800*600,那么我们每次只需加载以主角为中心的周围9块地图片(1200*900像素)即可实现填充,这比起一次性加载诺大一张地图,且在移动时对其不停的切割,性能优越得太多太多。
那么本节我将向大家讲解如何为地图编辑器添加自定义切片与导出功能,从而为下一节的游戏地图性能优化做准备。
大家是否还记得在第三十四节中,我将地图显示部份分为了三层:障碍物层,障碍物单元格边线层及地图切片边线层。我曾在文中提过利用Grid的ShowGridLines来为单元格设置边框操作,尽管使用简单但性能却极其低下。这里我们可以通过在障碍物单元格边线层中绘制Line的方式来优化它:
/// <summary>
/// 绘制网格边线
/// </summary>
private void SetGridLines(Canvas carrier, double totalWidth, double totalHeight, double singleWidth, double singleHeight,Color lineColor, double dashWidth, double dashSpace) {
carrier.Children.Clear();
int sectionXNum = (int)(totalWidth / singleWidth);
int sectionYNum = (int)(totalHeight / singleHeight);
for (int x = 1; x <= sectionXNum; x++) {
Line line = new Line() {
X1 = x * singleWidth,
X2 = x * singleWidth,
Y1 = 0,
Y2 = totalHeight,
Stroke = new SolidColorBrush(lineColor),
StrokeDashArray = new DoubleCollection() { dashWidth, dashSpace },
};
carrier.Children.Add(line);
}
for (int y = 1; y <= sectionYNum; y++) {
Line line = new Line() {
X1 = 0,
X2 = totalWidth,
Y1 = y * singleHeight,
Y2 = y * singleHeight,
Stroke = new SolidColorBrush(lineColor),
StrokeDashArray = new DoubleCollection() { dashWidth, dashSpace },
};
carrier.Children.Add(line);
}
}
其中Line的Stroke参数用于设置线条颜色(注意Fill属性对它来说没有任何效果),而StrokeDashArray参数则用来定义线条虚线部分的宽与间距。既然地图切的是矩形片,那么我们同样可以将此方法用于地图切片分割的功能上。为了在地图编辑器上增加可以自定义切片尺寸的选择器,考虑到Slider不太合适,这里我选择使用WinForm控件库中的NumericUpDown,即灵活又强大。在WPF中的添加WinForm控件需要几个步骤:
1) 添加dll引用:需要引用WindowsFormsIntegrationl和System.Windows.Forms。
2) 如果是在xaml中使用,则需要添加类似如下的定义:
xmlns:WinFormHost = "clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
xmlns:WinForm = "clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
3)使用的时候通过一个WinFormHost包含一个WinForm控件的形式,以添加两个NumericUpDown为例,我们可以这样写:
<WinFormHost:WindowsFormsHost Canvas.Left="820" Canvas.Top="170" Height="20" Width="101">
<WinForm:NumericUpDown x:Name="SectionWidth" Maximum="500" Minimum="100" Increment="10" Value="300" ValueChanged="SectionSize_ValueChanged" />
</WinFormHost:WindowsFormsHost>
<WinFormHost:WindowsFormsHost Canvas.Left="820" Canvas.Top="196" Height="20" Width="101">
<WinForm:NumericUpDown x:Name="SectionHeight" Maximum="500" Minimum="100" Increment="10" Value="300" ValueChanged="SectionSize_ValueChanged" />
</WinFormHost:WindowsFormsHost>
在VS设计器中我们仅能看到它们以WinFormHost的描述形式显示,只有在运行时才能看到它们真正的实体内容:
那么接下来同样的我们通过绘制Line的方式来绘制切片边线,具体方法与绘制障碍物边线非常相似,额外再通过一些简单的事件即可实现自定义地图切片设计:
上图中灰色虚线为10*10像素单位的障碍物单元格边线,棕红色虚线为250*286像素单位的切片边线,通过NumericUpDown可以非常方便的定义最大值、最小值、有效值及跨越值,并且通过它的ValueChanged事件实现轻松的所见即所得。
设定完切片边线的显示后,最后将根据此切片预览实现真正的地图片导出功能。这里我们可以使FolderBrowserDialog文件夹选择对话框,并配合如下方法来实现此功能:
int sectionXNum = (int)(mapWidth / (double)SectionWidth.Value);
int sectionYNum = (int)(mapHeight / (double)SectionHeight.Value);
for (int x = 0; x < sectionXNum; x++) {
for (int y = 0; y < sectionYNum; y++) {
CroppedBitmap croppedBitmap =
new CroppedBitmap(mapSource, new Int32Rect(x * (int)SectionWidth.Value, y * (int)SectionHeight.Value, (int)SectionWidth.Value, (int)SectionHeight.Value));
System.IO.FileStream fileStream =
new System.IO.FileStream(string.Format(@"{0}/{1}_{2}.jpg", outputSection.SelectedPath, x, y), System.IO.FileMode.Create);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(croppedBitmap));
encoder.Save(fileStream);
fileStream.Close();
}
}
通过前面章节的学习,大家对CroppedBitmap再熟悉不过了,它应该算是WPF中最受宠爱的对象之一。此方法根据地图切片数量(sectionXNum,sectionYNum),利用FileStream和JpegBitmapEncoder循环导出jpg图片到FolderBrowserDialog指定的文件夹(SelectedPath)中。最后得到的是类似如下若干被自动切割得整整齐齐的地图切片:
最后还有两个问题需要说明:
1)设定的切割尺寸最好能完整切割完整张地图,例如地图尺寸为2800*3200,那么您可以将切片的单位尺寸设定为350*400,这样切割下来刚好64张;如果您将切片尺寸设定为320*400,则地图最右边缘的一部分将不被切割出来。因此地图尺寸与切割尺寸需要默契配合,这理当属于美工的范畴,作到应该不难。
2)为了降低地图编辑器的设计难度,我并未在Stream中对图片数据流进行压缩处理,这将使得导出的jpg图片由于质量过高而引起容量很大,基于Web的游戏对文件大小非常敏感。因此,我建议您可以自行添加数据流压缩方法,或者通过Photoshop的压缩批处理来完成这些操作。下面是以上地图片压缩前后的容量对比:
嘿嘿~这一节非常有意思吧。那么多的图片眼睛都快花了,该怎样使用呢?下一节我将向大家讲解如何在本教程示例游戏中使用它们来实现地图的区域性按需加载,关注哦。
(地图编辑器已更新,请大家到目录中下载)