C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三十七)地图自适应区域加载...
目前地圖編輯器已經能夠對地圖圖片進行切片了,那么接下來我們需要做的是對切好的地圖片進行拼裝從而取代整張大地圖。需要特別說明的是,如果一次性將所有的切片加載進游戲中并顯示出來,那么效果與使用一張整的地圖幾乎沒區別,性能上甚至可能會更差;本節我們最終要達到的目的是利用這些切片自適應的填充游戲窗口區域,即用最少的地圖片實現窗口的無縫填充,從而提升游戲的整體性能。
教程示例游戲窗口模式時的窗體尺寸為800*600,那么我們首先根據此尺寸以400*300為單位利用地圖編輯器切割出若干地圖切片:
此時我們回過頭來對照一下地圖原圖的容量與現在切好片的地圖片的容量總和即會發現,在保持畫質不變的前提下,切片后的總容量比之前減少了近一半。如果您做的是網絡版,那么通過切片來實現按需下載,將縮短用戶加載程序的時間同時降低服務器的負擔:
接下來是關鍵了,游戲窗口800*600,地圖切片每張為400*300,那么每次最少需要顯示多少張切片才能在主角無論處于何位置時都能填滿游戲窗口呢?大家不妨先看下圖:
主角始終處于游戲窗口的正中,那么它距離左邊400像素,距離頂端300像素,因此,我們通過邊緣法可以得出,只需要根據主角的x,y坐標計算出主角當前所處的地圖切片代號。以上圖為例,此時主角所處的切片為2_3.jpg,那么我們只需加載2_3以及它周邊的9張切片:1_2.jpg、2_2.jpg、3_2.jpg、1_3.jpg、2_3.jpg、3_3.jpg、1_4.jpg、2_4.jpg、3_4.jpg即可完美填充整個游戲窗口。當然,填充用地圖切片必須是自適應的,即時時根據主角所處位置來更換所需要切片。
理解原理后,代碼實現起來就簡單多了,首先我們需要修改地圖Surface初始化代碼:
??????? Canvas MapSurface;
??????? int mapSectionWidth, mapSectionHeight, mapSectionXNum, mapSectionYNum;
??????? /// <summary>
??????? /// 初始化地圖地表層
??????? /// </summary>
??????? private void InitMapSurface(XElement args) {
??????????? MapSurface = new Canvas() {
??????????????? Width = Convert.ToDouble(args.Attribute("Width").Value),
??????????????? Height = Convert.ToDouble(args.Attribute("Height").Value),
??????????? };
??????????? Add(MapSurface);
??????????? mapSectionWidth = Convert.ToInt32(args.Attribute("SectionWidth").Value);
??????????? mapSectionHeight = Convert.ToInt32(args.Attribute("SectionHeight").Value);
??????????? mapSectionXNum = (int)(MapSurface.Width / mapSectionWidth);
??????????? mapSectionYNum = (int)(MapSurface.Height / mapSectionHeight);
??????? }
??? 然后我們定義兩個非常重要的切片定位用變量:
??????? int _leaderSectionX;
??????? /// <summary>
??????? /// 主角所處的切片X值
??????? /// </summary>
??????? public int leaderSectionX {
??????????? get { return _leaderSectionX; }
??????????? set { if (_leaderSectionX != value) { _leaderSectionX = value; ChangeMapSection(); } }
??????? }
??????? int _leaderSectionY;
??????? /// <summary>
??????? /// 主角所處的切片Y值
??????? /// </summary>
??????? public int leaderSectionY {
??????????? get { return _leaderSectionY; }
??????????? set { if (_leaderSectionY != value) { _leaderSectionY = value; ChangeMapSection(); } }
??????? }
leaderSectionX和leaderSectionY分別為主角所處的切片X,Y值(對應切片命名中下劃線“_”兩端的值),其中任意一個更改后都將激發ChangeMapSection方法:
??????? Image mapSection;
??????? int startSectionX, startSectionY,endSectionX,endSectionY;
??????? /// <summary>
??????? /// 更新呈現的地圖切片
??????? /// </summary>
??????? private void ChangeMapSection() {
??????????? MapSurface.Children.Clear();
??????????? if (leaderSectionX == 0) {
??????????????? startSectionX = 0; endSectionX = 2;
??????????? } else if (leaderSectionX == mapSectionXNum - 1) {
??????????????? startSectionX = leaderSectionX - 2; endSectionX = leaderSectionX;
??????????? } else {
??????????????? startSectionX = leaderSectionX - 1; endSectionX = leaderSectionX + 1;
??????????? }
??????????? if (leaderSectionY == 0) {
??????????????? startSectionY = 0; endSectionY = 2;
??????????? } else if (leaderSectionY == mapSectionYNum - 1) {
??????????????? startSectionY = leaderSectionY - 2; endSectionY = leaderSectionY;
??????????? } else {
??????????????? startSectionY = leaderSectionY - 1; endSectionY = leaderSectionY + 1;
??????????? }
??????????? for (int x = startSectionX; x <= endSectionX; x++) {
??????????????? for (int y = startSectionY; y <= endSectionY; y++) {
??????????????????? mapSection = new Image() {
??????????????????????? Source = Super.getImage(string.Format("Map/{0}/Surface/{1}_{2}.jpg", mapCode.ToString(), x, y)),
?????????????????? ?????Width = mapSectionWidth,
?????????????????? ?????Height = mapSectionHeight,
??????????????????????? Stretch = Stretch.Fill,
??????????????????? };
??????????????????? MapSurface.Children.Add(mapSection);
??????????????????? Canvas.SetLeft(mapSection, x * mapSectionWidth);
??????????????????? Canvas.SetTop(mapSection, y * mapSectionHeight);
??????????????? }
??????????? }
??????? }
這里我的處理是每次更換切片時都首先清除原有的9塊切片,然后再添加新的9塊切片到指定位置,終究是比較苯的辦法,但實現起來簡單;優化它的方法有兩種:1)判斷主角是向右移動到了新的切片上還是向左或是向下、向下,然后移除對應的3塊再添加3塊。2)將所有切片加載進地圖中,只顯示主角當前所處的9塊切片,而其他的所有切片均處于Visibility.Collapsed狀態不參加布局。此兩種方法應該算得上最終解決方案,您可以根據您游戲的實際需要進行選擇。
最后,在游戲的輔助線程中間隔檢測主角所處的切片位置(由于有間隔,所以在區域轉換時偶爾會出現白邊的現象,如果接受不了,您完全可以將下面兩行代碼放到畫面Loop中進行時時判斷處理):
???? leaderSectionX = (int)(Leader.X / mapSectionWidth);
???? leaderSectionY = (int)(Leader.Y / mapSectionHeight);
嘿嘿~是不是很簡單,且讓我們先運行一下看看效果:
問題出來了,切片的邊緣怎么也給顯示了出來?難道是我們沒有切割好嗎?我將這些切片放進PS中重新對接,其實是可以完美吻合的,問題到底出在哪?希望好心的朋友告訴一下啦~。當然,這些縫隙是可以很輕易處理掉的,只需將每塊切片的寬與高各加1即可:
……
mapSection = new Image() {
????? Source = Super.getImage(string.Format("Map/{0}/Surface/{1}_{2}.jpg", mapCode.ToString(), x, y)),
????? Width = mapSectionWidth + 1,
????? Height = mapSectionHeight + 1,
????? Stretch = Stretch.Fill,
};
……
修改后,我們再次運行游戲,當主角移動到右下角時,我們會看到兩片白色無圖區域:
難道又是BUG?NO,NO。在上一節中我曾說過,地圖編輯器只會按比例切割出同尺寸圖片,如果某片的尺寸小于每片單位尺寸,則不切割。由于我們使用的示例地圖圖片的尺寸為2560*1920,因此以每片400*300為單位,只能切割出2400*1800的區域,這就是導致游戲中最終出現上圖情況的原因了。
遇到這種情況我們該如何處理呢?最好的辦法就是規范統一地圖尺寸與切片尺寸:假設您的游戲窗口尺寸為800*600,切片單位為400*300,那么您的整張地圖尺寸必須設定寬度為400的整倍數,高度為300的整倍數。另一種方案就是通過邏輯來呈現這些邊緣不標準的切片。
地圖自適應區域加載的實現,在一定程度上優化了游戲的整體性能,在地圖尺寸越大的游戲中表現得更為突出。所有關于地圖的教程快接近尾聲了,下一節我將為大家講解如何實現地圖切換與傳送,敬請關注。
作者:深藍色右手出處:http://alamiye010.cnblogs.com/
教程目錄及源碼下載:點擊進入(歡迎加入WPF/Silverlight小組 WPF/Silverlight博客團隊)
本文版權歸作者和博客園共有,歡迎轉載。但未經作者同意必須保留此段聲明,且在文章頁面顯著位置給出原文連接,否則保留追究法律責任的權利。 新人創作打卡挑戰賽發博客就能抽獎!定制產品紅包拿不停!
總結
以上是生活随笔為你收集整理的C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三十七)地图自适应区域加载...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用JavaScript实现一个Timel
- 下一篇: c# char unsigned_dll