「北海道大戦」カテゴリーアーカイブ

【北海道大戦】今後の実装にあたって。【設計のコツ】

たぶん、どんな感じで動かすか、と言うのをきちんと整理しておかないとごちゃごちゃしてくると思うので、

こんな感じのものをサクッと作成しました。

まぁ、フローチャートですわな。

ゲームの状態をステータス(status)で定義し、その状態によってループ処理とか、マウスクリック処理を変えていくように実装させて行く必要があると思います。

ゲーム開始時はstatus=1の状態で始まり、プレイヤーが担当する自治体を選択します。

選択したらゲームスタート。

自分の番が来るまで敵が行動するのですが、その状態がstatus=2です。

で、自分の番が来たらstatus=3に移行。

攻撃対象となる自治体を選択(クリック)します。

その後status=4に移行し、戦闘結果が表示されます。

その後クリックで再びstatus=2に移行し、以後、自治体が残り一つになるまでループします。

最終的に、自治体が残り一つになればstatus=5に遷移してゲーム終了。

とまぁ、サクッと説明しましたが、こういった整理が出来るかどうかで設計能力が問われるわけで。

「このステータスの時」「このイベントが起きると」「このアクションをする」

こんな感じで、図や表なんかに書き出してみると、きれいに整理できたりします。

案外こういうのが解決の近道だったりするので、困ったときは一度立ち止まって整理することをオススメします。

【北海道大戦】バトル処理を実装する。

前回までの状況はこちら。

最新ソースはこちら(gitHub)

https://github.com/takishita2nd/HokkaidoWar

前回は対戦相手を選択する処理を作成したので、

今回は実際にバトルを行う処理を作成します。

    class Battle
    {
        public void NextTurn()
        {
            if (lastDeffece != null)
            {
                lastDeffece.ClearPaint();
            }
            if (lastAttack != null)
            {
                lastAttack.ClearPaint();
            }

            var targets = _cities[cityCnt].GetLinkedCities();
            var r = Singleton.GetRandom();
            int targetIdx = r.Next(0, targets.Count + 1);
            lastAttack = _cities[cityCnt];
            lastAttack.PaintAttackColor();

            var info = Singleton.GetGameProcessInfomation();
            if(targetIdx >= targets.Count)
            {
                info.ShowText(lastAttack.GetPosition(), string.Format("{0} turn {1} / {2} {3}",
                    turn, cityCnt + 1, _cities.Count, lastAttack.Name));
            }
            else
            {
                lastDeffece = targets[targetIdx];
                lastDeffece.PaintDeffenceColor();
                float attack = lastAttack.Population * (float)(r.Next(5, 30) / 10.0);
                float deffence = lastDeffece.Population * (float)(r.Next(5, 30) / 10.0);
                if(attack > deffence)
                {
                    info.ShowText(lastAttack.GetPosition(), string.Format("{0} turn {1} / {2} {3}\r\ntarget {4} \r\n{5} vs {6}\r\nwin",
                        turn, cityCnt + 1, _cities.Count, lastAttack.Name, lastDeffece.Name, (int)attack, (int)deffence));
                    lastAttack.CombinationCity(lastDeffece);
                    _cities.Remove(lastDeffece);
                    lastDeffece = null;
                }
                else
                {
                    info.ShowText(lastAttack.GetPosition(), string.Format("{0} turn {1} / {2} {3}\r\ntarget {4} \r\n{5} vs {6}\r\nlose",
                        turn, cityCnt + 1, _cities.Count, lastAttack.Name, lastDeffece.Name, (int)attack, (int)deffence));
                }
            }

            cityCnt++;
            if(cityCnt >= _cities.Count)
            {
                _cities = cityRandomReplace(_cities);
                cityCnt = 0;
                turn++;
            }
        }
    class City
    {
        public List<Map> GetMaps()
        {
            return _maps;
        }

        public void CombinationCity(City lose)
        {
            addMaps(lose.GetMaps());
            _population += lose.Population;
        }

        private void addMaps(List<Map> maps)
        {
            foreach(var m in maps)
            {
                m.SetCity(this);
            }
            _maps.AddRange(maps);
        }

とりあえず、0.5倍~3倍の乱数で戦力値に補正をかけて勝敗を決めます。

そして、攻撃側が勝利した場合は防御側を吸収合併します。

これで一通り実装は完了したので、実際に大戦を実行しましょう。

札幌の一人勝ちでした。

もうちょっとパラメータを調整しないといけないですね。

【北海道大戦】対戦ルールを検討する

さて、前回までで、マップ関連は完成しました。

次回からバトル関連を作成していこうと思うのですが、バトルのルールを決めておく必要があります。

まずは、コンピューターだけでバトルさせて、どの市町村が勝利するかを見てみたいと思います。

ということで以下の様にしていました。

  • 各市町村、1ターンに1回行動できる
  • 行動順はランダムで並び替えされる
  • 1ターンにつき、隣接する市町村に攻撃を仕掛けることが出来る
  • 戦力値は人口×0.5~3.0(乱数)で決定される
  • 戦力値が大きい方が勝利する
  • 攻撃側が勝利した場合は防御側の市町村を吸収合併する。
  • 防御側が勝利しても何も無し。
  • 最終的に市町村が残り1つになるまで繰り返す。

こんな感じでどうだろうか?

まずは、作ってみて動かしてみましょうか。

【北海道大戦】離島問題を解決する

前回までの状況はこちら。

最新ソースはこちら(gitHub)

https://github.com/takishita2nd/HokkaidoWar

さて、前回で隣接都市とのリンクを作成しましたが、

1つ問題がありまして、

それは離島とのリンクです。

今のままではマスの隣接関係でリンクを確認しているので、マスが離れている離島との隣接関係がありません。

具体的には、

  • 奥尻ー江差
  • 礼文ー稚内
  • 礼文ー利尻
  • 礼文ー利尻富士
  • 利尻富士ー稚内

この隣接関係を作成しなければ行けません。

※ちなみに、天売島、焼尻島は羽幌に属しています。

今回はUp(),Down(),Left(),Right()処理に例外を入れることで対応します。

マップそのものが変わってしまうと、ここの処理も修正しなければ鳴りませんが、他に有効な手段が思いつかないので、仕方がありません。

    class Map
    {
        public Map Up { 
            get {
                var field = Singleton.GetFieldMap();
                if(_x == 18 && _y == 0)
                {
                    return field.GetMap(23, 0);
                }
                else if(_x == 23 && _y == 0)
                {
                    return field.GetMap(18, 0);
                }
                else if (_x == 20 && _y == 1)
                {
                    return field.GetMap(18, 0);
                }
                else
                {
                    return field.GetMap(_x, _y - 1);
                }
            }
        }

        public Map Down
        {
            get
            {
                var field = Singleton.GetFieldMap();
                if(_x == 18 && _y == 0)
                {
                    return field.GetMap(19, 1);
                }
                else
                {
                    return field.GetMap(_x, _y + 1);
                }
            }
        }

        public Map Left
        {
            get
            {
                var field = Singleton.GetFieldMap();
                if (_x == 2 && _y == 29)
                {
                    return field.GetMap(0, 29);
                }
                else if(_x == 19 && _y == 1)
                {
                    return field.GetMap(18, 0);
                }
                else if (_x == 23 && _y == 0)
                {
                    return field.GetMap(20, 1);
                }
                else
                {
                    return field.GetMap(_x - 1, _y);
                }
            }
        }

        public Map Right
        {
            get
            {
                var field = Singleton.GetFieldMap();
                if (_x == 0 && _y == 29)
                {
                    return field.GetMap(2, 29);
                }
                else if(_x == 18 && _y == 0)
                {
                    return field.GetMap(20, 1);
                }
                else if (_x == 20 && _y == 1)
                {
                    return field.GetMap(23, 0);
                }
                else
                {
                    return field.GetMap(_x + 1, _y);
                }
            }
        }

こんな感じで離島とのリンク関係を強引に作りました。

【北海道大戦】マップデータを描画する

前回までの状況はこちら。

gitHubにソースファイルを公開しました。

https://github.com/takishita2nd/HokkaidoWar

前回作成したJsonファイルを読み込んで、画面に出力させたいと思います。

まずはJsonデータをオブジェクトで持つためのクラスを作成します。

    [JsonObject("MapDataModel")]
    public class MapData
    {
        [JsonProperty("list")]
        public List[] list { get; set; }
    }

    [JsonObject("List")]
    public class List
    {
        [JsonProperty("name")]
        public string name { get; set; }
        [JsonProperty("point")]
        public Point[] point { get; set; }
    }

    [JsonObject("Point")]
    public class Point
    {
        [JsonProperty("x")]
        public int x { get; set; }
        [JsonProperty("y")]
        public int y { get; set; }
    }

これは、Jsonをクリップボードにある状態で、「編集」→「形式を選択して貼り付け」→「Json」を選択すると簡単にJsonに合わせてクラスを作成してくれます。

では、実際にJsonを読み込んでオブジェクト化する処理。

    class FileAccess
    {
        private const string _filename = "hokkaido.json";
        public static MapData Load()
        {
            string str = string.Empty;
            using (var stream = new StreamReader(_filename, true))
            {
                str = stream.ReadToEnd();
            }
            return JsonConvert.DeserializeObject<MapData>(str);
        }
    }

これをAltseedで画面に表示させます。

    class HokkaidoWar
    {
        MapData mapData = null;
        public HokkaidoWar()
        {
            mapData = FileAccess.Load();
        }

        public void Run()
        {
            asd.Engine.Initialize("北海道大戦", 1800, 1000, new asd.EngineOption());

            // 下地
            var background = new asd.GeometryObject2D();
            asd.Engine.AddObject2D(background);
            var bgRect = new asd.RectangleShape();
            bgRect.DrawingArea = new asd.RectF(0, 0, 1800, 1000);
            background.Shape = bgRect;

            var r = new Random();
            foreach (var map in mapData.list)
            {
                var color = new asd.Color((byte)r.Next(0, 255), (byte)r.Next(0, 255), (byte)r.Next(0, 255));
                foreach (var point in map.point)
                {
                    var geometryObj = new asd.GeometryObject2D();
                    geometryObj.Color = color;
                    asd.Engine.AddObject2D(geometryObj);
                    var rect = new asd.RectangleShape();
                    rect.DrawingArea = new asd.RectF(24 * point.x + 50, 24 * point.y + 50, 24, 24);
                    geometryObj.Shape = rect;
                }
            }

            while (asd.Engine.DoEvents())
            {
                asd.Engine.Update();
            }
            asd.Engine.Terminate();
        }
    }
        static void Main(string[] args)
        {
            HokkaidoWar hokkaidoWar = new HokkaidoWar();
            hokkaidoWar.Run();
        }

色はとりあえず乱数で振ることにしました。

実行結果はこうなりました。

【北海道大戦】マップデータを作成

そもそも、北海道大戦って何?

北海道の全市町村179ありますが、それらが全道統一を目指してバトルしたらどうだろう?って思い付きで考えたゲームです。

とりあえず、Excelで北海道マップを作成しました。

Excel方眼紙はこうやって使う。

でも、このままではデータとして使用できないので、番号に置き換えます。

ちゃんと179市町村ありました。

右に市町村の一覧があり、それらに番号が振られており、マップ上の市町村に番号を設置しました。

これをさらにExcelマクロで加工します。

イメージとしては、Json形式となるようにマップを検索し、X座標とY座標をまとめます。

Sub output()
    Dim TownArea As Object
    Dim Rows As Integer
    Set TownArea = Worksheets("Sheet1").Range("AU1", Range("AU1").End(xlDown).End(xlToRight))
    Rows = TownArea.Rows.Count
    
    Dim Area As Object
    Dim RightLength As Integer
    Dim DownLength As Integer
    Set Area = Worksheets("Sheet1").Range("B2:AS36")
    RightLength = Area.Columns.Count
    DownLength = Area.Rows.Count
    
    Dim WS2 As Object
    Set WS2 = Worksheets("Sheet2")
    Dim WriteLine As Integer
    WriteLine = 1
    WS2.Cells(WriteLine, 1).Value = "{""list"":["
    WriteLine = WriteLine + 1
    
    For R = 1 To Rows
        Dim number As Integer
        Dim name As String
        number = TownArea.Cells(R, 1).Value
        name = TownArea.Cells(R, 2).Value
        
        WS2.Cells(WriteLine, 2).Value = "{""name"" :"""
        WS2.Cells(WriteLine, 3).Value = name
        WS2.Cells(WriteLine, 4).Value = """, ""point"" :["
        
        For RL = 1 To RightLength
            For DL = 1 To DownLength
                If Area.Cells(RL, DL).Value = number Then
                    WS2.Cells(WriteLine, 5).Value = "{""x"" :"
                    WS2.Cells(WriteLine, 6).Value = RL - 1
                    WS2.Cells(WriteLine, 7).Value = ", ""y"" :"
                    WS2.Cells(WriteLine, 8).Value = DL - 1
                    WS2.Cells(WriteLine, 9).Value = "}"
                    WS2.Cells(WriteLine, 10).Value = ","
                    WriteLine = WriteLine + 1
                End If
            Next
        Next
        WS2.Cells(WriteLine - 1, 10).Value = "]},"
    Next
    WS2.Cells(WriteLine - 1, 10).Value = "]}"
    WS2.Cells(WriteLine, 1).Value = "]}"
End Sub

これをテキストにコピーして、セル間のタブを置換処理で削除すれば、Jsonとして完成します。

整形したらこんな感じになりました。

これでようやくマップデータとして使用できそうです。