C#開發WPF/Silverlight動畫及游戲系列教程(Game Course):(十一)地圖遮罩層的實現
前面的章節主要針對地圖表現層進行講解。通常來說,簡單的游戲光有它就足夠了;但是為了達到更加真實的光影效果,模擬真實的虛擬世界,我們還得繼續在地圖上下大工夫。本節將就如何實現地圖中的遮罩層,即物體對角色的遮擋進行詳細講解。
首先我們來看一張比較完善的地圖應該包含哪些內容:
從上圖可以看到,我將一張地圖引擎結構分成了3層(難道這就是傳說中的地圖三層架構?汗一個先。。。)。中間的圖片代表地圖的表現層,也就是我們視覺上直接看到的地圖界面。關於它,前面的章節中已有非常多的講解,這裡就不再累述了。接著我們再看最下面那張:地圖底層,它由黑白兩大顏色組成,似乎還有一圈黃色在右小角呢。有的朋友覺得它很奇怪,似乎摸不著頭腦,好象和地圖沒啥關系吧?其實只要將它和第二張圖進行分析比較就會發現,它上面的黑色就是地圖中障礙物區域,白色則為可以通行的區域,那黃色呢??還有朋友要問了:前面的章節不是有講A*尋路嗎?通過Matrix[]數組來構建障礙物不是很完美嗎?那為什麼還要多此一舉再為每張地圖構造一張同比例的障礙物底層圖呢?我只想告訴廣大的朋友們:它的作用可大了,尤其尤其在目前的Silverlight游戲開發中,它的作用及拓展性可謂承前啟後,用科學發展觀的話講就是:面向對象的思維開發Silverlight游戲。太多太多的懸念,才能有更多的期待,那麼關於這張神秘底層圖的講解,請聽下回分析。
讀者聲音:同志,你也太假了吧,這樣就講完這節啦?BS你一下。
作者:安啦,怎麼可能嘛,這叫倒敘懂不?(啥叫倒敘其實俺也不太。。。?嘿嘿)
不瞎扯啦,還剩一張圖沒講呢,對啦,本節的主角就是它了:地圖遮罩層。
首先來講講實現原理吧:我們可以從地圖表現層(下文直接就稱之地圖好了)中看到,遮擋人物的只有一棵樹。那麼我們想要在此地圖上實現遮罩效果,首先就得用Photoshop將這棵樹給截出來,當然越精確越好,然後將它單獨保存成一張背景透明的圖片(通常Windows桌面RPG游戲中會將所有的遮擋物統一規格,例如50*50一張(如大於則分兩張、三張…等等),然後將全部遮擋物圖片放進一個龐大的二進制文件中,顯然這對於Silverlight基於網頁的游戲是不容許的),如果一張地圖上有多個遮擋物,同樣將他們都截取出來然後依次命名保存。准備工作做完後,我們就需要將遮罩層的圖片放在頂層,將地圖放在底層,人物等放在中間層。最後分別將遮罩層的所有圖片布局到它們應該遮擋的位置上,這樣就完成了所有的遮擋工作了。好了。下面我將用代碼來實現它。
這裡我以下圖作為地圖實例:
很明顯該地圖有三處障礙物,兩處遮擋物。障礙物我用綠色區域描繪出來了,遮擋物則為兩棵數,我用Photoshop將它們分別截取了出來命名為:Mask1.png和Mask2.png。
匆忙了點,截得不好可不要見怪哪!誰讓這兩棵樹長得如此奇怪呢?嘿嘿。
OK,接下我以第九節的代碼為基礎進行修改,首先構建障礙物:
//構建障礙物
for (int y = 22; y <= 24; y++) {
for (int x = 5; x <= 16; x++) {
//障礙物在矩陣中用0表示
Matrix[x, y] = 0;
rect = new Rectangle();
rect.Fill = new SolidColorBrush(Colors.GreenYellow);
rect.Opacity = 0.3;
rect.Stroke = new SolidColorBrush(Colors.Gray);
rect.Width = GridSize;
rect.Height = GridSize;
Carrier.Children.Add(rect);
Canvas.SetLeft(rect, x * GridSize);
Canvas.SetTop(rect, y * GridSize);
}
}
for (int y = 11; y <= 14; y++) {
for (int x = 27; x <= 31; x++) {
//障礙物在矩陣中用0表示
Matrix[x, y] = 0;
rect = new Rectangle();
rect.Fill = new SolidColorBrush(Colors.GreenYellow);
rect.Opacity = 0.3;
rect.Stroke = new SolidColorBrush(Colors.Gray);
rect.Width = GridSize;
rect.Height = GridSize;
Carrier.Children.Add(rect);
Canvas.SetLeft(rect, x * GridSize);
Canvas.SetTop(rect, y * GridSize);
}
}
for (int y = 18; y <= 21; y++) {
for (int x = 33; x <= 37; x++) {
//障礙物在矩陣中用0表示
Matrix[x, y] = 0;
rect = new Rectangle();
rect.Fill = new SolidColorBrush(Colors.GreenYellow);
rect.Opacity = 0.3;
rect.Stroke = new SolidColorBrush(Colors.Gray);
rect.Width = GridSize;
rect.Height = GridSize;
Carrier.Children.Add(rect);
Canvas.SetLeft(rect, x * GridSize);
Canvas.SetTop(rect, y * GridSize);
}
}
三個循環分別構建了上圖中的三處障礙物,這幾章都對它進行了修改,大家應該再熟悉不過了。接下來就是遮擋物那兩棵樹了,這裡我用Image控件作為遮擋物的容器:
//創建遮罩層
Image Mask1 = new Image();
Image Mask2 = new Image();
private void InitMask() {
Mask1.Width = 238;
Mask1.Height = 244;
Mask1.Source = new BitmapImage((new Uri(@"Map\Mask1.png", UriKind.Relative)));
Mask1.Opacity = 0.7;
Carrier.Children.Add(Mask1);
Canvas.SetZIndex(Mask1, 10000);
Canvas.SetLeft(Mask1, 185);
Canvas.SetTop(Mask1, 220);
Mask2.Width = 198;
Mask2.Height = 221;
Mask2.Source = new BitmapImage((new Uri(@"Map\Mask2.png", UriKind.Relative)));
Mask2.Opacity = 0.7;
Carrier.Children.Add(Mask2);
Canvas.SetZIndex(Mask2, 10000);
Canvas.SetLeft(Mask2, 466);
Canvas.SetTop(Mask2, 11);
}
這樣就將遮擋物加入進了游戲窗體。有了前面那麼多章節關於Image控件的使用知識,上面的代碼應該不難理解。這裡特別要說一下的是為什麼要將它們的Opacity設置為0.7:因為這樣的遮擋物會有一定的透明度,當角色置身其中時會若隱若現,從而達到真實模擬MMORPG的效果。至於為什麼要將遮擋物的Zindex屬性設置為10000呢?這關系到游戲運行時地圖中不光只有一個角色,還會有非常多的物體及對象角色的存在,它們之間也同樣有著相互遮擋與被遮擋的關系。而在WPF/Silverlight游戲中,物體的遮擋順序一樣可以使用畫家算法,該算法原理簡單描述就是近物遮擋遠物,幸運的是在WPF/Silverlight中,我們可以很方便的只要動態更新(一個對象的Zindex屬性)=(它的Y屬性)即可以巧妙的實現此效果,是不是有點邪惡?嘿嘿。所以要將遮蓋物的ZIndex設置得足夠大以防止任何一個物體它的Y屬性大過遮蓋物的Zindex屬性,從而造成畫面顯示BUG。
其他的代碼均和第九章的一樣,到這,本節的目標已經達到了。那麼讓我們運行測試一下吧:
大家可以隨便在地圖上點擊,會發現只要主角有經過這兩棵樹的地方都會被樹以0.7的透明度遮擋,並且障礙物也同樣並行存在著,主角如有經過同樣會饒過它。障礙物,人物,遮罩層次分明,互不干預,完美默契的並行著。
至此,地圖引擎就基本完成了。下一節將講解本節開始所提到的神秘第三層,它在WPF/Silverlight游戲輔助方面起著非常大的拓展作用,敬請關注。