前回の記事ではローグライクゲームの罠システムを実装しました。
プレイヤー、敵、パラメータとUI、宝箱、罠と順に作成してきました。かなりシステム面が仕上がってきました。
前回の記事:
今回の記事ではいよいよローグライクゲームの大きな特徴であるマップの自動生成システムを開発していきます。
本講座で最難関となる箇所ですが気合を入れて取り組んでいきましょう。
もし難しければ一旦書き写して動作する形にしてから色々値を変えたり改造して理解を深めていきましょう。
迷路自動生成アルゴリズムの作成
マップ自動作成には色々な方法がありますが、この記事では次のようなやり方でダンジョンを自動生成していきます。
- 指定したマップのサイズを埋めるように壁を配置する
- スタートとゴールを結ぶ道を作成する
- 適当に壁を道や敵などに変更する
自動生成の処理は「Map」スクリプトに追加し、実際に生成する命令を出すのは「MapSceneManager」コンポーネントで行います。
まず、「MapSceneManager」コンポーネントを自動生成に対応するように修正します。
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 |
//次のものを追加 public bool IsAutoGenerate = true; [SerializeField] Map.GenerateParam GenerateParam; public void GenerateMap() { var map = GetComponent<Map>(); map.DestoryMap(); map.GenerateMap(GenerateParam); } //次のものを修正 void Awake() { GameOver.SetActive(false); var map = GetComponent<Map>(); if(IsAutoGenerate)//追加 { map.GenerateMap(GenerateParam); //追加 } else { var lines = mapData.Split('\n').ToList(); map.BuildMap(lines); } } |
また、確認を簡単にするために「MapSceneManager」コンポーネントに次のメソッドを追加するのもいいでしょう。
1 2 3 4 5 6 7 8 |
//デバッグ用のコード private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { GenerateMap(); } } |
「Update」メソッドでスペースキーを押すとマップを再生成するようにしています。
このメソッドはあくまで確認用なので、後々削除するのを忘れないようにしましょう。
上のコードではまだ定義していないものを使用しているので、コンパイルには失敗します。
マップを壁で埋める
では指定したサイズのマップを作成し、その全てを壁に埋めるようにします。
「Map」スクリプトに以下の内容を書き加えて修正してください。
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 |
//次のものを追加 [System.Serializable] public class GenerateParam { public Vector2Int Size = new Vector2Int(20, 20); public int GoalMinDistance = 10; [Range(0, 1)] public float LimitMassPercent = 0.5f; [Range(0, 1)] public float RoadMassPercent = 0.7f; } public void DestoryMap() { for(var i=transform.childCount-1; 0<=i; --i) { Object.Destroy(transform.GetChild(i).gameObject); } } public void GenerateMap(GenerateParam generateParam) { InitMassData(); var mapData = new List<string>(); var wallData = this[MassType.Wall]; var line = ""; for (var x = 0; x < generateParam.Size.x; ++x) { line += wallData.MapChar; } for (var y = 0; y < generateParam.Size.y; ++y) { mapData.Add(line); } PlacePlayerAndGoal(mapData, generateParam); PlaceMass(mapData, generateParam); BuildMap(mapData); } //後ほど作成する void PlacePlayerAndGoal(List<string> mapData, GenerateParam generateParam) {} void PlaceMass(List<string> mapData, GenerateParam generateParam) {} |
この状態で再生するとマップが全て壁に埋め尽くされる状態になっています。
マップのサイズはシーンの「MapManager」の「MapSceneManager」コンポーネントの「MapSize」フィールドで指定することができます。
その時、「MapSceneManager」コンポーネントの「IsAutoGenerate」フィールドにチェックを入れるようにしてください。
注意点として、マップにプレイヤーがいないのでカメラが変な位置になっています。Sceneビューで適宜確認してみてください。
スタートからゴールまでの経路を作成
次にランダムな位置にプレイヤーとゴールを配置し、その間を結ぶように道を作ります。
スクリプトの修正が少々ややこしいので段階を踏んで行います。
まず、準備としてMap.csのGenerateMapメソッドを修正します。
まとめ
今回の記事では比較的簡単な方法(とは言えそれなりに複雑)ではありますが、マップの自動生成を行えるようにしました。
この記事の内容は乱数による自動生成なので、大雑把なマップが生成されます。
より精密なマップを生成したい時はパラメータを増やしたり、異なるマップ生成アルゴリズムを使用するといいでしょう。
テンプレートとなるマップ予め作成しておき、それらを組み合わせるのも一つの方法になります(レベルデザインの観点ではこのようなやり方の方が実践的かもしれないです)。
また、敵の移動アルゴリズムもマップの特徴に合わせたものにする方が自然な動作になります。この辺りも作成する内容によるので、色々とお好みに応じて試してみてください。
ローグライクゲームは作り始めると奥が深いです。
今回の講座で開発した内容は次のものになります。
- マップの自動生成処理の組み込み
- 指定したサイズのマップを壁で埋め尽くすように生成した。
- 開始位置からゴールまでの位置を結ぶようにした
- ランダムで壁マスを他のマスに置き換えるようにした。
それでは次の記事に行ってみましょう!
コメント