<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
大家好,老胡又和大家見面了。首先承認今天的部落格有點標題黨了,人生是沒有存檔,也沒有後悔藥的。有存檔和後悔藥的,那是遊戲,不知道這是不是遊戲讓人格外放鬆的原因之一。
今天恰逢端午放假,就讓我們來試著做一個小遊戲吧,順帶看看備忘錄模式是如何在這種情況下面工作的。
這是一個簡單的打怪遊戲,有玩家,有怪獸,玩家作為主角光環,有如下三個特殊能力
首先是角色類,角色類提供玩家和怪獸最基本的抽象,比如血量、攻擊力、攻擊和治療。(對於怪獸來說,治療是沒有提供實現的,壞人肯定不能再治療了)
class Character { public int HealthPoint { get; set; } public int AttackPoint { get; set; } public virtual void AttackChracter(Character opponent) { opponent.HealthPoint -= this.AttackPoint; if (opponent.HealthPoint < 0) { opponent.HealthPoint = 0; } } public virtual void Cure() { //故意留空給子類實現 } }
玩家實現了治療功能並且有暴擊機率。
class Player : Character { private float playerCriticalPossible; public Player(float critical) { playerCriticalPossible = critical; } public override void AttackChracter(Character opponent) { base.AttackChracter(opponent); Console.WriteLine("Player Attacked Monster"); Random r = new Random(); bool critical = r.Next(0, 100) < playerCriticalPossible * 100; if (critical) { base.AttackChracter(opponent); Console.WriteLine("Player Attacked Monster again"); } } public override void Cure() { Random r = new Random(); HealthPoint += r.Next(5, 10); Console.WriteLine("Player cured himself"); } }
怪獸沒有治療能力但是有一定的機率丟失攻擊目標。
class Monster : Character { private float monsterMissingPossible; public Monster(float missing) { monsterMissingPossible = missing; } public override void AttackChracter(Character opponent) { Random r = new Random(); bool missing = r.Next(0, 100) < monsterMissingPossible * 100; if (missing) { Console.WriteLine("Monster missed it"); } else { base.AttackChracter(opponent); Console.WriteLine("Monster Attacked player"); } } }
遊戲類負責範例化玩家和怪獸、記錄回合數、判斷遊戲是否結束,暴露可呼叫的公共方法給遊戲操作類。
class Game { private Character m_player; private Character m_monster; private int m_round; private float playerCriticalPossible = 0.6f; private float monsterMissingPossible = 0.2f; public Game() { m_player = new Player(playerCriticalPossible) { HealthPoint = 15, AttackPoint = 2 }; m_monster = new Monster(monsterMissingPossible) { HealthPoint = 20, AttackPoint = 6 }; } public bool IsGameOver => m_monster.HealthPoint == 0 || m_player.HealthPoint == 0; public void AttackMonster() { m_player.AttackChracter(m_monster); } public void AttackPlayer() { m_monster.AttackChracter(m_player); } public void CurePlayer() { m_player.Cure(); } public void BeginNewRound() { m_round++; } public void ShowGameState() { Console.WriteLine("".PadLeft(20, '-')); Console.WriteLine("Round:{0}", m_round); Console.WriteLine("player health:{0}", "".PadLeft(m_player.HealthPoint, '*')); Console.WriteLine("monster health:{0}", "".PadLeft(m_monster.HealthPoint, '*')); } }
在我們這個簡易遊戲中,沒有UI程式碼,遊戲操作類負責在使用者輸入和遊戲中搭建一個橋樑,解釋使用者的輸入。
class GameRunner { private Game m_game; public GameRunner(Game game) { m_game = game; } public void Run() { while (!m_game.IsGameOver) { m_game.BeginNewRound(); bool validSelection = false; while (!validSelection) { m_game.ShowGameState(); Console.WriteLine("Make your choice: 1. attack 2. Cure"); var str = Console.ReadLine(); if (str.Length != 1) { continue; } switch (str[0]) { case '1': { validSelection = true; m_game.AttackMonster(); break; } case '2': { validSelection = true; m_game.CurePlayer(); break; } default: break; } } if(!m_game.IsGameOver) { m_game.AttackPlayer(); } } } }
使用者端的程式碼就非常簡單了,只需要範例化一個遊戲操作類,然後讓其執行就可以了。
class Program { static void Main(string[] args) { Game game = new Game(); GameRunner runner = new GameRunner(game); runner.Run(); } }
試著執行一下,
看起來一切都好。
雖然遊戲可以正常執行,但是總感覺還是少了點什麼。嗯,存檔功能,一個遊戲沒有存檔是不健全的,畢竟,人生雖然沒有存檔,但是遊戲可是有的!讓我們加上存檔功能吧,首先想想怎麼設計。
首先我們要明確,有哪些資料是需要存檔的,在這個遊戲中,玩家的生命值、攻擊力、暴擊率;怪獸的生命值、攻擊力和丟失率,遊戲的回合數,都是需要儲存的物件。
這是一個需要仔細思考的地方,一般來說,需要考慮以下幾個地方:
這個時候應該是主角出場的時候了。看看備忘錄模式的定義
在不破壞封閉的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態。這樣以後就可將該物件恢復到原先儲存的狀態
再看看UML,
看起來完全符合我們的需求啊,Originator就是遊戲類,知道如何創造存檔和從存檔中恢復狀態,Memento類就是存檔類,Caretaker是一個新類,負責儲存存檔。
經過思考,我們決定採取備忘錄模式,同時加入以下措施:
interface IGameSave { }
該類存放在game裡面,無壓力地在不破壞封裝的情況下存取game私有欄位
private class GameSave : IGameSave { public int PlayerHealth { get; set; } public int PlayerAttack { get; set; } public float PlayerCritialAttackPossible { get; set; } public int MonsterHealth { get; set; } public int MonsterAttack { get; set; } public float MonsterMissingPossible { get; set; } public int GameRound { get; set; } }
在game中新增建立存檔和從存檔恢復的程式碼,在從存檔恢復的時候,使用了向下轉型,因為從存檔管理器讀出來的只是空介面而已
public IGameSave CreateSave() { var save = new GameSave() { PlayerHealth = m_player.HealthPoint, PlayerAttack = m_player.AttackPoint, PlayerCritialAttackPossible = playerCriticalPossible, MonsterAttack = m_monster.AttackPoint, MonsterHealth = m_monster.HealthPoint, MonsterMissingPossible = monsterMissingPossible, GameRound = m_round }; Console.WriteLine("game saved"); return save; } public void RestoreFromGameSave(IGameSave gamesave) { GameSave save = gamesave as GameSave; if(save != null) { m_player = new Player(save.PlayerCritialAttackPossible) { HealthPoint = save.PlayerHealth, AttackPoint = save.PlayerAttack }; m_monster = new Player(save.MonsterMissingPossible) { HealthPoint = save.MonsterHealth, AttackPoint = save.MonsterAttack }; m_round = save.GameRound; } Console.WriteLine("game restored"); }
新增一個類專門管理存檔,此類非常簡單,只有一個存檔,要支援多存檔可以考慮使用List
class GameSaveStore { public IGameSave GameSave { get; set; } }
首先在遊戲操作類中新增一個存檔管理器
private GameSaveStore m_gameSaveStore = new GameSaveStore();
接著修改Run方法新增使用者操作
public void Run() { while (!m_game.IsGameOver) { m_game.BeginNewRound(); bool validSelection = false; while (!validSelection) { m_game.ShowGameState(); Console.WriteLine("Make your choice: 1. attack 2. Cure 3. Save 4. Load"); var str = Console.ReadLine(); if (str.Length != 1) { continue; } switch (str[0]) { case '1': { validSelection = true; m_game.AttackMonster(); break; } case '2': { validSelection = true; m_game.CurePlayer(); break; } case '3': { validSelection = false; m_gameSaveStore.GameSave = m_game.CreateSave(); break; } case '4': { validSelection = false; if(m_gameSaveStore.GameSave == null) { Console.WriteLine("no save to load"); } else { m_game.RestoreFromGameSave(m_gameSaveStore.GameSave); } break; } default: break; } } if(!m_game.IsGameOver) { m_game.AttackPlayer(); } } }
注意,上面的3和4是新新增的存檔相關的操作。試著執行一下。
看起來一切正常,這樣我們就使用備忘錄模式,完成了存檔讀檔的功能。
這就是備忘錄模式的使用,如果大家以後遇到這種場景
那麼就可以考慮使用這個模式。
遊戲有存檔,人生沒存檔,願我們把握當下,天天努力。
祝大家端午安康,更多關於C#備忘錄設計模式的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45