上一节中曾有提到,检测系统架构是否合理的评判标准之一就是系统的拓展性。在.NET网站应用中,一个优秀的架构可以在不同数据库之间相互转换,可以与不同的银行接口轻松对接,可以随意集成各种插件,而实现这些仅仅需要对局部进行小小手术而已;同样的,在游戏设计中,窗口化与全屏化的自适应完美切换同样是对游戏架构合理性的严肃考验,Are you ready?
游戏窗口化与全屏化之间的切换方式有两种,第一种为仅对可视范围面积进行扩大与缩小而不缩放所有对象物体的尺寸。此方式在本游戏设计中实现起来非常简单,我们首先添加一个按钮作为测试按钮,然后为其添加ChangeWindowMode事件,接着定义四个变量分别记录全屏时的尺寸与窗口化时的尺寸: double ScreenWidth, ScreenHeight, WindowWidth, WindowHeight,并来在游戏初始化对它们进行赋值:
private void InitializeGameSetting() {
//设置尺寸
ScreenWidth = SystemParameters.PrimaryScreenWidth;
ScreenHeight = SystemParameters.PrimaryScreenHeight;
WindowWidth = 800;
WindowHeight = ScreenHeight * WindowWidth / ScreenWidth; //根据屏幕分辨率计算出窗口模式下高度
……
}
前三个属性都很好理解,而第四个WindowHeight为什么非要用公式来计算出值而不是直接取600来得干脆?这涉及到窗口自适应用户电脑分辨率的问题。如果您的电脑是4:3类型的分辨率,如800*600、1024*768、1280*960等这样的传统分辨率,你大可以直接设置WindowWidth=800、WindowHeight=600;但是用户的电脑如果是宽屏的(如16:9等),此时设置窗口模式尺寸为800*600将导致全屏化切换错误。一般网络游戏中会给予几个或多个可选分辨率让用户自行设置窗口化/全屏化切换,这些切换的实现基于对系统分辨率及刷新率进行更改的基础上;而WPF中实现起来简单多了,不需要再去调用WindowsAPI更改系统设置而是直接通过修改窗体自身属性WindowStyle与WindowState轻松实现,具体方法如下:
private void ChangeWindowMode(object sender, RoutedEventArgs e) {
Button button = e.Source as Button;
string mode = button.Content.ToString();
if (mode == "全屏") {
this.WindowStyle = WindowStyle.None;
this.WindowState = WindowState.Maximized;
button.Content = "窗口";
} else if (mode == "窗口") {
this.WindowStyle = WindowStyle.SingleBorderWindow;
this.WindowState = WindowState.Normal;
button.Content = "全屏";
}
}
需要切换全屏时,我们只需要将窗口的标题栏与边框去掉(WindowStyle.None),并且设置窗口模式为最大化(WindowState.Maximized)即可;而如果需要将游戏窗口化则只需将窗口模式设置为单边框窗口(WindowStyle.SingleBorderWindow)并还原窗口(WindowState.Normal)即可。通过此方法实现的窗口化与全屏化的效果图如下:
第二种方式我称之为按比例缩放模式,顾名思义,使用此模式进行切换时,所有的对象包括人物,地图,障碍物等东西均进行等比例的放大/缩小。实现此方法,首要任务是进行需求分析:在切换时,什么属性在发生变化了?当然是游戏中的一切内容它们自身及子内容的尺寸在改变。这里要特别说明的是,在前面的章节中,障碍物均为正方形,只用一个GridSize来定义它的边长;但是要是用户电脑是宽屏的,那么缩放时必须以使用矩形作为基础单元格,因此将GridSize拆分成GridSizeX与GridSizeY两个变量分别代表单元格的宽与高,惟有这样才能胜任按比例缩放窗口的工作。那么根据此原理,我进行如下编写实现该模式下的窗口模式切换方法:
double ratioX, ratioY; //定义X,Y方向上的缩放比例
/// <summary>
/// 改变游戏窗口尺寸(缩放窗口)
/// </summary>
/// <param name="currentWidth">当前窗口宽</param>
/// <param name="currentHeight">当前窗口高</param>
/// <param name="newWidth">新窗口宽</param>
/// <param name="newHeight">新窗口高</param>
private void ChangeWindowSize(double currentWidth, double currentHeight, double newWidth, double newHeight) {
//主角的Storyboard停止(这很重要)
Leader.Action = Actions.Stop;
Leader.Direction = 4;
if (storyboard != null) { storyboard.Stop(); }
//计算缩放比例
ratioX = newWidth / currentWidth;
ratioY = newHeight / currentHeight;
//缩放障碍物
GridSizeX *= ratioX;
GridSizeY *= ratioY;
Carrier.Width = ActualWidth * ratioX;
Carrier.Height = ActualHeight * ratioY;
//所有精灵、地图等对象进行象素缩放
for (int i = 0; i < Carrier.Children.Count; i++) {
if (Carrier.Children[i] is QXObject) {
QXObject Obj = Carrier.Children[i] as QXObject;
Obj.X *= ratioX;
Obj.Y *= ratioY;
Obj.Width_ *= ratioX;
Obj.Height_ *= ratioY;
Obj.CenterX *= ratioX;
Obj.CenterY *= ratioY;
if (Obj is QXSpirit) {
QXSpirit Spirit = Obj as QXSpirit;
//缩放文字
Spirit.Describtion.Height *= ratioY;
Spirit.Describtion.SetValue(Canvas.TopProperty, (double)Spirit.Describtion.GetValue(Canvas.TopProperty) * ratioY);
Spirit.Faction.FontSize *= ratioX;
Spirit.Clan.FontSize *= ratioX;
Spirit.SName.FontSize *= ratioX;
}
}
}
}
首先计算出X、Y方向的缩放比例ratioX与ratioY,然后遍历所有对象物体与尺寸有关的属性进行“乘以”缩放比例操作。这样仅仅二十来行代码,即实现了对窗口中的所有对象物体的按比例缩放。大家不妨在不同的分辨率下或不同尺寸的显示器上进行此缩放操作测试,结果都是很完美的:
这里需要特别说明的是,WPF是基于矢量的图形引擎,但是如果您使用的是像素图片,例如本例中的角色与地图,那么它同样会被基于像素进行拉伸,因为它并未被转换成矢量图。
本节内容很简单,但是简单的背后是不为人知的烦琐与枯燥的调试。大家或许会因自觉本节内容毫无价值而十分恼火,但是我想告诉大家的是,不要小看了这不起眼的功能与调试,它不仅仅是对系统架构的一次有力考验(如果架构存在缺陷,就算勉强实现了表面上的窗口切换,角色一旦移动起来将会导致系统及画面漏洞百出);同时,就好比现在的网站需要符合W3C标准,需要同时兼容IE与FIREFOX一样,软件是做给客户用的,一款软件能够满足各种各样不同客户的使用需求,这才是价值两个字的深层体现。本节没有诗情画意的知识描述,只为一下节的华丽登场做好铺垫:帅气的主角将不再孤单:怪物们都出来吧!敬请关注。