C#開發WPF/Silverlight動畫及游戲系列教程(Game Course):(二十四) Be careful!前方怪物出沒
游戲的精靈框架到此為止算告一段落,讓我們一同來體驗它帶來的神奇效應。
一個安靜的黃昏,主角悠閒的甩著它帥氣的毛發獨跑於林陰大道。怎知天色已晚即將進入月亮的領地,嘿嘿,我們的故事就從這裡開始:Be careful,前方怪物出沒!
實在不忍心讓主角空有一身武藝而無處施展,本節為了不再讓它孤單,我將向游戲中加入可愛的妖精妹妹與之為伴:
好象在哪見過呢?對,就是她了,可愛吧(QXGameEngine中的怪物,^_^||難怪這眼熟)。
妖精怪物屬於精靈類型,因此要讓它在游戲中出現,我們只需創建QXSpirit的實例;這裡首先我添加一個刷怪方法InitMonster,接著循環添加怪物精靈實例及參數:
QXSpirit[] Monster;
/// <summary>
/// 初始化怪物(刷怪)
/// </summary>
/// <param name="num">怪物數量</param>
private void InitMonster(int num) {
Monster = new QXSpirit[num];
Random random = new Random(); //測試用,隨機數坐標
for (int i = 0; i < num; i++) {
if (Carrier.FindName("Magicer" + i.ToString()) == null) {
Monster[i] = new QXSpirit();
Monster[i].Name = "Magicer" + i.ToString();
Carrier.RegisterName(Monster[i].Name, Monster[i]);
Monster[i].Equipment[0] = 100;
Monster[i].Equipment[1] = 0;
……
Monster[i].X = 2000 - random.Next(1000);
Monster[i].Y = 1500 - random.Next(1000);
……
Carrier.Children.Add(Monster[i]);
}
}
}
代碼太多我就不一一羅列了,下面我提取部分重要的參數進行講解。首先我定義一個Random類型隨機函數用於隨機數值的產生,接著判斷每一個有名字的精靈是否存在,如果不存在則加入到游戲中。這裡為精靈注冊名字的目的是為了以後方便管理,例如精靈死掉了,我們需要找到它的實例並將之移除,而惟有通過它的名字或ID之類的方能將之捕獲;同時,在定時刷怪的機制下,我們得首先判斷某精靈是否還存在,如果存在則不可能再多刷一個,就好比網絡游戲中大家是否都有過蹲點等刷BOSS暴裝備的經歷,一個蘿卜一個坑,同一點上刷出兩個雙胞胎BOSS,這是很匪夷所思的事。那麼定義完怪物的名字後,我們接著還需要定義它的身體圖片代號,本例中它的代號為100(為了與主角類精靈用的身體圖片區別,我以0-99代表主角類身體(衣服)代號范圍,100-N代表怪物(NPC)類身體代號范圍),最後通過前面的random來定義它們的初始坐標:以(2000,1500)為中心邊長為1000的正方形范圍內的隨機位置。
就這麼完啦?對,簡單吧,還是那句老話,拓展性優良的架構是經得起全方位的考驗滴。嘿嘿,刷它30個怪(InitMonster(30))測試一下:
怪物滿天飛,主角此時激動的心情是難以用人類的語言來形容的。可是,當主角向四周望去時,蒙了:雜都和我名同姓捏?作者你腦神經搭到腳底了吧?。。。我還真沒注意到哪。在前面的章節裡,我將精靈的3個身份描述都定義在了xaml裡面(Faction門派,Clan家族,Sname名字)。此時,我們對精靈的重命名必須在精靈初始化的同時進行。但由此帶來的新問題:如果每個怪物都有不同的身份描述,而且需要經常性的修改調整,寫在內存裡的東西是無法擴展的。這不禁讓我聯想到了第二十二節中的xml配置文件解決方案。網絡游戲的服務器會根據地圖區域代號加載相應的地圖xml配置文件,其中包括地圖及遮擋物以及地圖上相當重要的精靈對象信息。當需要時我們只需對xml文件進行稍稍修改,例如精靈怪物的位置,描述,等級等相關信息即可以達到更新的目的。根據此原理,我對原有的Config配置文件進行如下改進,並添加怪物參數設置:
<Map Sign="1">
<Surface Src="Map\1\0.jpg" Width="3200" Height="2400" X="0" Y="0" CenterY="0" Opacity="1"></Surface>
<Masks></Masks>
<Spirits>
<Monster Name="Magicer001" Faction="傾城之戀" Clan="美麗呀" SName="倒影在心房" FrameRange0="10" FrameRange1="12" FrameRange2="11" FrameRange3="13" FrameRange4="15" SingleWidth="150" SingleHeight="150" TotalWidth="9150" TotalHeight="1200" Equipment0="100" Equipment1="0" CenterX="75" CenterY="125" X="700" Y="300" Direction="4" Speed="120"></Monster>
……
<Monster Name="Beast001" Faction="精英" Clan="食人族酋長" SName="絕對無敵" FrameRange0="8" FrameRange1="8" FrameRange2="9" FrameRange3="6" FrameRange4="9" SingleWidth="200" SingleHeight="200" TotalWidth="8000" TotalHeight="1600" Equipment0="101" Equipment1="0" CenterX="100" CenterY="170" X="200" Y="1200" Direction="0" Speed="125"></Monster>
……
</Spirits>
</Map>
以上為xml文件的部分代碼,大家將代碼中黃色背景的數據與InitMonster方法中的賦值屬性進行對照會發現它們之間基本上是一一對應的。設置完後,我們只需通過第二十二節中加載xml數據的方法:
XElement mapdata = Super.GetTreeNode(Super.SystemConfig, "Map", "Sign", "1");
……
//抽離精靈數據中的怪物集合並用其來初始化地圖上的怪物
InitMonster(mapdata.Element("Spirits").Elements("Monster"));
即可遍歷xml配置文件中地圖(Map)代號為1(Sign==1)的精靈類型(Spirits)中的具體類型為怪物(Monster)的數據,然後將它們的值與精靈的參數屬性進行一一對應賦值即可以動態的完成精靈怪物的初始化。
仔細看的朋友一定注意到了xml文件中地圖1中的怪物除了美麗的妖精外,還多了一個名字叫“絕對無敵”的家伙。嘿嘿,它可是我為了本教程特意花了1個小時處理出來的,以次感謝大家這段時間來對我的支持!同時也是對自己勞動的犒賞!挺值得的。那麼我們都各刷它若干只測試一下:
忽忽,怪物出現了。又到了本教程的國際慣例---“自戀一刻”!讓主角在地圖上逛逛吧~當到了怪多的地方突然會發現有些許的不連貫,問題出在線程上。本教程例子均使用封裝好的線程DispatcherTimer,因此管理起來是相當方便的(比起直接使用Thread簡單多了),僅僅需要設置它們的優先級別即可進行相應調整。經過再三考慮與調試,最終的線程結構如下:暫時將主角的生命線程表現級別設置為9級(Normal級),將怪物的生命線程表現級別設置為7級(Render級),並且分別對5個不同的動作(站立、跑步、戰斗、施法、死亡)設置相應的後台處理級別分別對應為Lowest、Normal、BelowNormal、BelowNormal、Lowest。對這些線程的調試過程是件非常有趣的嘗試,我曾反復嘗試並樂在其中(太過瘾導致我無法自拔因而延誤了教程的發布…讀者聲音:“癫子”。^_^||)。不同的線程級別搭配將在游戲性能、畫面及操作手感上產生相當明顯的區別,大家不妨在我的代碼基礎上,將主角的生命線程表現級別由Normal調節為Background,或將跑步的級別由Normal調節到最低級別Lowest,然後分別運行一下,區別一目了然。
最後發張最終的測試截圖,嘿嘿,我的怪獸軍團:
輕輕松松就創建出了如此美妙的怪物精靈,這全都得歸功於第十四節創建的精靈控件。上天入地如探囊取物,簡單設置即可隨意實現主角、怪物、NPC等等角色的創建,這就是控件封裝給我們帶來的完美體驗。
下一章我將講解怪物的層次關系以及如何准確的判斷及拾取鼠標下的精靈對象,敬請關注。
出處:http://alamiye010.cnblogs.com/