原創文章,轉載請保留作者署名!
最近做翻譯做的頭疼,疼過之餘,想想之前公司內組織的三國殺開發興趣小組在三國殺開發問題上幾乎又停滯了。於是又翻出來搞了搞,這次卻搞的有點像模像樣了,特地把思路和方法都共用出來,一起學習。
三國殺在上海是挺風靡的,如果你還不知道玩法和規則,請猛擊這裡。
今天要說的是實現的第一步。為了盡量簡化需求,我們最初要實現的東西也挺簡單,就是能支援兩個玩家自動對戰。具體的描述如下:在一個情境中設定兩個玩家,遊戲的牌堆中只有三種基本牌:殺、閃、桃。其中,只有殺和桃是屬於主動性遊戲牌,即可以在自己行動回合將其打出;閃是被動性遊戲牌,只有當別人對自己出殺的時候才可以出閃,否則扣除一點體力,而使用桃則為自己增加一點體力。同樣,為了簡化實現,只要有能使用的牌,就必須使用;而且,如果你的手牌數量大於體力值,則需要棄牌直到手牌數與體力值相等。在這種情況下,只要有一方體力為0,遊戲結束!
對需求進行整理,不難發現有以下需求待實現:
1、遊戲結束的條件:只要有玩家死亡即宣告結束。
2、牌堆的實現,注意牌堆中拿出的牌需要從牌堆中移除,以及洗牌的功能。
3、殺、閃、挑的邏輯實現。
4、從手牌中計算是否還有可用的牌。
5、玩家輪詢行動的實現。
在設計中,我的思路是這樣的:一局遊戲實際都在一個情境中進行,直到遊戲結束。因此首先有情境的定義:
/// <summary>
/// 表示遊戲情境。
/// </summary>
public class Scene
{
private readonly Player[] players;
}
其中情境中定義了遊戲玩家,因為情境中的遊戲玩家在情境產生之後就固定了,所以使用了readonly。
同樣,遊戲實際是個迴圈,結束的條件就是有玩家死亡。因此,先定義玩家死亡的條件:
private bool IsGameEnds()
{
return players.Where(p => p.IsDead).Count() > 0; // 如果選擇到IsDead的Player,遊戲結束
}
這樣,由於其中引用到了Player類,因此,建立玩家類,來對其按照需求進行設計。代碼如下:
Player
/// <summary>
/// 表示遊戲中的玩家。
/// </summary>
public class Player
{
private readonly List<GameCard> handCards = new List<GameCard>(20);
public Player(string name, byte maxHp)
{
this.Name = name;
this.Hp = maxHp;
this.MaxHp = maxHp;
}
/// <summary>
/// 表示該玩家的名稱。
/// </summary>
public string Name { get; private set; }
/// <summary>
/// 表示該玩家是否已經死亡。
/// </summary>
public bool IsDead
{
get { return Hp == 0; }
}
/// <summary>
/// 表示該玩家所選武將的當前HP.
/// </summary>
public byte Hp { get; private set; }
/// <summary>
/// 表示該玩家所選武將的最大HP.
/// </summary>
public byte MaxHp { get; private set; }
}
其中的handCards表示玩家手中可以拿的手牌數量,這裡設定上限為20張。那麼同樣要開始定義遊戲卡牌,代碼如下:
GameCard
/// <summary>
/// 表示遊戲中用作遊戲的卡牌。
/// </summary>
public abstract class GameCard
{
/// <summary>
/// 表示牌的花色。
/// </summary>
public CardMark Mark { get; protected set; }
/// <summary>
/// 表示牌的大小。
/// </summary>
public CardValue Value { get; protected set; }
/// <summary>
/// 表示牌的名稱。
/// </summary>
public string Name { get; protected set; }
/// <summary>
/// 對目標玩家使用卡牌。
/// </summary>
/// <param name="source">使用卡牌的來源物件。</param>
/// <param name="target">使用卡牌的目標對象。</param>
public abstract void Use(Player source, Player target);
}
直到現在,所需要的定義已經基本完成。這其中的代碼有很多不規範的地方,之所以展現出來,也是為了表現自己的思維過程,在後面會有很多對目前代碼的重構。
在這樣定義完了之後,下一步將開始仔細分析流程,按照流程先寫出最初能啟動並執行版本。(未完待續...)