C#開發WPF/Silverlight動畫及游戲系列教程(Game Course):(二十三)自適應性窗口化與全屏化(WPF Only)
上一節中曾有提到,檢測系統架構是否合理的評判標准之一就是系統的拓展性。在.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一樣,軟件是做給客戶用的,一款軟件能夠滿足各種各樣不同客戶的使用需求,這才是價值兩個字的深層體現。本節沒有詩情畫意的知識描述,只為一下節的華麗登場做好鋪墊:帥氣的主角將不再孤單:怪物們都出來吧!敬請關注。
出處:http://alamiye010.cnblogs.com/