WindowsFormsアプリケーションとして作成した「信長の野望」もどきを作ってみる とりあえず編をBlazor WebAssemblyで作成してみました。今回はとりあえず動いてくれればいいので改良は次回以降におこないます。
ただ作っただけです。期待はしないでください。
ますはIndex.razorのHTML部分を示します。
isGamingはゲーム中であるかどうかを示すフィールド変数です。ゲーム中はh1タグ部分を隠します。それから非表示にしたいボタンはdisplay:をnoneにすることで隠すことができます。
最初のボタンの状態は
1 2 3 4 |
btnStartDisplay = "inline-block"; // ゲーム開始ボタンのみを表示させる btnNextDisplay = "none"; btnDecisionDisplay = "none"; btnWaitDisplay = "none"; |
です。
フィールド変数 CurentCastleStatusListにそのときの城の状態をコピーしてforeach文でまわして城、城主、兵力を表にして表示させます。それからフィールド変数 yearAtring、turn、messageでその年、ターンが回ってくる城の順番、メッセージも表示させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
@page "/" @if (!isGaming) { <h1>信長の野望もどき</h1> } <button class="btn btn-primary" @onclick="btnGameStartClick" style="display:@btnStartDisplay">ゲーム開始</button> <p>@yearAtring</p> <p>@turn</p> <p>@message</p> <button class="btn btn-primary" @onclick="btnNextClick" style="display:@btnNextDisplay">次</button> <button class="btn btn-primary" @onclick="btnDecisionClick" style="display:@btnDecisionDisplay">決定</button> <button class="btn btn-primary" @onclick="btnWaitClick" style="display:@btnWaitDisplay">静観</button> <select name="area" @bind="@SelectedValue1"> @foreach (string str in StringChoices1) { <option value="@str">@str</option> } </select> <select name="area" @bind="@SelectedValue2"> @foreach (string str in StringChoices2) { <option value="@str">@str</option> } </select> @if (CurentCastleStatusList != null) { <div> <table class="table"> <thead> <tr> <th scope="col">ID</th> <th scope="col">城</th> <th scope="col">城主</th> <th scope="col">兵力</th> </tr> </thead> <tbody> @foreach (CastleStatus status in CurentCastleStatusList) { <tr> <td>@status.CastleID</td> <td>@status.CastleName</td> <td>@status.CastleOwner</td> <td>@status.SoldierCount</td> </tr> } </tbody> </table> </div> } else { <p>開始ボタンをクリックしてください</p> } |
CastleStatusクラスとSituationクラスは前回と同じです。
Index.razorのコード部分のフィールド変数部分をしめします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
@code { bool isGaming = false; // ゲーム中かそうでないか? // コンボボックスに表示させたい文字列 string[] StringChoices1 = { "", }; // 移動先の城の名前 string SelectedValue1 = ""; // 選択された移動先の城の名前 string[] StringChoices2 = { "", }; // 移動させたい人数 string SelectedValue2 = ""; // 選択された移動させる人数 string btnStartDisplay = "inline-block"; // 各ボタンを表示させるか否か string btnNextDisplay = "none"; string btnDecisionDisplay = "none"; string btnWaitDisplay = "none"; // ゲームのための乱数 Random random = new Random(); // 表示させたい城の状態 List<CastleStatus> CurentCastleStatusList = null; // 城の状態 public List<CastleStatus> CastleStatusList = new List<CastleStatus>(); // ターンがまわってくる順番 string turn = ""; // 表示させたいメッセージ string message = ""; // いま何年か? int year = 1570; string yearAtring = ""; public CastleStatus PlayerCastleStatus = null; // プレイヤーが操作しようとしている城 List<Situation> Situations = new List<Situation>(); // メッセージと城の状態のリスト } |
以下はゲーム開始時の処理です。ゲーム中であるというisGamingフラグをtrueにして、最初のターンを開始します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
@code { private void btnGameStartClick() { isGaming = true; InitCastleStatusList(); CastleStatusList = GetShuffledCastles(); // ターンがまわってくる順番を表示する string s = ""; foreach (CastleStatus status in CastleStatusList) { s += " >> " + status.CastleName; } str = s; Turn(); // 最初の年は1570年 year = 1570; yearAtring = year.ToString() + " 年"; } } |
InitCastleStatusListメソッドは城の状態を初期化します。CastleStatusListをいったんクリアして10個のCastleStatusオブジェクトを作成してCastleStatusListに格納します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
@code { public void InitCastleStatusList() { CastleStatusList.Clear(); CastleStatus status = new CastleStatus(); status.CastleID = 0; status.CastleName = "米沢城"; status.CastleOwner = "伊達"; status.SoldierCount = 5; CastleStatusList.Add(status); status = new CastleStatus(); status.CastleID = 1; status.CastleName = "春日山城"; status.CastleOwner = "上杉"; status.SoldierCount = 5; CastleStatusList.Add(status); status = new CastleStatus(); status.CastleID = 2; status.CastleName = "躑躅ヶ崎館"; status.CastleOwner = "武田"; status.SoldierCount = 5; CastleStatusList.Add(status); status = new CastleStatus(); status.CastleID = 3; status.CastleName = "小田原城"; status.CastleOwner = "北条"; status.SoldierCount = 5; CastleStatusList.Add(status); status = new CastleStatus(); status.CastleID = 4; status.CastleName = "岡崎城"; status.CastleOwner = "徳川"; status.SoldierCount = 5; CastleStatusList.Add(status); status = new CastleStatus(); status.CastleID = 5; status.CastleName = "岐阜城"; status.CastleOwner = "織田"; status.SoldierCount = 5; CastleStatusList.Add(status); status = new CastleStatus(); status.CastleID = 6; status.CastleName = "二条城"; status.CastleOwner = "足利"; status.SoldierCount = 5; CastleStatusList.Add(status); status = new CastleStatus(); status.CastleID = 7; status.CastleName = "吉田郡山城"; status.CastleOwner = "毛利"; status.SoldierCount = 5; CastleStatusList.Add(status); status = new CastleStatus(); status.CastleID = 8; status.CastleName = "岡豊城"; status.CastleOwner = "長宗我部"; status.SoldierCount = 5; CastleStatusList.Add(status); status = new CastleStatus(); status.CastleID = 9; status.CastleName = "内城"; status.CastleOwner = "島津"; status.SoldierCount = 5; CastleStatusList.Add(status); UpdateCastlesStatus(CastleStatusList); } } |
各城の行動順位を決めるためのGetShuffledCastlesメソッドは前回と同じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@code { public List<CastleStatus> GetShuffledCastles() { List<CastleStatus> ret = new List<CastleStatus>(); var castlesCopy = this.CastleStatusList.ToList(); while (castlesCopy.Count > 0) { int r = random.Next(castlesCopy.Count); ret.Add(castlesCopy[r]); castlesCopy.RemoveAt(r); } foreach (CastleStatus status in ret) status.Done = false; return ret; } } |
城の状態を表示させるUpdateCastlesStatusメソッドはCurentCastleStatusListに引数をコピーするだけです。このまえにボタンがクリックされているはずなので変更されたCurentCastleStatusListが表示されます。
1 2 3 4 5 6 |
@code { void UpdateCastlesStatus(List<CastleStatus> newStatuses) { CurentCastleStatusList = newStatuses.OrderBy(x => x.CastleID).ToList(); } } |
他の城のターンの場合、「決定」ボタンなどをクリックできないように非表示にします。ボタンの非表示のさせかたが違うだけで前回とほとんど同じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
@code { public void Turn() { // 「決定」ボタンなどをクリックできないように非表示にする btnDecisionDisplay = "none"; btnWaitDisplay = "none"; btnStartDisplay = "none"; // 次のメッセージを表示させるためのボタンをクリック可能にする btnNextDisplay = "inline-block"; // まだそのターンでなにもしていない城のリストを取得する List<CastleStatus> Statuses = CastleStatusList.Where(x => !x.Done).ToList(); foreach (var status in Statuses) { string owner = status.CastleOwner; int id = status.CastleID; // 城主が「織田」ならプレイヤーのターンである if (owner == "織田") { PlayerCastleStatus = status; string str1 = "順番がきました。ここは " + status.CastleName + " です。"; Situations.Add(new Situation(str1, CastleStatusList)); ShowSituation(); return; } bool doesContinue = EnemyTurn(status); if (!doesContinue) return; } PlayerCastleStatus = null; ShowSituation(); } bool EnemyTurn(CastleStatus status) { string owner = status.CastleOwner; int id = status.CastleID; status.Done = true; // 敵にとって自分の城 List<CastleStatus> myCastles = CastleStatusList.Where(x => x.CastleID != id && x.CastleOwner == owner).ToList(); // 敵にとって敵の城 List<CastleStatus> enemyCastles = CastleStatusList.Where(x => x.CastleID != id && x.CastleOwner != owner).ToList(); // 兵の人数を調べて0の場合はなにもしない、兵が存在する場合は戦争を仕掛ける if (status.SoldierCount > 0) { // 戦争に動員する兵の人数を求める。これが0のときはなにもしない int r1 = random.Next(status.SoldierCount + 1); if (r1 == 0) { string str1 = status.CastleName + " は静観します。"; Situations.Add(new Situation(str1, CastleStatusList)); return true; } // 戦争はどこにしかけるか? // 敵が所有する城を適当に選ぶ int r = random.Next(enemyCastles.Count); CastleStatus atackTarget = enemyCastles[r]; string oldOwner = atackTarget.CastleOwner; string str = status.CastleName + "の" + status.CastleOwner + "軍 が " + atackTarget.CastleName + " に攻め込みました。\n"; Situations.Add(new Situation(str, CastleStatusList)); // 戦争の結果を求める(勝ったらtrue、負けたらfalse) if (War(status, r1, atackTarget)) { string str1 = status.CastleOwner + " が " + atackTarget.CastleName + " を攻略しました。"; Situations.Add(new Situation(str1, CastleStatusList)); // 「織田」が負け、織田が所有する城が0になったらゲームオーバー if (CastleStatusList.Count(x => x.CastleOwner == oldOwner) == 0) { Situations.Add(new Situation(String.Format("{0}は滅亡しました!!!", oldOwner), CastleStatusList)); if (oldOwner == "織田") { Situations.Add(new Situation("ゲームオーバー", CastleStatusList)); ShowSituation(); return false; } } } else { string str1 = status.CastleOwner + " の攻撃は失敗しました。\n"; str1 += oldOwner + " は" + atackTarget.CastleName + " を守り抜きました。"; Situations.Add(new Situation(str1, CastleStatusList)); } } else { string str1 = status.CastleName + " には兵がいません。"; Situations.Add(new Situation(str1, CastleStatusList)); } return true; } } |
城攻め時のメソッドです。これも前回とほとんど同じですが、戦闘中のどこがどこを攻めているのかをわかりやすく表示させるようにしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
@code { bool War(CastleStatus attackingside, int attackingSoldiers, CastleStatus attackedside) { attackingside.SoldierCount -= attackingSoldiers; int attackedSoldiers = attackedside.SoldierCount; string str = String.Format("攻撃側:{0}と守備側:{1}の戦いです。", attackingSoldiers * 1000, attackedSoldiers * 1000); Situations.Add(new Situation(str, CastleStatusList)); while (attackingSoldiers > 0 && attackedSoldiers > 0) { int r = random.Next(3); if (r == 0) attackedSoldiers--; else attackingSoldiers--; string str1 = String.Format("攻撃側({0}軍):{1} 守備側({2}軍 {3}):{4}", attackingside.CastleOwner, attackingSoldiers * 1000, attackedside.CastleOwner, attackedside.CastleName, attackedSoldiers * 1000); Situations.Add(new Situation(str1, CastleStatusList)); } if (attackingSoldiers > 0) { attackedside.SoldierCount = attackingSoldiers; attackedside.CastleOwner = attackingside.CastleOwner; return true; } else { attackedside.SoldierCount = attackedSoldiers; return false; } } } |
ShowSituationメソッドはボタンをおすごとにメッセージを表示させます。メッセージを表示させる方法が違うだけでこれも前回とほとんどかわりません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
@code { void ShowSituation() { if (Situations.Count > 0) { string mes = Situations[0].Message; message = mes; UpdateCastlesStatus(Situations[0].CastleStatusList); Situations.RemoveAt(0); if (Situations.Count == 0) { bool gameClear = CastleStatusList.Count(x => x.CastleOwner != "織田") == 0; bool gameOver = CastleStatusList.Count(x => x.CastleOwner == "織田") == 0; if (gameClear || gameOver) { // ゲーム終了の場合 btnStartDisplay = "inline-block"; btnNextDisplay = "none"; btnDecisionDisplay = "none"; btnWaitDisplay = "none"; isGaming = false; return; } if (PlayerCastleStatus != null && !PlayerCastleStatus.Done) { PlayerAction(); } } } else if (Situations.Count == 0) { if (!CastleStatusList.Any(x => !x.Done)) { Situations.Add(new Situation("次の年になりました。", CastleStatusList)); CastleStatusList = GetShuffledCastles(); Turn(); // 年が変わったらその年のターンがまわってくる順番を表示させる string s = ""; foreach (CastleStatus status in CastleStatusList) { s += " >> " + status.CastleName; } str = s; year++; yearAtring = year.ToString() + " 年"; } } } } |
PlayerActionメソッドはプレイヤーに順番が回ってきたときに実行されるメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
@code { void PlayerAction() { btnStartDisplay = "none"; btnNextDisplay = "none"; btnWaitDisplay = "inline-block"; if (PlayerCastleStatus.SoldierCount > 0) { btnDecisionDisplay = "inline-block"; } else { btnDecisionDisplay = "none"; return; } List<CastleStatus> statuses = this.CastleStatusList.Where(x => x.CastleName != PlayerCastleStatus.CastleName).ToList(); statuses = statuses.OrderBy(x => x.CastleID).ToList(); List<string> strings1 = new List<string>(); foreach (CastleStatus status in statuses) { strings1.Add(status.CastleName); } StringChoices1 = strings1.ToArray(); List<string> strings2 = new List<string>(); for (int i = 1; i <= PlayerCastleStatus.SoldierCount; i++) { strings2.Add(i.ToString()); } StringChoices2 = strings2.ToArray(); SelectedValue1 = "米沢城"; SelectedValue2 = "1"; } } |
以下は[次][決定][静観]ボタンがクリックされたときに行なわれる処理です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
@code { private void btnDecisionClick() { int soldierCount = int.Parse(SelectedValue2); string castleName = SelectedValue1; CastleStatus status = CastleStatusList.First(x => x.CastleName == castleName); if (status.CastleOwner == "織田") { // 移動の場合 PlayerCastleStatus.SoldierCount -= soldierCount; status.SoldierCount += soldierCount; } else { // 戦争の場合(自動的に攻め方の城の人数は差し引かれる) string oldOwner = status.CastleOwner; if (War(PlayerCastleStatus, soldierCount, status)) { string str = "勝ったぞ。えいえいおー\n" + status.CastleName + " は織田家の城になりました。"; Situations.Add(new Situation(str, CastleStatusList)); if (CastleStatusList.Count(x => x.CastleOwner == oldOwner) == 0) Situations.Add(new Situation(String.Format("{0}家は滅亡しました!!!", oldOwner), CastleStatusList)); if (CastleStatusList.Count(x => x.CastleOwner != "織田") == 0) Situations.Add(new Situation("織田家が天下統一に成功しました!!!", CastleStatusList)); } else { string str = "残念ながら闘いに敗れました。\n"; Situations.Add(new Situation(str, CastleStatusList)); } } PlayerCastleStatus.Done = true; UpdateCastlesStatus(CastleStatusList); bool gameClear = CastleStatusList.Count(x => x.CastleOwner != "織田") == 0; if (gameClear) { btnStartDisplay = "none"; btnNextDisplay = "inline-block"; btnDecisionDisplay = "none"; btnWaitDisplay = "none"; ShowSituation(); } else Turn(); } void btnWaitClick() { PlayerCastleStatus.Done = true; PlayerCastleStatus.SoldierCount = 7; UpdateCastlesStatus(CastleStatusList); Turn(); } void btnNextClick() { ShowSituation(); } } |