最近在大連的同事強(qiáng)力推薦我玩 爐石傳說(shuō),一個(gè)卡牌游戲。加上五一放一個(gè)很長(zhǎng)很長(zhǎng)的假期,為了磨練自己,決定嘗試開(kāi)發(fā)一個(gè)C#的爐石傳說(shuō)。
這件事情有人已經(jīng)干過(guò)了,開(kāi)發(fā)了一個(gè)網(wǎng)頁(yè)版的爐石,但是貌似不能玩。。。。
http://cnodejs.org/topic/529c1366a6957a0809485f3d
如果這位同志看到這篇文章,請(qǐng)一定和我聯(lián)系??!
rudermail@QQ.com或Q我377372779
第一天
開(kāi)始學(xué)習(xí)爐石傳說(shuō)的玩法,最好的方法是不停的玩游戲。
一個(gè)應(yīng)用是否邏輯清晰,取決于你對(duì)于業(yè)務(wù)的了解程度,一般到開(kāi)發(fā)后期發(fā)現(xiàn)有些邏輯內(nèi)聚和耦合發(fā)生問(wèn)題,往往都是前期對(duì)于業(yè)務(wù)的理解不夠透徹。
很多開(kāi)發(fā)都往往是隨著業(yè)務(wù)邏輯的了解,進(jìn)行不停的重構(gòu),當(dāng)然,這個(gè)也是一個(gè)必然的過(guò)程,但是如果能夠在前期就了解業(yè)務(wù)的話(huà),則可以節(jié)約后期大量的時(shí)間。
由于長(zhǎng)期做對(duì)日軟件的緣故,式樣書(shū),設(shè)計(jì)書(shū)先行是根深蒂固的思想。所有設(shè)想都先以文字或者偽代碼的形式寫(xiě)下來(lái),進(jìn)行一些假想的驗(yàn)證。
整個(gè)項(xiàng)目的平衡感,脈絡(luò),各個(gè)模塊,層次結(jié)構(gòu)都在這個(gè)時(shí)候定下來(lái)。這個(gè)時(shí)候是修改成本最低的階段,等到后期這些模塊再重新劃分,風(fēng)險(xiǎn)就高了。
通過(guò)第一天的學(xué)(you)習(xí)(xi),大概整理除了一個(gè)脈絡(luò):
(文字版本的不是很好看,下面的Excel版本,瀏覽器也看不到。。)

卡牌基礎(chǔ)
法術(shù)卡牌
熟讀各種法術(shù)牌,講法術(shù)牌分類(lèi)
隨從卡牌
各種特性的整理,可以參考各種網(wǎng)絡(luò)上的資料
武器卡牌
比較簡(jiǎn)單的類(lèi)型
游戲環(huán)境
英雄
生命值
基本技能
武器
牌堆
套牌
手牌
手里的牌
戰(zhàn)場(chǎng)
7個(gè)位置的隨從
法力水晶
由于某些卡牌會(huì)改變水晶,也為了細(xì)化系統(tǒng),法力水晶升級(jí)為一個(gè)獨(dú)立的類(lèi)
第二天
開(kāi)始進(jìn)行Coding。由于英語(yǔ)不是很好,有一些單詞不知道,然后開(kāi)始中英文夾雜編碼。
很久前,也討論過(guò)中文編程的問(wèn)題,其實(shí)很多變量,用中文還是英語(yǔ)完全沒(méi)有限制。
寫(xiě)代碼只要能讓自己和維護(hù)的人讀得懂就可以了。畢竟即使你用英語(yǔ)變量,你的注釋還是中文的。。。
當(dāng)然,如果你想讓代碼能夠國(guó)際化,特別是開(kāi)源項(xiàng)目,能用標(biāo)準(zhǔn)的英語(yǔ)來(lái)寫(xiě)代碼是極好的。
NUnit用的不是很好,所以,自己寫(xiě)了一些GUI的界面來(lái)做一些簡(jiǎn)單的UT測(cè)試。

第三天
代碼的重構(gòu),設(shè)計(jì)書(shū)和代碼的同步。
很多項(xiàng)目,在一開(kāi)始的時(shí)候還有設(shè)計(jì)書(shū),然后在開(kāi)發(fā)的時(shí)候,往往重構(gòu)好代碼后,設(shè)計(jì)書(shū)還是重構(gòu)之前的樣子。
IDE可以自動(dòng)重構(gòu)代碼,但是不能自動(dòng)重構(gòu)設(shè)計(jì)書(shū)。。。。
國(guó)內(nèi)項(xiàng)目不注重文檔,所以這種情況很常見(jiàn)。日系的開(kāi)發(fā),設(shè)計(jì)書(shū)則相當(dāng)重要,一個(gè)是為了日后維護(hù)能有個(gè)依據(jù),二是為了能夠明確責(zé)任。
這個(gè)地方為什么要修改,對(duì)于整體項(xiàng)目有什么影響,都能從設(shè)計(jì)書(shū)的修改履歷中看出端倪。
代碼和設(shè)計(jì)書(shū)同步的時(shí)候,也是一個(gè)反思的機(jī)會(huì),看看現(xiàn)階段寫(xiě)的代碼,是不是很干凈優(yōu)雅,
往往將代碼轉(zhuǎn)換為設(shè)計(jì)書(shū)的時(shí)候,可以看到代碼的問(wèn)題。特別是代碼的一致性上,散落在不同地方的代碼,經(jīng)過(guò)整理,用#region歸納后,可以看到很多問(wèn)題。

第四天
爐石C#版本不是短時(shí)間內(nèi)可以完成的,在完成整個(gè)爐石之前,可以考慮用當(dāng)前的代碼,先制作一些小的工具。
一來(lái)可以拉攏人氣,隔一段時(shí)間有個(gè)小的可以檢證的成果物,不至于半途而廢;
二來(lái),小工具的制作也是為了爐石服務(wù)的,有些小工具的代碼也可以反饋到爐石主體代碼。
我向來(lái)反對(duì)一開(kāi)始就要做個(gè)了不起的東西,或者只開(kāi)發(fā)了不起的東西,忘記了留下二次開(kāi)發(fā)的接口或者周邊產(chǎn)品的接口。
魔法的定義
魔法類(lèi)型
攻擊
回復(fù)
召喚
卡牌 奧術(shù)智慧
變形 變羊術(shù)
水晶 幸運(yùn)幣
奧秘
魔法關(guān)系
或者 抉擇系:例如:抉擇: 對(duì)一個(gè)隨從造成3點(diǎn)傷害;或者造成1點(diǎn)傷害并抽一張牌。
并且 有副作用的魔法,例如:造成4點(diǎn)傷害,隨機(jī)棄一張牌。
目標(biāo)選擇模式
隨機(jī)
全體
指定
目標(biāo)選擇方向
本方
對(duì)方
無(wú)限制
目標(biāo)選擇角色
隨從
英雄
全體
標(biāo)準(zhǔn)效果點(diǎn)數(shù)
傷害效果點(diǎn)數(shù)、治療效果點(diǎn)數(shù)、抽牌數(shù)
實(shí)際效果點(diǎn)數(shù)
由于某些卡牌效果會(huì)影響效果點(diǎn)數(shù)
效果回?cái)?shù)
例如:奧術(shù)飛彈是3次1點(diǎn)傷害
附加信息
難以用上面的規(guī)則的卡牌,特殊的附加信息
奧術(shù)智慧的定義:
1.有一個(gè)效果:抽兩張牌
2.成本是1點(diǎn)
3.對(duì)象時(shí)本方
/// <summary> /// 初始化奧術(shù)智慧 /// </summary> /// <returns></returns> public static Card.MagicCard Get奧術(shù)智慧() { Card.MagicCard 奧術(shù)智慧 = new Card.MagicCard(); 奧術(shù)智慧.SN = "M000002"; 奧術(shù)智慧.Name = "奧術(shù)智慧"; 奧術(shù)智慧.Description = "隨機(jī)抽兩張牌。"; 奧術(shù)智慧.Rare = Card.CardBasicInfo.稀有程度.綠色; //使用成本 奧術(shù)智慧.ActualCostPoint = 1; 奧術(shù)智慧.StandardCostPoint = 1; 奧術(shù)智慧.JoinType = Card.MagicCard.EffectJoinType.None; //隨機(jī)抽兩張牌 Card.MagicCardStockEffect cardStockEffect = new Card.MagicCardStockEffect(); cardStockEffect.StandardEffectPoint = 2; cardStockEffect.EffectCount = 1; cardStockEffect.EffectTargetSelectDirect = Card.CardUtility.TargetSelectDirectEnum.本方; 奧術(shù)智慧.FirstMagicDefine = cardStockEffect; return 奧術(shù)智慧; }
第五天
我一直在考慮,AI是不是能代替人。
爐石這樣的游戲,有許多常用的套路,只要組好了套牌,然后能夠?qū)⒑芏喑S玫目ㄅ平M合,優(yōu)先策略教授給AI,應(yīng)該可以做到和人對(duì)戰(zhàn)。
和國(guó)際象棋,圍棋比起來(lái),爐石這樣的游戲,勝利無(wú)非是:運(yùn)氣好,套牌組的合理,正確衡量場(chǎng)面上各種對(duì)方卡牌的威脅程度,熟練使用各種套路,有耐心,不犯低級(jí)錯(cuò)誤。
運(yùn)氣好,套牌組的合理,這個(gè)事情,前者人和AI都一樣,套牌可以人組好后直接給AI使用。
正確衡量場(chǎng)面上各種對(duì)方卡牌的威脅程度:這個(gè)也不難,其實(shí)卡牌的使用成本已經(jīng)是一個(gè)可以量化的威脅度指標(biāo)了。
熟練使用各種套路:對(duì)方出了一個(gè) 10/10 (合理的閥值)的家伙,如果有變羊術(shù),就變掉;對(duì)手一大堆血量3,4的隨從,就用清場(chǎng)的牌,這些套路也很直觀(guān)
有耐心,不犯低級(jí)錯(cuò)誤:這個(gè)是AI的長(zhǎng)處,AI絕對(duì)不會(huì)忘記還有魔法可以直接 打臉,還有可以使用的隨從去 打臉
當(dāng)然,對(duì)于頂級(jí)高手AI還不是可以簡(jiǎn)單的取勝,審時(shí)度勢(shì),及時(shí)調(diào)整戰(zhàn)略的能力,人還是有著無(wú)可比擬的優(yōu)勢(shì)。
第六天
看看客戶(hù)端和服務(wù)器端分工如何:
順便提一句,日常文檔的編寫(xiě),wps不比Office差,支持國(guó)貨

核心庫(kù) Card.DLL 還有客戶(hù)端,服務(wù)器,之間要連接起來(lái)
客戶(hù)端-服務(wù)器-核心庫(kù) 核心庫(kù):委托形式 /// <summary> /// 抽牌委托 /// </summary> /// <param name="IsFirst">先后手區(qū)分</param> /// <param name="magic">法術(shù)定義</param> public delegate List<CardBasicInfo> delegateDrawCard(Boolean IsFirst, int DrawCount); /// <summary> /// 抽牌魔法(服務(wù)器方法) /// </summary> public static delegateDrawCard DrawCard; 客戶(hù)端:實(shí)現(xiàn)委托 /// <summary> /// 初始化 /// </summary> public static void Init() { //抽牌的具體方法 CardUtility.DrawCard += DrawCardAtServer; } /// <summary> /// 抽牌(服務(wù)器方法) /// </summary> /// <returns></returns> public static List<String> DrawCardAtServer(Boolean IsFirst, int Count) { //向服務(wù)器提出請(qǐng)求,獲得牌 return GameStatus.DrawCard(IsFirst,Count); } 服務(wù)器端:實(shí)際操作牌堆 /// <summary> /// 抽牌 /// </summary> /// <param name="IsFirst"></param> /// <param name="Count"></param> /// <returns></returns> public static List<String> DrawCard(Boolean IsFirst, int Count) { var targetStock = IsFirst ? FirstCardStock : SecondCardStock; return targetStock.DrawCard(Count); } (調(diào)用Card核心庫(kù)方法) /// <summary> /// 抽卡 /// </summary> /// <param name="CardCount"></param> /// <returns></returns> public List<String> DrawCard(int CardCount) { List<String> newList = new List<String>(); for (int i = 0; i < CardCount; i++) { if (CardList.Count == 0) break; newList.Add(CardList.Pop()); } return newList; }
第七天
考慮服務(wù)器和客戶(hù)端的開(kāi)發(fā)。
客戶(hù)端-服務(wù)器通信
TCP協(xié)議,類(lèi)似于網(wǎng)站那樣的短連接。
玩家A 服務(wù)器消息區(qū) 玩家B
回合開(kāi)始 STARTTURN
使用卡牌A,造成結(jié)果B USE:A|EFFECT:B 每隔5秒從服務(wù)器端讀一次A的行為,改變當(dāng)前戰(zhàn)場(chǎng)狀態(tài),知道讀取到ENDTRUN消息
使用卡牌C,造成結(jié)果D USE:C|EFFECT:D
回合結(jié)束 ENDTURN
STARTTURN 回合開(kāi)始
每隔5秒從服務(wù)器端讀一次B的行為,改變當(dāng)前戰(zhàn)場(chǎng)狀態(tài),知道讀取到ENDTRUN消息 USE:A|EFFECT:B 使用卡牌A,造成結(jié)果B
USE:C|EFFECT:D 使用卡牌C,造成結(jié)果D
ENDTURN 回合結(jié)束
請(qǐng)求分類(lèi) (3位)
游戲
新建一個(gè)游戲 新建一個(gè)游戲
加入一個(gè)游戲 加入一個(gè)游戲
認(rèn)輸 認(rèn)輸,退出一個(gè)游戲
等待游戲列表 獲取等待加入者游戲的列表
確認(rèn)游戲啟動(dòng)狀態(tài) 確認(rèn)游戲是否處于啟動(dòng)狀態(tài)
是否為先手 是否為先手
動(dòng)作
抽牌 抽牌
回合結(jié)束 回合結(jié)束
行動(dòng) 改變戰(zhàn)場(chǎng)的動(dòng)作
下面這個(gè)鏈接是OneDriver上共享的設(shè)計(jì)書(shū),有些圖形對(duì)象無(wú)法在瀏覽器中顯示,不知道能不能通過(guò)下載的方式保存到本地,然后打開(kāi)。
點(diǎn)擊這里查看 onlne Excel 版本的 設(shè)計(jì)書(shū)
代碼在GitHub上面,不過(guò)為了幫MongoDB的項(xiàng)目拉人氣,所以,將代碼放到了MongoDB的解決方案里面了。
大家下載代碼的時(shí)候,順手點(diǎn)個(gè)贊吧 Star 一下
https://github.com/magicdict/MagicMongoDBTool
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注