




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
摘要在如今的互聯(lián)網(wǎng)時(shí)代影響下,人們的生活交往方式產(chǎn)生了變化。在學(xué)習(xí)工作的時(shí)間之外,游戲娛樂已經(jīng)成為人們休閑娛樂的主要方式之一,尤其是在互聯(lián)網(wǎng)高速發(fā)展的今天,網(wǎng)絡(luò)游戲成為了越來越多人的選擇。網(wǎng)絡(luò)游戲不僅是人們放松身心的娛樂方式,也是溝通交友的有效途徑,網(wǎng)絡(luò)游戲已然成為人們休閑之時(shí)的很好選擇,網(wǎng)絡(luò)游戲的發(fā)展越來越受到重視。Unity3D是目前最流行的游戲開發(fā)引擎之一,以其低成本與高效率被多數(shù)游戲開發(fā)者所使用。本文主要描述了使用Unity3D開發(fā)基于網(wǎng)絡(luò)的多人策略放置類型游戲的實(shí)際開發(fā)過程,以及開發(fā)過程中所應(yīng)用到的網(wǎng)絡(luò)連接與信息交流的原理與方法。在具體的開發(fā)流程中,主要描述了使用Unity3D作為開發(fā)引擎的游戲客戶端的開發(fā),隨后實(shí)現(xiàn)基于TCP的服務(wù)端的簡(jiǎn)單實(shí)現(xiàn),以及服務(wù)端與客戶端進(jìn)行交流的實(shí)現(xiàn),然后會(huì)實(shí)現(xiàn)游戲熱更新的相關(guān)步驟,最后進(jìn)行游戲的打包與功能的測(cè)試。為后續(xù)的同類型游戲開發(fā)提供借鑒與參考。關(guān)鍵詞:Unity3D;策略類游戲;網(wǎng)絡(luò)游戲
AbstractUndertheinfluenceoftheInternetera,people'swayoflifeandcommunicationhaschanged.Inadditiontostudyandworktime,gameentertainmenthasbecomeoneofthemainwaysofpeople'sleisureandentertainment,especiallyintherapiddevelopmentoftheInternettoday,onlinegameshavebecomemoreandmorepeople'schoice.Onlinegamesarenotonlyawayforpeopletorelaxphysicallyandmentally,butalsoaneffectivewaytocommunicateandmakefriends.Onlinegameshavebecomeagoodchoiceforpeopleintheirleisuretime,andthedevelopmentofonlinegameshasattractedmoreandmoreattention.Unity3Disoneofthemostpopulargamedevelopmentengines,whichisusedbymostgamedeveloperswithitslowcostandhighefficiency.Thispapermainlydescribestheactualdevelopmentprocessofdevelopingnetwork-basedmultiplayerstrategyplacementtypegameswithUnity3D,andtheprinciplesandmethodsofnetworkconnectionandinformationexchangeappliedinthedevelopmentprocess.Inthespecificdevelopmentprocess,itmainlydescribesthedevelopmentofthegameclientwhichUSESUnity3Dasthedevelopmentengine,thenrealizesthesimpleimplementationoftheserverbasedonTCP,aswellasthecommunicationbetweentheserverandtheclient,thenrealizestherelevantstepsofthegamehotupdate,andfinallycarriesoutthepackagingandfunctionaltestofthegame.Itprovidesreferenceforthefollowingdevelopmentofthesametypeofgames.Keywords:Unity3D;Strategygame;Networkgame
目錄第一章 緒論 緒論選題的目的和意義隨著人民生活水平的提高和互聯(lián)網(wǎng)終端硬件設(shè)施的改善,更多的人選擇在線上游戲里釋放生活中的壓力,為自己的生活增添更多的樂趣,游戲產(chǎn)業(yè)因此飛速發(fā)展。在眾多不同類型的游戲中,人們更趨向于選擇網(wǎng)絡(luò)游戲,所以網(wǎng)絡(luò)游戲占據(jù)絕對(duì)主體地位。選擇開發(fā)一款網(wǎng)絡(luò)游戲能夠獲得更大的市場(chǎng)需求和更多的機(jī)會(huì)。計(jì)算機(jī)的持續(xù)普及和網(wǎng)絡(luò)技術(shù)的不斷發(fā)展,為網(wǎng)絡(luò)游戲的發(fā)展提供了硬件基礎(chǔ)和技術(shù)支持。一款好的游戲開發(fā)引擎可以幫助開發(fā)者快速將游戲構(gòu)想付諸實(shí)現(xiàn)。在眾多游戲開發(fā)引擎中的Unity3D,以它的多平臺(tái)開發(fā)、上手難度低等特點(diǎn)脫穎而出。選用Unity3D作為開發(fā)引擎,可以幫助開發(fā)者更加便捷、高效地開發(fā)出一款完整的游戲。由于Unity3D游戲開發(fā)的成本較低、周期較短、適應(yīng)不同終端平臺(tái)、支持多種游戲類型開發(fā)等特點(diǎn),是許多游戲開發(fā)公司的首選,因此Unity3D開發(fā)等相關(guān)崗位擁有比較廣闊的市場(chǎng)和發(fā)展前景。研究現(xiàn)狀隨著互聯(lián)網(wǎng)技術(shù)和終端硬件的發(fā)展與完善、人們生活水平的提高,游戲市場(chǎng)越來越受到重視,游戲產(chǎn)業(yè)高速發(fā)展。2019年全球數(shù)字游戲總營(yíng)收高達(dá)1201億美元,其中的移動(dòng)端游戲營(yíng)收644億美元。移動(dòng)端游戲的收入占免費(fèi)游戲營(yíng)收的比重高達(dá)74%,在這其中騰訊的王者榮耀穩(wěn)定強(qiáng)勁的營(yíng)收占據(jù)了較大比重。這也體現(xiàn)了如今的游戲產(chǎn)業(yè)中,移動(dòng)端網(wǎng)絡(luò)游戲是比較受歡迎,有較大的市場(chǎng)和前景。論文組織結(jié)構(gòu)論文從最初的構(gòu)思到最終的完成,結(jié)合應(yīng)用了游戲開發(fā)與網(wǎng)絡(luò)交流相關(guān)的知識(shí)和技術(shù),成功實(shí)現(xiàn)了一個(gè)基于Unity3D的網(wǎng)絡(luò)多人策略游戲的開發(fā),其中具體介紹了游戲框架設(shè)計(jì)以及游戲制作的關(guān)鍵步驟,也包括游戲的系統(tǒng)功能設(shè)計(jì)和一系列代碼的編寫。論文分為六章,各章節(jié)安排如下:?第一部分:緒論,主要闡述了該畢業(yè)設(shè)計(jì)制作的背景、相關(guān)課題的研究現(xiàn)狀以及本設(shè)計(jì)的大概介紹;?第二部分:介紹了本文制作游戲使用的各類開發(fā)工具和Unity3D的大致介紹;第三部分:介紹游戲流程設(shè)計(jì);第四部分:游戲?qū)嵺`開發(fā)的關(guān)鍵步驟;?第五部分:對(duì)游戲進(jìn)行打包并測(cè)試;第六部分:進(jìn)行最終總結(jié),回顧游戲的缺陷以及改進(jìn)方法。
開發(fā)工具介紹Unity3D游戲引擎Unity3D是一款3D跨平臺(tái)次時(shí)代游戲引擎。作為一款專業(yè)的游戲開發(fā)引擎,它有資深技術(shù)團(tuán)隊(duì)的支持、友善的圖形化界面、豐富的拓展插件和多平臺(tái)的導(dǎo)出配置。Unity降低了游戲開發(fā)門檻,引擎的上手難度較小,對(duì)新手比較友好。使用Unity開發(fā)游戲的成本低,開發(fā)周期較短,是大多數(shù)游戲公司和游戲開發(fā)者的最佳選擇。C#開發(fā)語(yǔ)言C#是微軟公司發(fā)布的一種運(yùn)行于.NETFramework和.NetCore上的高級(jí)程序設(shè)計(jì)語(yǔ)言,是由C和C++衍生出來的面向?qū)ο蟮木幊陶Z(yǔ)言,擁有較高的運(yùn)行效率和較強(qiáng)的操作能力,因?yàn)槠鋭?chuàng)新性的語(yǔ)言特性以及面向組件的編程支持,成為了Unity3D開發(fā)選用的主流語(yǔ)言。C#不僅能用于開發(fā)傳統(tǒng)Windows環(huán)境中的應(yīng)用程序,還可以用來開發(fā)原生的Android、iOS、WindowsPhone和MacApp應(yīng)用程序,甚至還能整合Azure或Hadoop技術(shù)開發(fā)云計(jì)算和大數(shù)據(jù)應(yīng)用系統(tǒng)。[5]C#也提供了網(wǎng)絡(luò)編程相關(guān)類庫(kù),因此也可被用于服務(wù)端的開發(fā)。VisualStudio2017VisualStudio2017(以下簡(jiǎn)稱VS)是微軟公司推出的集成開發(fā)環(huán)境。提供強(qiáng)大的調(diào)試和性能分析工具,快捷地查找和修復(fù)錯(cuò)誤的方式,強(qiáng)大的庫(kù)調(diào)用和云集成,為Unity3D游戲開發(fā)提高了效率。MySQL數(shù)據(jù)庫(kù)MySQL是由瑞典MySQLAB公司開發(fā)的一個(gè)關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng)。因?yàn)槠潢P(guān)系型的特點(diǎn),存儲(chǔ)時(shí)將數(shù)據(jù)保存在不同的表中,提高了讀寫速度和整個(gè)數(shù)據(jù)庫(kù)的靈活性。也擁有體積小、速度快、運(yùn)營(yíng)成本低等優(yōu)點(diǎn),是中小型項(xiàng)目開發(fā)時(shí)數(shù)據(jù)庫(kù)使用的較好選擇。Lua開發(fā)語(yǔ)言Lua是由巴西里約熱內(nèi)盧天主教大學(xué)里的一個(gè)由RobertoIerusalimschy、WaldemarCeles和LuizHenriquedeFigueiredo三人所組成的研究小組于1993年開發(fā)的一個(gè)小巧的腳本語(yǔ)言,該腳本語(yǔ)言設(shè)計(jì)之初就是為了嵌入到應(yīng)用程序中,為其提供擴(kuò)展和定制等功能。由于C#是預(yù)編譯語(yǔ)言,在手機(jī)端的熱更新上不太兼容,所以使用Lua語(yǔ)言嵌入C#實(shí)現(xiàn)游戲代碼的熱更新。
游戲設(shè)計(jì)游戲玩法設(shè)計(jì)本游戲是根據(jù)目前比較流行的策略放置類型游戲:自走棋,進(jìn)行設(shè)計(jì)和開發(fā)的。在游戲中,玩家有金幣欄、生命值和經(jīng)驗(yàn)欄。每一小局系統(tǒng)會(huì)給玩家若干金幣,提供5個(gè)英雄讓玩家購(gòu)買,玩家通過點(diǎn)擊UI使用金幣購(gòu)買英雄、刷新英雄或提升自身的等級(jí),通過賣出英雄獲得金幣。玩家收集不同的英雄搭配,為英雄佩戴裝備,提升自己的陣容強(qiáng)度,在對(duì)戰(zhàn)時(shí)刻來臨時(shí),對(duì)戰(zhàn)雙方的英雄陣營(yíng)進(jìn)行自動(dòng)對(duì)戰(zhàn),失敗一方扣除生命。玩家的生命值小于等于0,就淘汰出局,游戲一直持續(xù)至出現(xiàn)唯一存活下來的勝利玩家。游戲熱更新在游戲以及上架后,如果我們發(fā)現(xiàn)游戲中有Bug或者想要修改游戲中的模型貼圖甚至是發(fā)行DLC等等,我們可以選擇在Unity3D編輯器中修改,然后再打包后發(fā)行。當(dāng)是這樣操作后,游戲就得重新花費(fèi)時(shí)間進(jìn)行審核,玩家也得重新下載整個(gè)游戲。這就會(huì)造成玩家的流失,尤其在手機(jī)玩家的流失方面更為嚴(yán)重。因此游戲熱更新應(yīng)運(yùn)而生。游戲熱更,只需下載需要更新的部分覆蓋舊的部分,極大的降低了游戲更新地代價(jià)。尤其在網(wǎng)絡(luò)游戲方面,如果出現(xiàn)一個(gè)BUG可能會(huì)破壞游戲平衡甚至導(dǎo)致整個(gè)游戲的終結(jié),熱更新的及時(shí)性,可以降低游戲修復(fù)的時(shí)間和難度。在每次打開游戲的時(shí)候,都會(huì)進(jìn)行更新檢查,如果發(fā)現(xiàn)新的版本,就會(huì)進(jìn)行熱更新。圖3-1游戲進(jìn)行熱更新游戲登錄界面在熱更新完成之后,就會(huì)進(jìn)入游戲登錄界面。玩家可以在登錄界面進(jìn)行賬號(hào)注冊(cè)和登錄。登錄及注冊(cè)的內(nèi)容會(huì)通過協(xié)議發(fā)送到服務(wù)端,服務(wù)端解析后,通過SQL語(yǔ)句訪問數(shù)據(jù)庫(kù),完成玩家的操作。圖3-2游戲登錄界面游戲大廳及房間在玩家成功登錄后,會(huì)進(jìn)入游戲大廳。大廳會(huì)顯示出所有的游戲房間。左側(cè)為玩家的賬號(hào)信息,右側(cè)為大廳界面,提供創(chuàng)建房間和刷新界面的按鈕。玩家可以創(chuàng)建房間或者進(jìn)入別人的房間進(jìn)行游戲。圖3-3游戲大廳界面當(dāng)玩家進(jìn)入一個(gè)房間后,游戲會(huì)切換至房間界面。房間界面提供了離開、添加機(jī)器人玩家、開始游戲三個(gè)按鈕。一個(gè)房間最多可以進(jìn)入8位玩家,當(dāng)房間內(nèi)的玩家數(shù)量至少為2名時(shí),才能進(jìn)行游戲。圖3-4房間界面游戲流程英雄和裝備購(gòu)買房主點(diǎn)擊開始游戲按鈕后發(fā)送開始游戲協(xié)議到服務(wù)端,服務(wù)端接收到消息后在派送給所有客戶端,客戶端接收到開始游戲協(xié)議后,開始加載游戲資源,當(dāng)資源加載完成后,發(fā)送完成協(xié)議到服務(wù)端,服務(wù)端收集到全部的協(xié)議后,廣播正式開始游戲協(xié)議,所有玩家進(jìn)入游戲界面。通過點(diǎn)擊游戲右下角的棋簍按鈕可進(jìn)入英雄和裝備購(gòu)買界面,英雄購(gòu)買界面顯示英雄的價(jià)格、羈絆等信息。玩家通過購(gòu)買英雄和裝備并使用,從而與其它玩家進(jìn)行對(duì)戰(zhàn)。圖3-5英雄和裝備購(gòu)買界面玩家點(diǎn)擊英雄后進(jìn)行購(gòu)買,購(gòu)買的英雄會(huì)出現(xiàn)在等待區(qū)域,通過拖拽方式將英雄放置到戰(zhàn)斗區(qū)域,拖拽時(shí)會(huì)出現(xiàn)綠色的UI提示可放置位置。購(gòu)買后的裝備會(huì)出現(xiàn)在左下角的倉(cāng)庫(kù)內(nèi),拖拽倉(cāng)庫(kù)內(nèi)的裝備至英雄身上進(jìn)行裝備。圖3-6拖拽英雄至戰(zhàn)斗區(qū)域圖3-7拖拽裝備至英雄身上英雄羈絆在游戲畫面的左側(cè)有英雄羈絆界面,玩家將購(gòu)買后的英雄放置到戰(zhàn)斗區(qū)后,系統(tǒng)會(huì)統(tǒng)計(jì)戰(zhàn)斗區(qū)域英雄的羈絆組合,將羈絆組合顯示在羈絆界面并為英雄增加相應(yīng)的屬性,玩家可以通過不同的羈絆組合實(shí)現(xiàn)屬于自己的英雄陣容,游戲的可玩性得到提升。圖3-8羈絆界面升級(jí)英雄當(dāng)玩家購(gòu)買且集齊三個(gè)同等級(jí)同類型的英雄后,可以點(diǎn)擊英雄屬性界面的升級(jí)按鈕,將三個(gè)一級(jí)英雄升級(jí)為一個(gè)二級(jí)英雄,獲得更高的英雄屬性,更少的位置占用。圖3-9英雄升級(jí)畫面戰(zhàn)斗階段當(dāng)游戲畫面上方的倒計(jì)時(shí)結(jié)束后,進(jìn)入戰(zhàn)斗階段,戰(zhàn)斗區(qū)域的雙方英雄進(jìn)行戰(zhàn)斗。戰(zhàn)斗階段結(jié)束后,又會(huì)回到準(zhǔn)備階段。玩家需要不斷調(diào)整自己的英雄組合和裝備,確保自己贏得更多的戰(zhàn)斗。圖3-10英雄戰(zhàn)斗畫面結(jié)算界面每個(gè)戰(zhàn)斗階段結(jié)束后,如果任何一方有英雄存活,就會(huì)對(duì)對(duì)方玩家造成基于英雄數(shù)量的傷害。在游戲畫面的右方是游戲中玩家的生命值,當(dāng)某位玩家的生命值為0時(shí),就表示失敗,不再繼續(xù)參加游戲。當(dāng)只剩下一位玩家存活時(shí),這位玩家就是勝利者,同時(shí)游戲畫面會(huì)切換至結(jié)算畫面。結(jié)算界面會(huì)根據(jù)玩家們游戲失敗時(shí)的時(shí)間長(zhǎng)短,顯示玩家們的名次,對(duì)應(yīng)增加勝敗次數(shù)。圖3-11結(jié)算畫面在游戲結(jié)算后,玩家可以在結(jié)算界面查看自己的名次及勝負(fù)場(chǎng)次。然后可以點(diǎn)擊游戲畫面右下角的返回大廳按鈕,返回到游戲大廳,繼續(xù)進(jìn)入房間進(jìn)行游戲,或是退出游戲。
游戲?qū)崿F(xiàn)UI界面的實(shí)現(xiàn)UI界面框架PureMVC是一個(gè)基于MVC模式建立的輕量級(jí)框架,實(shí)現(xiàn)了經(jīng)典MVC設(shè)計(jì)模式中的Model、View、Controller三層分離的方式,有利于減少應(yīng)用程序與視圖間的依賴,降低整個(gè)程序的耦合程度。PureMVC框架非常適用于UI界面的構(gòu)造。UI視圖、數(shù)據(jù)和控制命令間的解耦,有利于UI界面的管理和更新。圖4-1PureMVC框架的結(jié)構(gòu)圖PureMVC大致分為四大部分:View:由具體的UI組件和對(duì)應(yīng)的Mediator組成。UI組件只負(fù)責(zé)UI的獲取。Mediator負(fù)責(zé)接收操作UI組件的命令和對(duì)UI組件進(jìn)行具體操作。Model:由Data和Proxy組成。Data保存了View層的數(shù)據(jù),可以是本地的或是下載得到的數(shù)據(jù)。Proxy是Data的封裝層,負(fù)責(zé)Data的獲取和操作。Controller:由自己編寫的許多Command組成,通過這些Command在其它兩層之間進(jìn)行交互,或是MVC系統(tǒng)外部對(duì)內(nèi)部的訪問。Fa?ade:是整個(gè)PureMVC體系的唯一外部接口,外部通過Fa?ade和對(duì)應(yīng)的Command對(duì)內(nèi)部進(jìn)行訪問。所有的Mediator、Proxy、Command都要在Fa?ade內(nèi)進(jìn)行注冊(cè)和刪除。UI子物體的獲取在Unity的的UI交互設(shè)計(jì)中,初學(xué)者通常是在預(yù)先編寫的腳本內(nèi),通過Find或GetChild獲取到UIPanel下的子物體進(jìn)行邏輯設(shè)計(jì)。當(dāng)我們?cè)谟螒蜷_發(fā)中后期,要對(duì)游戲UI進(jìn)行修改調(diào)整時(shí),如調(diào)整UI的層級(jí)或是添加新的子物體,原本編寫好的代碼就無(wú)法獲取到UI子物體,重新編寫腳本就顯得耗時(shí)耗力。所以可以使用一種便捷地獲取需要進(jìn)行使用的UI子物體的方式。在制作UIPanel之初,我們可以把需要使用的UI子物體的名稱進(jìn)行修改添加標(biāo)記,如“Button_”,在所有子物體的名稱后添加“_”。使用一個(gè)單例類UIManager,存放一個(gè)鍵值對(duì)為父節(jié)點(diǎn)的名稱和所有需要使用的子物體,類型為Dictionary<string,Dictionary<string,Transform>>的字典;以及對(duì)應(yīng)該字典的添加、移除和獲取方法。在每個(gè)UIPanel的初始化時(shí),遍歷自身的所有節(jié)點(diǎn),就能根據(jù)標(biāo)記獲取到所有需要的子物體,保存到字典內(nèi)。使用這種方式后,我們只需要知道父物體的名稱,該物體的名稱,以及該物體的類型,就可以根據(jù)獲得的transform得到對(duì)應(yīng)的組件。當(dāng)我們調(diào)整子物體的層級(jí)時(shí),原腳本無(wú)需修改;而當(dāng)添加新物體時(shí),只需要根據(jù)模板進(jìn)行獲取UI。GameObject
root
=
skin.gameObject;
Transform[]
allChild
=
root.GetComponentsInChildren<Transform>();
foreach
(var
i
in
allChild)
{
if
(.EndsWith("_"))
{
UIManager.Instance.AddUI(,
,
i);
}
}
UI界面流程控制和管理在所有父UI的層面上,我們也應(yīng)該進(jìn)行管理;使用類PanelManager和BasePanel組成界面管理。BasePanel作為所有Panel的基類,與UIManager和PureMve相結(jié)合。在OnShow中預(yù)先編寫之前的UI子物體獲取保存方式,每個(gè)Panel在OnShow時(shí)自動(dòng)運(yùn)行。在MVC的表現(xiàn)上:Panel腳本和其相互對(duì)應(yīng)的Mediator和作為UI界面的View層,對(duì)應(yīng)的Proxy和Data作為Model層,作為兩層間交互的控制命令作為Controller層,其實(shí)例在之后UIPanel的實(shí)際開發(fā)中體現(xiàn)。Panel繼承BasePanel并重寫OnInit、OnShow、OnClose三個(gè)虛函數(shù),編寫各自在不同情況下的邏輯;保存了SkinName和Skin,根據(jù)SkinName加載對(duì)應(yīng)的UIPanel游戲物體。 public
class
BasePanel
:
MonoBehaviour
{
//皮膚路徑
public
string
skinName;
//皮膚
public
GameObject
skin;
//層級(jí)
public
PanelManager.Layer
layer
=
PanelManager.Layer.Panel;
public
SceneEnum
SceneIndex;
public
int
itemIndex;//如果是子面板,索引
public
PanelManager.CanvasEnum
canvasIndex
=
0;//畫布索引
//初始化
public
void
Init(Transform
rootLayer,
Dictionary<string,
BasePanel>
panels,
object[]
para)
public
void
Init(Transform
rootLayer,
List<BasePanel>
childrenPanels,
object[]
para)
//關(guān)閉
public
void
Close()
//初始化時(shí)
public
virtual
void
OnInit()
//顯示時(shí)
public
virtual
void
OnShow(object[]
para)
//關(guān)閉時(shí)
public
virtual
void
OnClose()
}
PanelManager作為所有Panel的管理類,存儲(chǔ)并管理所有的UI界面和子UI界面。提供界面的打開和關(guān)閉,并控制UI界面在其生命周期內(nèi)的運(yùn)行邏輯。public
static
class
PanelManager
{
//Layer
public
enum
Layer
public
enum
CanvasEnum
//層級(jí)列表
private
static
Dictionary<Layer,
Dictionary<int,
Transform>>
layers;
//面板列表
public
static
Dictionary<string,
BasePanel>
panels
=
new
Dictionary<string,
BasePanel>();
public
static
Dictionary<GameObject,
List<BasePanel>>
childPanels
=
new
Dictionary<GameObject,
List<BasePanel>>();
//結(jié)構(gòu)
public
static
Transform
root;
public
static
List<Transform>
canvas;
public
static
FirstFacade
firstFacade;
public
static
SecondFacade
secondFacade;
//初始化
public
static
void
Init()
//打開面板
public
static
void
Open<T>(params
object[]
para)
where
T
:
BasePanel
public
static
void
AddChildPanel<T>(GameObject
parent,
object[]
para)
where
T
:
BasePanel
//關(guān)閉面板
public
static
void
Close(string
name)
public
static
void
Close<T>()
where
T
:
BasePanel
public
static
void
RemoveChildPanels(GameObject
parent)
public
static
void
RemoveChildPanelSelf(GameObject
parent,
string
Name)
}
UI界面是一個(gè)優(yōu)秀游戲中最基礎(chǔ)的、最不可獲取的部分,是玩家更直觀地游玩游戲地依托。為了更好地UI界面的管理和維護(hù),使用了基于PureMVC框架實(shí)現(xiàn)的UI界面設(shè)計(jì),因此需要有View、Model、Command三層分離的實(shí)現(xiàn)方式。接下來以游戲中的英雄羈絆界面為例,進(jìn)行UI界面實(shí)例的實(shí)現(xiàn)。View層的實(shí)現(xiàn)HeroTypePanel創(chuàng)建腳本HeroTypePanel繼承BasePanel,重寫基類的OnInit、OnShow、OnClose三個(gè)方法。在OnInit中,調(diào)用基類的OnInit的多態(tài)實(shí)現(xiàn)的另一個(gè)同名方法,填入?yún)?shù)分別為:SkinName(UI界面的GameObject的名稱,用于加載UI界面的圖形化表示的實(shí)例到游戲中),SceneIndex(GameObject加載時(shí)需要的參數(shù),表示來源于哪個(gè)資源包),PanelIndex(表示將這個(gè)UI界面添加到哪個(gè)層級(jí)(或是當(dāng)前UI界面是什么類型的):例如彈窗類型是永遠(yuǎn)覆蓋于所有的普通界面類型之上的),CanvasIndex(表示UI界面的目標(biāo)畫布)。在OnShow方法中:調(diào)用基類的OnShow方法,遍歷UI界面的所有子物體,保存到UIManager中。調(diào)用Fa?ade的Register進(jìn)行與這個(gè)UI界面相關(guān)的Mediator、Proxy、Command的注冊(cè)。在OnClose方法中:調(diào)用Fa?ade的SendNotification方法進(jìn)行廣播消息,移除該UI界面的子UI界面。調(diào)用Fa?ade的Remove進(jìn)行與該UI界面相關(guān)的MVC三層的移除。public
class
HeroTypePanel
:
BasePanel
{
public
override
void
OnInit()
{
base.OnInit("HeroTypePanel",
SceneEnum.GameScene,
PanelManager.Layer.Panel,
PanelManager.CanvasEnum.World);
}
public
override
void
OnShow(params
object[]
para)
{
base.OnShow(para);
PanelManager.secondFacade.Register(SecondFacade.RegisterPanel.HeroTypePanel,
skin.gameObject,
para);
}
public
override
void
OnClose()
{
base.OnClose();
PanelManager.secondFacade.SendNotification(NotificationCons.RemoveHeroTypeItemPanelMediator);
PanelManager.secondFacade.Remove(SecondFacade.RegisterPanel.HeroTypePanel);
}
}
HeroTypePanelMediator創(chuàng)建腳本HeroTypePanelMediator繼承Mediator。在構(gòu)造函數(shù)中:接收需要的參數(shù),并為Base傳入mediatorName(此Mediator類的索引)用于注冊(cè)。content是該UI界面的子UI界面的根結(jié)點(diǎn),通過UIManager獲取。重寫ListNotificationInterests和HandleNotification。用于Notification(PureMVC內(nèi)部提供的消息系統(tǒng))的注冊(cè)和接收。編寫其它的方法進(jìn)行UI界面的具體操作,通過消息接收觸發(fā)。public
class
HeroTypePanelMetiator
:
Mediator
{
public
const
string
mediatorName="HeroTypePanelMetiator";
private
GameObject
content;
private
string
curId;
public
HeroTypePanelMetiator(GameObject
root)
:
base(mediatorName)
{
content
=
UIManager.Instance.GetUI(,
"content_").gameObject;
}
public
override
string[]
ListNotificationInterests()
public
override
void
HandleNotification(INotification
notification)
public
void
UpdateHeroTypePanel(string
id,
Dictionary<int,int>
dict)
private
void
UpdateHeroTypeItem(string
id,
int
type,
int
count,
bool
newItem
=
false)
private
void
RemoveAllChildItemPanel()
private
void
OnChangePlayerForHeroTypePanelDisplay(string
id,
Dictionary<int,
int>
dict)
}
當(dāng)HeroTypePanelMediator觸發(fā)添加子界面的時(shí)候,會(huì)實(shí)例化一個(gè)HeroTypeItemPanel界面和實(shí)例化其對(duì)應(yīng)的Mediator、Proxy、Command。在HeroTypeItemPanelMediator的構(gòu)造函數(shù)中,通過UIManager獲取UI組件用于使用。public
HeroTypeItemPanelMediator(GameObject
_root,int
_heroTypeIndex)
:
base(string.Format("{0}{1}",
mediatorName,
_heroTypeIndex.ToString()))
{
heroTypeIndex
=
_heroTypeIndex;
root
=
_root;
heroTypeImg
=
UIManager.Instance.GetUI(,
"heroTypeImg_").GetComponent<Image>();
currentCountText
=
UIManager.Instance.GetUI(,
"currentCountText_").GetComponent<TextMeshProUGUI>();
heroTypeNameText
=
UIManager.Instance.GetUI(,
"heroTypeNameText_").GetComponent<TextMeshProUGUI>();
heroTypeCountText
=
UIManager.Instance.GetUI(,
"heroTypeCountText_").GetComponent<TextMeshProUGUI>();
scriptPanel
=
UIManager.Instance.GetUI(,
"scriptPanel_").gameObject;
scriptText
=
UIManager.Instance.GetUI(,
"scriptText_").GetComponent<TextMeshProUGUI>();
scriptMask
=
UIManager.Instance.GetUI(,
"scriptMask_").GetComponent<RectTransform>();
UIManager.Instance.RemoveUI();
MessageMgr.AddMessageListener<object[]>(MessageMgr.MessageType.DisplayHeroTypeScriptPanel,
DisplayHeroTypeScriptPanel);
InitPanel();
}Model層的實(shí)現(xiàn)1)HeroTypePanelProxy創(chuàng)建腳本HeroTypePanelProxy繼承Proxy,ProxyName是該P(yáng)roxy類的索引名稱。在其構(gòu)造函數(shù)中,初始化data并監(jiān)聽更新界面的命令。當(dāng)接收到更新命令,獲取新數(shù)據(jù)后,把數(shù)據(jù)更新到data中。至于View層的更新,可以是Proxy自己在更新data后隨即調(diào)用界面更新方法,或是其它方式通過Command調(diào)用Proxy中的界面更新方法,通過該方法可以調(diào)用Mediator注冊(cè)好的的Notification傳送data數(shù)據(jù)作為參數(shù)進(jìn)行View層的更新。public
class
HeroTypePanelProxy
:
Proxy
{
public
const
string
proxyName
=
"HeroTypePanelProxy";
private
HeroTypePanelData
data;
public
HeroTypePanelProxy()
:
base(proxyName)
{
data=new
Data();
MessageMgr.AddMessageListener<MsgBase>(MessageMgr.MessageType.OnMsgUpdateHeroTypePanel,
OnMsgUpdateHeroTypePanel);
}
public
override
void
OnRemove()
{
MessageMgr.RemoveMessageListener<MsgBase>(MessageMgr.MessageType.OnMsgUpdateHeroTypePanel,
OnMsgUpdateHeroTypePanel);
}
public
void
OnMsgUpdateHeroTypePanel(MsgBase
msgBase)
public
void
OnChangePlayerForHeroTypePanelDisplay(string
id)
}
2)HeroTypePanelDataHeroTypePanelData類的實(shí)例只是數(shù)據(jù)的存放,不做其它操作。public
class
HeroTypePanelData
{
Public
string
id;
public
Dictionary<string
,Dictionary<int,int>>
heroTypeIndexsDict;
public
HeroTypePanelData()
{
HeroTypeIndexsDict=new
Dictionary<string
,Dictionary<int,int>>();
}
}
Command層UpdateHeroTypePanelCommand繼承SimpleCommand,Execute方法實(shí)例化時(shí)便執(zhí)行,該命令執(zhí)行了調(diào)用HeroTypePanelProxy的OnMsgUpdateHeroTypePanel方法,進(jìn)行Model層數(shù)據(jù)的更新。public
class
UpdateHeroTypePanelCommand
:
SimpleCommand
{
public
override
void
Execute(INotification
notification)
{
MsgUpdateHeroTypePanel
data
=
notification.Body
as
MsgUpdateHeroTypePanel;
HeroTypePanelProxy
proxy
=
Facade.RetrieveProxy(HeroTypePanelPxyName)
as
HeroTypePanelProxy;
proxy.OnMsgUpdateHeroTypePanel(data);
}
}
UpdateHeroTypeItemCommand命令進(jìn)行View層數(shù)據(jù)的更新。public
class
UpdateHeroTypeItemCommand
:
SimpleCommand
{
public
override
void
Execute(INotification
notification)
{
HeroTypeItemData
data
=
notification.Body
as
HeroTypeItemData;
SendNotification(NotificationCons.UpdateHeroTypeItemInfoMediator,
data);
}
}ChangePlayerForHeroTypePanelCommand命令調(diào)用方法,執(zhí)行當(dāng)該界面的顯示目標(biāo)ID發(fā)生改變時(shí)的行為。public
class
ChangePlayerForHeroTypePanelCommand
:
SimpleCommand
{
public
override
void
Execute(INotification
notification)
{
HeroTypePanelProxy
proxy
=
Facade.RetrieveProxy(HeroTypePanelPxyName)
as
HeroTypePanelProxy;
proxy.OnChangePlayerForHeroTypePanelDisplay(notification.Body.ToString());
}
}
全局管理層Fa?ade新建類SecondFacade繼承Fa?ade管理第二個(gè)場(chǎng)景內(nèi)的所有UI界面的Fa?ade,方便不同場(chǎng)景內(nèi)UI界面的控制。在SecondFacade中編寫Register方法,做Switch-case判斷不同UI進(jìn)行不同的注冊(cè)內(nèi)容編寫。通過RegisterMediator、RegisterProxy、RegisterCommand進(jìn)行三層的注冊(cè)。removeFun是一個(gè)委托,為委托添加對(duì)應(yīng)的三層注銷方法并存儲(chǔ)到字典內(nèi),當(dāng)該界面移除時(shí),觸發(fā)該委托執(zhí)行,從而進(jìn)行對(duì)應(yīng)三層的注銷。以英雄羈絆UI界面為例,在初始化界面時(shí),F(xiàn)a?ade的注冊(cè)方法執(zhí)行,注冊(cè)進(jìn)行羈絆界面相關(guān)的Mediator、Proxy和Command,并且提前將卸載命令放入委托字典,在卸載UI界面時(shí)調(diào)用委托進(jìn)行卸載相關(guān)類。case
RegisterPanel.HeroTypePanel:
RegisterMediator(new
HeroTypePanelMetiator(root));
RegisterProxy(new
HeroTypePanelProxy());
RegisterCommand(NotificationCons.UpdateHeroTypeItemCommand,
()
=>
{
return
new
UpdateHeroTypeItemCommand();
});
RegisterCommand(NotificationCons.UpdateHeroTypePanelCommand,
()
=>
{
return
new
UpdateHeroTypePanelCommand();
});
RegisterCommand(NotificationCons.ChangePlayerForHeroTypePanelCommand,
()
=>
{
return
new
ChangePlayerForHeroTypePanelCommand();
});
removeFun
+=
()
=>
{
RemoveMediator(HeroTypePanelMetiator.mediatorName);
RemoveProxy(HeroTypePanelPxyName);
RemoveCommand(NotificationCons.UpdateHeroTypeItemCommand);
RemoveCommand(NotificationCons.UpdateHeroTypePanelCommand);
RemoveCommand(NotificationCons.ChangePlayerForHeroTypePanelCommand);
};
AddToRemoveDict(RegisterPanel.HeroTypePanel,removeFun);
break;
游戲資源游戲?qū)傩詳?shù)值游戲的人物是進(jìn)行對(duì)戰(zhàn)的英雄們,英雄們都有著相同的屬性類型(生命,力量,攻速等等),但是不同類型英雄的基礎(chǔ)屬性數(shù)值是不同的;英雄穿戴不同裝備獲得的屬性加成是不同的;不同英雄的上場(chǎng)組合形成的羈絆對(duì)英雄們的加成也是有區(qū)別的。為了更直觀全面地統(tǒng)攬游戲數(shù)值、更便捷地進(jìn)行游戲數(shù)值的修改和完善、更平衡舒適的游戲體驗(yàn),所以使用Excel進(jìn)行游戲數(shù)據(jù)的編輯。先在Excel表格中按照預(yù)先規(guī)定好的格式編寫好Excel表格。分別對(duì)英雄屬性、羈絆屬性、裝備屬性的表格進(jìn)行數(shù)據(jù)填寫。圖4-2游戲人物數(shù)值設(shè)定圖4-3羈絆屬性的設(shè)定圖4-4裝備的屬性設(shè)定在數(shù)據(jù)設(shè)定完成后Excel的數(shù)據(jù)保存為xlsx文件,通過LitJson類庫(kù),將不同表格依托不同的模板類轉(zhuǎn)換成對(duì)應(yīng)的json文件。json文件就是游戲程序可以直接讀取的游戲數(shù)據(jù),不同的模板類是表格格式設(shè)定的依托。表格的第2行的是模板類的屬性名稱,第3行代表屬性的類型,由此可以形成xlsx數(shù)據(jù)于C#類的轉(zhuǎn)換。xlsx數(shù)據(jù)轉(zhuǎn)C#類的過程:獲取要轉(zhuǎn)換Excel表格數(shù)據(jù)和對(duì)應(yīng)的模板類獲取模板類實(shí)例判斷模板類實(shí)例屬性并填入表格數(shù)據(jù)將該實(shí)例保存成json文件當(dāng)需要加載數(shù)據(jù)時(shí),通過json文件轉(zhuǎn)換成模板類(屬性類) private
static
void
ReadSingleSheet(Type
type,
DataTable
dataTable,
string
jsonPath)
{
int
rows
=
dataTable.Rows.Count;
int
Columns
=
dataTable.Columns.Count;
//UnityEngine.Debug.Log(rows
+
"行,"
+
Columns
+
"列");
//
工作表的行數(shù)據(jù)
DataRowCollection
collect
=
dataTable.Rows;
//
xlsx對(duì)應(yīng)的數(shù)據(jù)字段,規(guī)定是第二行
string[]
jsonFileds
=
new
string[Columns];
//
要保存成Json的obj
List<object>
objsToSave
=
new
List<object>();
for
(int
i
=
0;
i
<
Columns;
i++)
{
jsonFileds[i]
=
collect[1][i].ToString();
UnityEngine.Debug.Log(jsonFileds[i]);
}
//
從第三行開始
for
(int
i
=
3;
i
<
rows;
i++)
{
//
生成一個(gè)實(shí)例
object
objIns
=
type.Assembly.CreateInstance(type.ToString());
for
(int
j
=
0;
j
<
Columns;
j++)
{
//
獲取字段
FieldInfo
field
=
type.GetField(jsonFileds[j]);
if
(field
!=
null)
{
object
value
=
null;
try
//
賦值
{
value
=
Convert.ChangeType(collect[i][j],
field.FieldType);
}
catch
(InvalidCastException
e)
{
Console.WriteLine(e.Message);
string
str
=
collect[i][j].ToString();
string[]
strs
=
str.Split(',');
int[]
ints
=
new
int[strs.Length];
for
(int
k
=
0;
k
<
strs.Length;
k++)
{
ints[k]
=
int.Parse(strs[k]);
}
value
=
ints;
}
field.SetValue(objIns,
value);
}
else
{
UnityEngine.Debug.LogFormat("有無(wú)法識(shí)別的字符串:{0}",
jsonFileds[j]);
}
}
objsToSave.Add(objIns);
}
//
保存為Json
string
content
=
JsonMapper.ToJson(objsToSave);
SaveFile(content,
jsonPath);
}
英雄屬性的模板類如下,存儲(chǔ)英雄的基礎(chǔ)屬性數(shù)據(jù):
public
class
HeroInfoTMP
{
public
int
index;
public
string
type_name;
public
string
hero_name;
public
int
max_life;
public
int
mana_recover_value;
public
int
power;
public
int
spell_power;
public
int
power_defend;
public
int
spell_defend;
public
int
atk_cd;
public
bool
isNearAtk;
public
int
atk_distance;
public
int
price;
public
string
type;
}
每個(gè)不同的英雄的基礎(chǔ)屬性都是不同數(shù)值的模板類,為了便于管理,創(chuàng)建HeroInfoMgr單例類作為英雄屬性管理類,存儲(chǔ)了所有英雄的基礎(chǔ)屬性類型,提供提供使用索引查找某個(gè)英雄屬性的方法,便于游戲開發(fā)。以同樣的方式創(chuàng)建HeroTypeMgr和EquipInfoMgr作為英雄羈絆管理類和裝備管理類。準(zhǔn)備游戲資源人物模型作為英雄在游戲中的GameObject,為AI腳本、尋路腳本及其它繼承Mono(需要掛載在場(chǎng)景物體)的腳本提供搭乘實(shí)體。英雄在攻擊和釋放技能時(shí),都會(huì)生成不同的特效、觸發(fā)不同的音效。在英雄攻擊時(shí)實(shí)例化到場(chǎng)景內(nèi)。為了更好地區(qū)分各個(gè)資源,以及方便使用Addressables進(jìn)行調(diào)用。必須規(guī)范化地修改Prefabs的名稱(該名稱是英雄屬性填寫的依托),并添加可尋址標(biāo)記。圖4-5游戲人物模型圖4-6技能預(yù)設(shè)命名資源的管理Unity3D里有場(chǎng)景、模型、聲音、視頻、文本等許多不同類型的游戲資源,為了方便加載使用,我們需要對(duì)這些游戲資源進(jìn)行很好地管理。在之前的資源管理行為上,我們需要把資源添加標(biāo)記并打包成AssetBundle包,在加載資源時(shí),解壓AssetBundle包后讀取資源,在這種方式的操作過程中,繁瑣的操作步驟極易產(chǎn)生錯(cuò)誤。一個(gè)小小的錯(cuò)誤可能會(huì)導(dǎo)致資源無(wú)法加載,而且難以發(fā)現(xiàn)并修正。Unity3D官方也因此推出了新的資源管理系統(tǒng)Addressables。Addressables是對(duì)AssetBundle的高層封裝。優(yōu)化了游戲資源的打包和加載,極大地提升了游戲開發(fā)的整體效率。Addressables會(huì)對(duì)實(shí)例化的資源的個(gè)數(shù)進(jìn)行標(biāo)記,當(dāng)實(shí)例化的資源減少時(shí),減少標(biāo)記數(shù),如果標(biāo)記數(shù)為零,表示沒有該資源的引用,就會(huì)卸載掉該資源的內(nèi)存,從而也幫助開發(fā)者實(shí)現(xiàn)了內(nèi)存的優(yōu)化管理。由于Addressables并非Unity3D內(nèi)置的系統(tǒng),所以需要進(jìn)行導(dǎo)入,打開Unity3D的Window>PackageManager標(biāo)簽找到Addressable包后進(jìn)行導(dǎo)入。圖4-7Addressables包導(dǎo)入添加一個(gè)資源到Addressables時(shí),打開資源的Inspector界面,打上Addressables標(biāo)記并修改標(biāo)記名稱。在Addressables窗口中就會(huì)出現(xiàn)對(duì)應(yīng)的資源,我們還可以為不同的資源添加不同的標(biāo)簽。在資源加載時(shí),就是根據(jù)標(biāo)記名稱和標(biāo)簽進(jìn)行不同資源或是不同的資源包進(jìn)行的。圖4-8Addressables的標(biāo)記和標(biāo)簽要對(duì)標(biāo)記好的資源進(jìn)行加載時(shí),引用UnityEngine.AddressableAssets,Addressables類提供了多種不同的加載方式。通過Addressables.LoadAssetsAsync可以異步地加載多個(gè)labels下的同類型資源。Addressables.LoadAssetsAsync<T>(labels,
null,
loadmode).Completed
+=
(handles)
=>
{
handle
=
handles;
foreach
(var
item
in
handle.Result)
{
result.Add(item);
}
if
(++loadCounter
>=
loadCount)
{
loadFinish();
}
};
人物動(dòng)畫控制器Unity3D中Animator系統(tǒng)可以對(duì)游戲的模型動(dòng)畫進(jìn)行控制和管理。新建一個(gè)AnimatorController作為一個(gè)英雄的動(dòng)畫控制器,添加Idle、Move、Skill、Atk、Death四種狀態(tài)。將Idle狀態(tài)設(shè)置為默認(rèn)狀態(tài),設(shè)置好狀態(tài)間的過度條件:Move、Skill、Atk狀態(tài)只能與Idle狀態(tài)進(jìn)行轉(zhuǎn)換,任何狀態(tài)都能進(jìn)入死亡狀態(tài)。圖4-9動(dòng)畫控制器邏輯連接當(dāng)動(dòng)畫控制器進(jìn)行不同的狀態(tài)時(shí),觸發(fā)對(duì)應(yīng)的不同角色動(dòng)畫。為了區(qū)分游戲人物的個(gè)性,每個(gè)游戲的人物動(dòng)畫應(yīng)是不同的??梢允褂肁nimator系統(tǒng)中的AnimatorOverrideController對(duì)基礎(chǔ)的AnimatortControll進(jìn)行覆蓋,這樣就可以在不會(huì)改變?cè)械膭?dòng)畫間的邏輯情況下,改變具體的模型動(dòng)畫作為另一個(gè)英雄的動(dòng)畫控制器。圖4-10動(dòng)畫控制器的覆蓋游戲人物的創(chuàng)建游戲中使用單例類HeroMgr作為英雄的管理類,負(fù)責(zé)英雄的創(chuàng)建和管理。在英雄購(gòu)買UI界面進(jìn)行英雄購(gòu)買后,會(huì)創(chuàng)建相應(yīng)英雄到游戲場(chǎng)景中的等待區(qū)域供玩家操作。通過HeroMgr的CreateHeroToWait方法創(chuàng)建英雄,通過英雄索引加載對(duì)應(yīng)的屬性,依托英雄屬性的內(nèi)容,通過Addressables異步加載模型和技能,為英雄添加不同的AI控制腳本腳本等等。public
void
CreateHeroToWait(string
id,
int
hero_type_index)
{
//根據(jù)編號(hào)獲得英雄屬性信息
HeroInfoTMP
heroInfoTmp
=
heroInfoMag.GetHeroModel(hero_type_index);
string
model_name
=
heroInfoTmp.heroModel_name;
//加載英雄模型
ResManager.LoadInstantiateAsync(model_name,
hero_model
=>
{
hero_model.AddComponent<AIPath>();
hero_model.AddComponent<AIPathFinder>();
//加入字典
hero_dict[id].Add(hero_model);
//不同歸屬英雄添加不同的基礎(chǔ)腳本
string[]
ids
=
id.Split('_');
if
(id.Equals(NetManager.id)
||
ids[0].Equals(NetManager.id))
(GameManager.AddComponent(hero_model,
"HeroPositive")
as
HeroPositive).OnInit(id,
(sbyte)(indexTarget[id]++),
heroInfoTmp);
else
(GameManager.AddComponent(hero_model,
"HeroNegative")
as
HeroNegative).OnInit(id,
(sbyte)(indexTarget[id]++),
heroInfoTmp);
//英雄的血條,其它特效
ResManager.LoadInstantiateAsync(MainDefine.LifeBarPrefab,
hero_life_bar
=>
{……});
});
//在等待區(qū)放置hero
bool
isPosi
=
true;
PlacePlaneFind
parentPlane
=
PlacePlaneMag.placePlaneDict[id];
PlacePlaneFind
placePlaneTmp
=
parentPlane;
if
(progressMag.gameProgressState
!=
(byte)ProgressMag.GameProgressState.PlaceState)
{
for
(int
i
=
0;
i
<
progressMag.playerId.Count;
i++)
{
if
(NetManager.id.Equals(progressMag.playerId[i])
&&
!progressMag.enemyId[i].Equals("posi"))
{
isPosi
=
false;
placePlaneTmp
=
PlacePlaneMag.placePlaneDict[progressMag.enemyId[i]];
}
}
}
……
});
}
AI控制腳本游戲中的人物主要是自動(dòng)戰(zhàn)斗或者在等待區(qū)域的英雄們。玩家在游戲中扮演的是決策者,因此在游戲創(chuàng)建英雄時(shí)就會(huì)為英雄添加AI腳本,以便游戲中的英雄在戰(zhàn)斗階段進(jìn)行自動(dòng)戰(zhàn)斗。由于采用了單一客戶端進(jìn)行邏輯演算,所以AI腳本也會(huì)有主動(dòng)和被動(dòng)之分。使用HeroBase作為不同類型AI腳本的基類,保存了腳本對(duì)應(yīng)英雄的屬性數(shù)據(jù)、組件引用和其它控制腳本的引用等,為對(duì)單個(gè)英雄的相關(guān)操作提供了方法。HeroBase創(chuàng)建了虛函數(shù)方法表示英雄在不同時(shí)期的邏輯,方便控制英雄在其生命周期內(nèi)的行為。public
class
HeroBase
:MonoBehaviour
{
public
string
id
=
"";
private
sbyte
index;
public
sbyte
Index{}
public
string
only_index;//id-index
protected
float
syncInterval;//同步幀率
protected
bool
isNormalAtking
=
false;
protected
bool
isSkilling
=
false;
public
short
originalPos;//初始位置代碼:index*10+1/0
:準(zhǔn)備/等待
public
sbyte
ready_index_map=-1;//在映射數(shù)組中的位置,用于戰(zhàn)斗尋路
public
int
ready_index_map_before=-1;//預(yù)先占據(jù)位置
public
bool
canLevelUp;
public
HeroInfo
heroInfo;
public
AIPathFinder
aiPath;
public
EquipBase[]
equipArray;
public
Transform
target_enemy;
public
HeroLifeBarCtr
lifeBarCtr;
public
List<Transform>
enemy_list;
private
OtherFXCtr
fxCtr;
public
OtherFXCtr
otherFxCtr
protected
Animator
ani;
protected
AnimationClip[]
clips;
protected
Collider
col;
public
virtual
void
Init(HeroInfo
_heroInfo,string
_id,sbyte
_index)
……
}
主動(dòng)AI基礎(chǔ)腳本HeroBasePositive:繼承于HeroBase,在主機(jī)客戶端創(chuàng)建英雄時(shí)添加的AI腳本。負(fù)責(zé)在此客戶端執(zhí)行英雄的邏輯決策、在規(guī)定的時(shí)間間隔發(fā)送腳本對(duì)應(yīng)英雄的位置、狀態(tài)信息到其它被動(dòng)客戶端內(nèi)進(jìn)行同步。HeroBasePosition發(fā)送同步消息方法如下:public
void
SyncUpdate()
{
//時(shí)間間隔判斷
if
(Time.time
-
lastSendSyncTime
<
syncInterval)
{
return;
}
lastSendSyncTime
=
Time.time;
//發(fā)送同步協(xié)議
MsgMgr.SendMsgSyncTransform(id,transform,
(sbyte)(ani.GetBool("Move")
?
1
:
0),ready_index_map==-1?UpdateSelfReadyMapIndex():ready_index_map);
}
被動(dòng)AI基礎(chǔ)腳本HeroBaseNegative:繼承于HeroBase,在主機(jī)客戶端以外的客戶端創(chuàng)建英雄時(shí)添加的AI腳本,不會(huì)進(jìn)行游戲人物的邏輯決策,只是通過解析接收到的協(xié)議對(duì)場(chǎng)景中的人物的行為狀態(tài)進(jìn)行調(diào)整。HeroBaseNegative的接收并解析人物移動(dòng)協(xié)議的方法如下: public
void
OnMsgSyncTransform(MsgBase
msgBase)
{
MsgSyncTransform
msg
=
(MsgSyncTransform)msgBase;
if
(msg.id.Equals(id)&&msg.index.Equals(Index)&&heroInfo.isPositive==false)
{
ani.SetBool("Move",
msg.move.Equals(0)
?
false
:
true);
//預(yù)測(cè)位置
Vector3
pos
=
new
Vector3(msg.x
,
msg.y,
msg.z);
Vector3
rot
=
new
Vector3(transform.rotation.x,
msg.ey,
transform.rotation.z);
forecastPos
=
pos
+
2
*
(pos
-
lastPos);
forecastRot
=
rot
+
2
*
(rot
-
lastRot);
//更新
lastPos
=
pos;
lastRot
=
rot;
forecastTime
=
Time.time;
PlacePlaneFind
plane
=
PlacePlaneMag.placePlaneDict[PlacePlaneMag.GetTargetPlaneId(msg.id)];
if
(ready_index_map
!=
-1)
{
plane.all_ready_place_map[ready_index_map]
=
null;
}
ready_index_map
=
msg.ready_index_map;
plane.all_ready_place_map[ready_index_map]
=
only_index;
}
}
游戲人物的邏輯決策只在主動(dòng)AI類HeroBasePositive的有限狀態(tài)機(jī)內(nèi)進(jìn)行判斷和控制,在HeroBasePosition中初始化有限狀態(tài)機(jī),為狀態(tài)機(jī)添加多個(gè)不同時(shí)刻的狀態(tài),對(duì)應(yīng)HeroBase中的不同行為。通過SetCurrentIndex方法使?fàn)顟B(tài)機(jī)進(jìn)行空狀態(tài),為運(yùn)行做準(zhǔn)備。public
void
InitFsmsys()
{
fsmsys
=
new
FSMSystemN();
NullState
nullState
=
new
NullState(this);
FightStateBase
fightStateBase
=
new
FightStateBase(this);
IdleState
idleState
=
new
IdleState(this);
MoveState
moveState
=
new
MoveState(this);
AtkState
atkState
=
new
AtkState(this);
DeathState
deathState
=
new
DeathState(this);
fsmsys.AddState(nullState);
溫馨提示
- 1. 本站所有資源如無(wú)特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- SB/T 11235-2023人像攝影服務(wù)機(jī)構(gòu)誠(chéng)信評(píng)價(jià)規(guī)范
- 2025年軍隊(duì)文職人員招聘之軍隊(duì)文職管理學(xué)與服務(wù)全真模擬考試試卷A卷含答案
- 2025年軍隊(duì)文職人員招聘之軍隊(duì)文職管理學(xué)提升訓(xùn)練試卷B卷附答案
- 新泰數(shù)學(xué)初一試題及答案
- 安全防火知識(shí)培訓(xùn)課件
- 2025年黨史競(jìng)賽知識(shí)題庫(kù)50題及答案
- 監(jiān)理基礎(chǔ)知識(shí)培訓(xùn)課件
- 人工智能醫(yī)療輔助系統(tǒng)應(yīng)用及操作指南
- 唐宋八大家之一王安石介紹與作品欣賞教案
- 公司股份制改革法律文件匯編手冊(cè)
- 2024年鄭州市公安機(jī)關(guān)招聘警務(wù)輔助人員筆試真題
- 2025年貴州貴安新區(qū)產(chǎn)業(yè)發(fā)展控股集團(tuán)有限公司招聘筆試參考題庫(kù)附帶答案詳解
- 2.3品味美好情感 課 件 -2024-2025學(xué)年統(tǒng)編版道德與法治七年級(jí)下冊(cè)
- ACLS-PC-SA課前自我測(cè)試試題及答案
- 第十四章磨削及砂輪課件
- 水泥企業(yè)化驗(yàn)室控制組試題(庫(kù))
- 肇慶市勞動(dòng)合同
- 電力施工安全技術(shù)交底記錄表
- E4A使用手冊(cè)(DOC)
- (民法典版)離婚登記申請(qǐng)受理回執(zhí)單
- 食品質(zhì)量控制管理方案
評(píng)論
0/150
提交評(píng)論