MENU

#1 簡易野球シミュレーションゲームを作ろう【Unity】

そうだ。野球をしよう。チーム名は、「MonsterGirls」だ!というわけでUnityを使いながら野球のシミュレーションゲームを作りたいと思います。段階的に作っていきますので、最初の方の段階はしょぼく感じるかも知れませんが、最終的にはチーム編成を行ったり出来るようにして行きたいと思います。

目次

野球シミュレーターを作る(大まかな部分)

勝敗のみを決める

まずはシンプルに勝った負けたを決める。ここでは本当にランダムで勝ち負けを決めたいと思います。なので何vs何という判定も一切せず、ゲームが開始したらどちらが勝ったかを決めたいと思います。

イニングごとに得点を取る

さすがに勝った負けただけだとジャンケンにも劣るので、次の段階としてはイニングごとに得点を行う処理を作りたいと思います。得点するルールは得点出来たかどうかの判定を行い、出来た場合は+1して再度同じ抽選に戻り、得点出来なかった場合は次のイニングに交代します。まずはコールドゲームなどはなしで、9回まで行いたいと思います。(後攻が勝っていた場合は当然9回裏は行わない)

勝敗も適当ではなくちゃんとチームの合計得点で勝敗を判断出来るようにします。

アウトカウントの概念を導入する

野球は1イニング3アウトで構成されています。さきの修正で1イニングに何点取るかをざっくりで行いましたが、今度はアウトカウントも込で得点が取れるかどうかを判定に入れたいと思います。

イニングごとに点をとっていたルールをアウトカウントか得点かで行います。

簡易打席システムを導入

今まではスコアボードの数字がどう変わるかとアウトカウントが多少絡む程度で野球のシミュレーターを作成しました。ここでは1打席ごとの結果をもとにアウトカウントが増えるのか、得点につながるのかなどに結びつけたいと思います。進塁打がどうこうとかはなしで、まずは野球盤システムで作成しようと思います。

打席内での行動を追加する

ここでは一度打席のみの処理を作ります。段階的に打席の結果は以下に分類。

  • 三振(見逃しか空振りは問わず)
  • フォアボール
  • 凡打(アウト)
  • シングルヒット
  • ツーベース
  • ホームラン

凡打が多めにしておいて、ホームランが一番出づらいで調整します。まずは各打席の結果を抽選します。ファールとかは一旦なしで。

ピッチャーは投球、バッターはスイング

次は1打席中に1球ずつ判定を挟むためにピッチャーが投球するための処理を実装します。同時にバッターもスイングを行います。この時点では選球眼的なものは考慮しません。振るか振らないかはランダムに行います。

先程の打席内での行動を少しアレンジして、ボール球に手を出した場合は凡打や貧打が起こりやすく、ストライクに手を出した場合はクリーンヒットが出やすいように調整します。

(こうなってくるとファールにする能力とか欲しくなってきますがまだ堪える)

ゲームに当てはめる。

やっと1打席と呼べるものが作れたので、あとは1イニング3アウトで交代するというルールに当てはめてみたいと思います。

アウトカウントの概念を導入した部分に今回の1打席ごとの結果を反映します。これで、より野球のシミュレーションらしくなります。

ソースファイルを一部抜粋

今回作成したもののソースファイルを実装部分に注目しながら公開します。主に実行される流れに沿って解説する方向で

ゲーム開始時に呼ばれる部分

1珠ずつゲームを進行するプログラムになったとき、ボタンを押して呼ばれるメソッドは以下

    // 1球ずつ行う ---------------------------------------------------
    public void TestOneBall()
    {
        GameInitialize();
        StartCoroutine(OneBallResult());
    }

ゲームの初期化を行って1球ずつ処理を行うゲームが開始されます。GameInitializeメソッドでは以下のようなことをやっています。9イニングで終わる様になってたり、先行後攻の決め方に悩んでたりしますね。最後の方でスコアボードをクリアしたりイニングごとにリセットする変数をクリアしてます。

    public void GameInitialize()
    {
        int[,] test = new int[3, 4];
        m_iScoreInning = new int[(int)攻撃順.最大, 9];
        for (int i = 0; i < 9; i++)
        {
            for (int j = 0; j < (int)攻撃順.最大; j++)
            {
                m_iScoreInning[j, i] = -1;
            }
        }
        ShowScoreBoard(m_iScoreInning);

        m_inningCount.Reset();
        ShowInningCount(m_inningCount);
    }

1球ずつゲームが進行する処理

先程のメソッドで呼ばれていたOneBallResultでは9回>先行後攻の順番ごとに各イニングの結果を作成する様になっています。コールドゲームを実装する場合はここにも何かテコ入れしないと対応できなさそうですね。

    private IEnumerator OneBallResult()
    {
        for (int i = 0; i < 9; i++)
        {
            for (int j = 0; j < (int)攻撃順.最大; j++)
            {
                yield return StartCoroutine(InningResultOneBall(i, (攻撃順)j));
            }
        }
        yield return 0;
    }

各イニングの大まかな流れ

ちょっと長い。イニングが開始されると呼ばれるメソッド。最初にイニングのデータをクリア(こう見るとGameInitializeでイニングデータをクリアしている処理はいらなかったなとか気づく)。プログラムの流れをざーっとまとめると・・・

  • サヨナラのチェック(現在は9回のウラ限定でサヨナラ判定)
  • アウトカウントが3未満の場合はひたすらループ
  • 1打席の結果がつくまでループ(←盗塁失敗なんかでのアウトカウントに対応出来てない)
  • ピッチャーが球を投げる(PitchingBall)
  • 投げたボールに対してバッターがどういう結果をもたらすかを求める(SWING_RESULT)
  • 打席結果はバッターが継続するかどうかのフラグを持たせている(bContinueDaseki)
  • 打席結果をランナーやスコア、アウトカウントなどに反映(打席が継続する場合はバッターカウントに反映)
  • 最後にサヨナラチェックをして次のバッターに移る(アウトカウントによってはループを抜ける)
    private IEnumerator InningResultOneBall(int _iInning, 攻撃順 _junban)
    {
        m_inningCount.Reset();
        ShowInningCount(m_inningCount);
        m_runnerManager.Clear();

        if (CheckSayonara(_iInning, _junban, m_iScoreInning))
        {
            m_iScoreInning[(int)_junban, _iInning] = -2;
            ShowScoreBoard(m_iScoreInning);
            yield return new WaitForSeconds(0.05f);
            yield break;
        }
        m_iScoreInning[(int)_junban, _iInning] = 0;

        for (; m_inningCount.outcount < 3;)
        {
            ShowScoreBoard(m_iScoreInning);

            SWING_RESULT swingResult = SWING_RESULT.MAX;
            bool bContinueDaseki = true;

            do
            {
                PitchingBall pitchingBall = m_daseki.Pitching();
                swingResult = m_daseki.GetDasekiResult(pitchingBall, m_inningCount);

                switch (swingResult)
                {
                    case SWING_RESULT.SEEOFF:
                        if (pitchingBall.IsStrikeAreaBall())
                        {
                            m_inningCount.AddStrike(false);
                        }
                        else
                        {
                            m_inningCount.AddBall();
                        }
                        break;
                    case SWING_RESULT.SWING_OUT:
                        m_inningCount.AddStrike(false);
                        break;

                    case SWING_RESULT.SINGLE:
                    case SWING_RESULT.TWOBASE:
                    case SWING_RESULT.THREEBASE:
                    case SWING_RESULT.HOMERUN:
                        bContinueDaseki = false;
                        break;

                    case SWING_RESULT.BONDA:
                    default:
                        m_inningCount.AddOutCount();
                        bContinueDaseki = false;
                        break;
                }

                if (bContinueDaseki)
                {
                    if (3 <= m_inningCount.strike)
                    {
                        m_inningCount.AddOutCount();
                        bContinueDaseki = false;
                    }
                    else if (4 <= m_inningCount.ball)
                    {
                        bContinueDaseki = false;
                    }
                }
                ShowInningCount(m_inningCount);
                yield return new WaitForSeconds(0.1f);
            }
            while (bContinueDaseki);

            int iAdvance = m_daseki.GetAdvanceCount(swingResult);
            if (4 <= m_inningCount.ball)
            {
                iAdvance = 1;
            }
            m_runnerManager.AddBatter(iAdvance);

            // ホームインしたランナーも消します
            int iAddScore = m_runnerManager.GetScore();
            m_iScoreInning[(int)_junban, _iInning] += iAddScore;

            ShowScoreBoard(m_iScoreInning);
            yield return new WaitForSeconds(0.1f);

            if (CheckSayonara(_iInning, _junban, m_iScoreInning))
            {
                break;
            }
        }
    }

簡易版の完成と今後の要素

今回は簡易版ということでこれにて一幕。ただ、野球ゲームとしてはまだまだ要素が欲しいですよね。細かいものも含め、実装したいものとかを上げていきましょう。

実装したいルールなど

今回は必要最低限のみでしたが、野球を行う上で色々な要素を見過ごしてます。順不同で思いついたものをダーッと書きなぐります。

  • ファウル
  • 送りバント
  • 犠牲フライ
  • 進塁打
  • 盗塁
  • 併殺打(ダブルプレー)
  • 打球の飛び方(ゴロ・ライナー・フライ)
  • エラー
  • 守備陣形

打席での複雑化

今回の実装では1球ごとに勝負が入りましたが、実際の野球ではもっと複雑な駆け引きが行われます。ここでは打席中で入れたい要素などを書きなぐります

  • 変化球
  • カウントによって投げる球の傾向を変える
  • 投手の利き手
  • バッターの左右での差
  • 選球眼(ボール球の見極め)
  • インコース・アウトコース
  • ローボール・ハイボール
  • 失投
  • ボール→ストライク(ストライク→ボール)

選手の能力反映

どれだけルールを決めても、この部分がなかったらゲーム的には面白くないよね。各選手ごとの個性や能力を反映させる処理。特殊能力とかも込で戦わせたいですね。ここに関しては言い出したらきりが無いですがとにかく書き下します。

  • パワー
  • バットコントロール
  • 球速
  • 変化球
  • 守備力
  • 走力
  • 送球
  • スタミナ
  • ジャンプ
  • クセ
  • 観察力
  • 得意守備位置
  • 得意球

特殊能力

通常の能力とは別でまとめます。プレーンな能力で十分楽しめるものにしてから特殊能力などを入れるのが良いでしょう。ここに関しては思いついたらどんどん追加していく予定。

  • チャンス
  • 逆境
  • バントが上手い
  • 流し打ちが得意
  • ひっぱりが得意
よかったらシェアしてね!

この記事を書いた人

コメント

コメントする

目次
閉じる