[RTS] 遠(yuǎn)古帝國(guó)

游戲鏈接https://pan.baidu.com/s/1eSd4ZHg
這是我開(kāi)發(fā)的一款策略戰(zhàn)棋游戲[遠(yuǎn)古帝國(guó)]
他是我以前上學(xué)時(shí)經(jīng)常玩的一款游戲 雖然現(xiàn)在市面上有很多重置版但是我還是喜歡玩原版 因?yàn)槔锩娴膽?zhàn)斗數(shù)據(jù)非常平衡 你能從中獲得更多的樂(lè)趣
所以我盡量還原經(jīng)典 仔細(xì)分析數(shù)據(jù) 力求完美(上下左右是wsad或方向鍵控制 確認(rèn)鍵是空格或回車(chē))


首先是移動(dòng)范圍的渲染

?
這里的人物移動(dòng)范圍可以在ACGame.Sprite中的MoveRange里配置

?
然后是地圖 這張大地圖都是由15*15的小地圖拼接起來(lái)的

?
每張小地圖上都掛了ACGame.Terrain腳本
CustomDepth設(shè)置的是移動(dòng)到該地形需消耗的移動(dòng)點(diǎn)
Def則是角色在該地形上的防御加成

?
當(dāng)玩家點(diǎn)擊了目的地 角色便會(huì)沿著路線移動(dòng)過(guò)去

?

?
當(dāng)角色移動(dòng)完畢會(huì)跳出一個(gè)提示框顯示是否結(jié)束當(dāng)前移動(dòng)
如果角色移動(dòng)的目的地中有敵人則會(huì)出現(xiàn)攻擊選項(xiàng)

?
每個(gè)角色的攻擊范圍不同?可以在腳本中配置

?
如果角色附近有多個(gè)敵人 玩家可以按上下左右鍵選擇要攻擊的敵人

?
然后就可以開(kāi)始攻擊

?
玩家攻擊完成后敵人也會(huì)反擊

?
角色的攻擊力是一個(gè)范圍 在腳本中可以配置 每次會(huì)從中隨機(jī)取一個(gè)值

?
攻擊的計(jì)算公式是(攻擊方的攻擊力-敵人防御-地形防御加成)*攻擊方的血量百分比
這樣可以保證攻擊時(shí)血量較多的一方比較有利
玩家也可以利用山地 房屋等有防御加成的地形攻擊敵人
也可以先使用滿血的單位攻擊 然后再使用殘血單位補(bǔ)刀如果玩家或者敵人血量為0 則會(huì)爆出一塊墓碑

?
接下來(lái)是購(gòu)買(mǎi)界面

?
點(diǎn)擊下方左右箭頭可以在各個(gè)兵種之間切換 這里實(shí)現(xiàn)了無(wú)縫滾動(dòng)
然后是我的代碼
代碼一共分為兩大類(lèi) 分別叫abstract和business
abstract中存放的都是接口 抽象類(lèi) 和工具類(lèi) 它們不依賴(lài)于business?所以不僅可以運(yùn)用于當(dāng)前項(xiàng)目 也可以在其他項(xiàng)目中使用
business存放具體的業(yè)務(wù)邏輯 這里的代碼依賴(lài)關(guān)系比較復(fù)雜
這樣的分類(lèi)也是我研究了好久 我曾經(jīng)試過(guò)將所有的類(lèi)都面向接口 耦合也降到最低 但是這樣做沒(méi)有意義
因?yàn)榫退忝嫦蛄私涌?但最終還是要把各處代碼組合在一起 耦合還是存在 并且修改的話還是有可能修改好幾處地方
后來(lái)我取消了接口 又發(fā)現(xiàn)代碼變得更加難以維護(hù)
最后我做了分類(lèi) 因?yàn)榧热晃覜](méi)辦法把所有的類(lèi)全部獨(dú)立 也不能把所有的類(lèi)依賴(lài)在一起 那我就索性把獨(dú)立性較強(qiáng)的類(lèi)和處理業(yè)務(wù)邏輯的類(lèi)分開(kāi)
這樣就誕生了abstract和business而在實(shí)際編程中經(jīng)常會(huì)出現(xiàn)business的代碼寫(xiě)到一半發(fā)現(xiàn)之前寫(xiě)過(guò) 然后把重復(fù)的邏輯整理好放到abstract中
而放入abstract中的代碼基本不會(huì)再去放回business

?
在abstarct中有一個(gè)分類(lèi)是專(zhuān)門(mén)存放設(shè)計(jì)模式的
雖然里面就兩個(gè)類(lèi) 但代碼中遠(yuǎn)不止這兩種設(shè)計(jì)模式
放在這里僅僅是因?yàn)樗鼈冋麄€(gè)類(lèi)里都在做設(shè)計(jì)模式做的事
而且其他模式則和其他邏輯耦合在一起

?
首先是DynamicFactroy
他本質(zhì)是一個(gè)工廠模式 但是沒(méi)有一本書(shū)上會(huì)有這種工廠模式 這是我自創(chuàng)的
這里就做兩件事 先找字典中有沒(méi)有你要的對(duì)象 沒(méi)有就用反射創(chuàng)建一個(gè)

?
第二個(gè)是單例模式
我知道學(xué)過(guò)c++的人都會(huì)七八種單例模式的寫(xiě)法
但是這里我就按照unity的思路寫(xiě)一個(gè)就行了

?
還有一個(gè)非常棘手的問(wèn)題就是對(duì)象的初始化參數(shù)
按照我們以前的思路 要么把類(lèi)中需要初始化的數(shù)據(jù)在聲明時(shí)就賦值
要么寫(xiě)一個(gè)構(gòu)造器 但是在unity中 如果你的類(lèi)繼承了MonoBehaviour你就沒(méi)辦法使用有參構(gòu)造器了
因?yàn)閡nity要序列化你的腳本顯示在inspector上 它走的是無(wú)參構(gòu)造器 就算你寫(xiě)了有參構(gòu)造器他也不會(huì)走 而且會(huì)報(bào)警
我看到很多老外的代碼里直接寫(xiě)了個(gè)setter 我覺(jué)得這樣做違背了初始化這個(gè)詞的兩個(gè)初衷
首先破壞了封裝 你的內(nèi)部變量可以在對(duì)象創(chuàng)建后隨意修改
其次有空數(shù)據(jù)的風(fēng)險(xiǎn) 如果你的代碼沒(méi)有調(diào)用相關(guān)setter 編譯不會(huì)察覺(jué) 那你的變量就為null了
這里我的解決方法是寫(xiě)一個(gè)ResourceManager 你可以看到它使用了我的單例模式

?
然后在腳本執(zhí)行順序中把他放到第一個(gè)

?
這樣我就可以在代碼中的任何地方隨意調(diào)用初始化好的參數(shù)

?
但是你可以看到ResourceManager中有各種各樣的控件 光標(biāo) 相機(jī)等
我的項(xiàng)目有這些控件 而你的項(xiàng)目不大可能也有這些
所以ResourceManager應(yīng)該歸到business中
那如果ResourceManager是business了 那么abstract中繼承了MonoBehaviour的類(lèi)也想要初始化參數(shù)怎么辦
這里還有一個(gè)辦法
就是在這些類(lèi)中寫(xiě)一個(gè)抽象函數(shù)作為構(gòu)造函數(shù)這里拿一個(gè)放大特效腳本做例子它的效果是激活后 物體會(huì)從一個(gè)點(diǎn)慢慢的放大成原來(lái)的樣子
接下來(lái)說(shuō)說(shuō)狀態(tài)模式?里面存放的全是游戲業(yè)務(wù)邏輯
如果你按下一個(gè)鍵后需要一大堆if else 那么你就需要狀態(tài)模式
我發(fā)現(xiàn)HeadFirstParttern一書(shū)中的狀態(tài)模式有些不足
第一 他的狀態(tài)會(huì)頻繁的創(chuàng)建
第二 他的狀態(tài)切換必須經(jīng)過(guò)Update
而我的狀態(tài)對(duì)象的獲取是通過(guò)我的動(dòng)態(tài)工廠獲得的 同一個(gè)狀態(tài)只有一個(gè)對(duì)象
然后我的狀態(tài)切換并不是通過(guò)update切換的 而是通過(guò)GoNextState<T>()
這樣我可以在任何事件中切換狀態(tài)
我的狀態(tài)模式基類(lèi)

?
狀態(tài)子類(lèi) 可以看到當(dāng)子類(lèi)中觸發(fā)了點(diǎn)擊事件后會(huì)進(jìn)入MoveState

?
進(jìn)入MoveState后角色移動(dòng)完畢又進(jìn)入BehaviourState

?
在BehaviourState中玩家如果選擇移動(dòng)結(jié)束則回到InitialState 如果選擇攻擊 則進(jìn)入SelectEmyState

?
在這個(gè)項(xiàng)目里一共有八個(gè)狀態(tài)

?
他們之間的切換過(guò)程可以在inspector中可以看到

?
這里出現(xiàn)了一個(gè)GameManager 起始它的作用就是刷新游戲狀態(tài)
這樣我的游戲中只有一個(gè)update在運(yùn)行

?
接下來(lái)是最困難的地方 就是地形的遍歷
作為一個(gè)策略戰(zhàn)棋游戲 最必不可少的就是地圖遍歷
這里我使用了四叉樹(shù)
而這里的四叉樹(shù)和一些算法書(shū)上不一樣 一般數(shù)據(jù)結(jié)構(gòu)的節(jié)點(diǎn)都會(huì)先建立一個(gè)四叉樹(shù)節(jié)點(diǎn)類(lèi)
然后在類(lèi)中存一個(gè)泛型數(shù)據(jù) 但我覺(jué)得這樣用的時(shí)候不方便
所以我的寫(xiě)法是每一個(gè)節(jié)點(diǎn)都包含了它上下左右四個(gè)同類(lèi)型的節(jié)點(diǎn)

?
設(shè)計(jì)好了四叉樹(shù) 那么在遍歷前先要把他們連接起來(lái)
這里我寫(xiě)個(gè)接口IQuadNodeSetter用于設(shè)置四叉樹(shù)的四個(gè)節(jié)點(diǎn)

?
然后是定義位置接口IPositional?因?yàn)槲蚁胪ㄟ^(guò)物體的位置關(guān)系來(lái)連接

?
連接之前我會(huì)對(duì)所有的地形根據(jù)位置進(jìn)行排序 所以還要用到IComparable<IPositional>
所以一個(gè)節(jié)點(diǎn)連接的所有接口準(zhǔn)備齊了 把它們合在一起就是ILinkable接口

?
任何類(lèi)只要實(shí)現(xiàn)了ILinkable接口就可以在PLinkQuadNode中被連接成四叉樹(shù)

?
在unity的界面中是這樣的

?
點(diǎn)擊連接按鈕它就會(huì)連接子節(jié)點(diǎn)中所有實(shí)現(xiàn)ILinkable的節(jié)點(diǎn)

?
你可以看到我并沒(méi)有開(kāi)始運(yùn)行游戲 而Terrain中已經(jīng)有了值
這樣我就可以在游戲運(yùn)行之前就連接好四叉樹(shù) 不會(huì)在游戲中消耗性能
所以PLinkQuadNode中的P表示的是Preprocess
凡是這種腳本都放在Preprocess文件分類(lèi)中有了連接好的四叉樹(shù) 那么就可以遍歷了
這里我寫(xiě)了三種遍歷器 而且每種遍歷器都使用了策略模式和享元模式

?
第一個(gè)構(gòu)造器中使用的IIterateBehaviour接口用來(lái)定義遍歷時(shí)的行為

?
先實(shí)現(xiàn)接口

?
再把他們組合起來(lái)

?
最后呈現(xiàn)出來(lái)的效果

?

如果改變遍歷時(shí)的行為

?
就會(huì)這樣渲染

?
這里我還可以使用相同的遍歷行為 但使用不同的遍歷方法 這樣就從策略模式變成橋接模式了

?

?

?

?
這里我測(cè)試了遍歷的邏輯

?

?
我建立了一個(gè)測(cè)試文件夾 里面存放test代碼

?
每次遍歷到一個(gè)節(jié)點(diǎn) 就把它z軸提高 這樣我可以清除的看到是否有重復(fù)遍歷

?
最后是享元模式 每個(gè)遍歷器都可以選擇創(chuàng)建新的變量 也可以公用其他遍歷器實(shí)例的變量

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末窑多,一起剝皮案震驚了整個(gè)濱河市唯竹,隨后出現(xiàn)的幾起案子戈稿,更是在濱河造成了極大的恐慌呻此,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件啤斗,死亡現(xiàn)場(chǎng)離奇詭異棉饶,居然都是意外死亡厦章,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)照藻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)袜啃,“玉大人,你說(shuō)我怎么就攤上這事幸缕∪悍ⅲ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵发乔,是天一觀的道長(zhǎng)熟妓。 經(jīng)常有香客問(wèn)我,道長(zhǎng)栏尚,這世上最難降的妖魔是什么起愈? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮译仗,結(jié)果婚禮上告材,老公的妹妹穿的比我還像新娘。我一直安慰自己古劲,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布缰猴。 她就那樣靜靜地躺著产艾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪滑绒。 梳的紋絲不亂的頭發(fā)上闷堡,一...
    開(kāi)封第一講書(shū)人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音疑故,去河邊找鬼杠览。 笑死,一個(gè)胖子當(dāng)著我的面吹牛纵势,可吹牛的內(nèi)容都是我干的踱阿。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼钦铁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼软舌!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起牛曹,我...
    開(kāi)封第一講書(shū)人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤佛点,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體超营,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鸳玩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了演闭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片不跟。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖船响,靈堂內(nèi)的尸體忽然破棺而出躬拢,到底是詐尸還是另有隱情,我是刑警寧澤见间,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布聊闯,位于F島的核電站,受9級(jí)特大地震影響米诉,放射性物質(zhì)發(fā)生泄漏菱蔬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一史侣、第九天 我趴在偏房一處隱蔽的房頂上張望拴泌。 院中可真熱鬧,春花似錦惊橱、人聲如沸蚪腐。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)回季。三九已至,卻和暖如春正林,著一層夾襖步出監(jiān)牢的瞬間泡一,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工觅廓, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鼻忠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓杈绸,卻偏偏與公主長(zhǎng)得像帖蔓,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瞳脓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容