見出し画像

【Unity】3Dローグライクゲームの作り方〜Step11-10〜

前回の記事はこちら
前回は有利な状態異常を追加しました。

ダンジョン生成のパラメーター反映について修正

ダンジョン生成のパラメーターを表すDungeon Infoですが、今の記述のままだとマップファイル側から変更した時に次の階層でも変更されたままになってしまうかと思われます。これを改善します。
LoadFieldMapクラスのReadMapFileを以下のように変更して下さい。

private Array2D ReadMapFile(string path)
{
   try
   {
       /*   省略   */
       if (data == null)
       {
           DungeonInfo info = new DungeonInfo();
           info.roomSizeMin = dungeonInfo.roomSizeMin;
           info.roomSizeMax = dungeonInfo.roomSizeMax;
           info.outerMargin = dungeonInfo.outerMargin;
           info.posMargin = dungeonInfo.posMargin;
           info.connectRate = dungeonInfo.connectRate;
           info.enemyNumMin = dungeonInfo.enemyNumMin;
           info.enemyNumMax = dungeonInfo.enemyNumMax;
           info.itemNumMin = dungeonInfo.itemNumMin;
           info.itemNumMax = dungeonInfo.itemNumMax;
           foreach (var prop in group.Element("properties").Elements("property"))
           {
               int num = int.Parse(prop.Attribute("value").Value);
               switch (prop.Attribute("name").Value)
               {
                   case "RoomSizeMin":
                       info.roomSizeMin = num;
                       break;
                   case "RoomSizeMax":
                       info.roomSizeMax = num;
                       break;
                   case "OuterMargin":
                       info.outerMargin = num;
                       break;
                   case "PosMargin":
                       info.posMargin = num;
                       break;
                   case "ConnectRate":
                       info.connectRate = num;
                       break;
                   case "EnemyNumMin":
                       info.enemyNumMin = num;
                       break;
                   case "EnemyNumMax":
                       info.enemyNumMax = num;
                       break;
                   case "ItemNumMin":
                       info.itemNumMin = num;
                       break;
                   case "ItemNumMax":
                       info.itemNumMax = num;
                       break;
               }
           }
           data = dungeon.Create(w, h, field, info);
       }
       return data;
   }
   catch (System.Exception i_exception)
   {
       Debug.LogErrorFormat("{0}", i_exception);
   }
   return null;
}

1Fもランダム生成マップにする

1Fもランダムな方が不都合がなくていいですね。という訳で今回の本題はこちらです。
まずFieldクラスを以下のように変更して下さい。

// パラメーターを削除
private bool isStart = true:

// パラメーターを追加
public string firstStartStairs = "Down";

// メソッドを変更
public void SetObject(string name, string type, int xgrid, int zgrid, int width, int height)
{
   switch (type)
   {
       case "StartPoint":
           if (firstStartStairs.Equals(startStairs))
               playerMovement.SetPosition(xgrid, zgrid);
           break;
       case "Stairs":
           if (name.Contains("Down"))
               stairs.GetComponentsInChildren<ObjectPosition>()[1].SetPosition(xgrid, zgrid);
           else
               stairs.GetComponentsInChildren<ObjectPosition>()[0].SetPosition(xgrid, zgrid);
           if (name.Contains(startStairs))
               playerMovement.SetPosition(xgrid, zgrid);
           break;
       /*   省略   */
   }
}

ゲーム開始時非表示にする階段をFirst Start Stairsに入力します。
次にLoadFieldMapクラスを変更します。

[System.Serializable]
public class DungeonInfo
{
   /*   省略   */
   public int isStartEnd = -1;
}

private class RandomDungeon
{
   /*   省略   */
   private void SetObjects(Field field)
   {
       Array2D tmpData = new Array2D(data.width, data.height);
       for (int x = 0; x < data.width; x++)
       {
           /*   省略   */
       }
       SetObject("UpStairs", "Stairs", field, tmpData);
       SetObject("DownStairs", "Stairs", field, tmpData);
       if (info.isStartEnd == 0)
       {
           if (field.firstStartStairs.Contains("Up"))
               field.SetObject("UpStairs", "Stairs", 0, 0, 1, 1);
           else field.SetObject("DownStairs", "Stairs", 0, 0, 1, 1);
           SetObject("StartPoint", "StartPoint", field, tmpData);
       }
       int num = Random.Range(info.enemyNumMin, info.enemyNumMax + 1);
       /*   省略   */
   }
   /*   省略   */
}

private Array2D ReadMapFile(string path)
{
   try
   {
       /*   省略   */
       if (data == null)
       {
           DungeonInfo info = new DungeonInfo();
           /*   省略   */
           // 以下1行を追記
           info.isStartEnd = dungeonInfo.isStartEnd;
           foreach (var prop in group.Element("properties").Elements("property"))
           {
               int num = int.Parse(prop.Attribute("value").Value);
               switch (prop.Attribute("name").Value)
               {
                   /*   省略   */
                   // 以下3行を追記
                   case "IsStartEnd":
                       info.isStartEnd = num;
                       break;
               }
           }
           data = dungeon.Create(w, h, field, info);
       }
       return data;
   }
   catch (System.Exception i_exception)
   {
       Debug.LogErrorFormat("{0}", i_exception);
   }
   return null;
}

DungeonInfoクラスのisStartEndパラメーターは0ならスタート地点、1以上なら終わり、-1以下なら途中階を表します。
ビルドしたら、新たにマップを作成します。1Fにあたる階にはカスタムプロパティ「IsStartEnd」を追加し、「0」を入力しておきます。
ゲーム開始時は階段が表示されなくなったと思います。

スクリーンショット 2020-10-20 17.40.50

階を移動して再度戻ってくると、上り階段の上に立っていると思います。
ちなみにUnityエディタ側からIs Start Endの値を0に変えてみて下さい。
すると、2Fに上がった時も下り階段が表示されなくなります。前の階に戻れないようにしたい場合は活用して下さい。

見た目を良くする

そろそろこの見た目にも飽きてきたので、少し手を加えて良くしてみようと思います。
よりダンジョンっぽくしてみましょうか。
まず最初に壁をレンガ風にするために、以下を使用します。

スクリーンショット 2020-10-21 4.01.29

「Voxel Dungeon Environment Set 1」で出てくると思います。
このモデルを使って壁を作成していきます。ヒエラルキータブ上のWalls内に空のオブジェクトを作成し、名前を「WallBlock」にします。その階層下にモデルを入れて、ブロック一つ分位のサイズに整えておきます。
出来上がったらプレハブ化し、ヒエラルキータブ上のWallBlockは削除して下さい。
そしてFieldコンポーネントのWallにWallBlockを指定します。
テストしてみます。以下のようになればOKです。

スクリーンショット 2020-10-21 4.19.56

次に地面に模様をつけます。
良いアセットが見つからなかったので、自作しました。

画像4

上記を保存し、Spritesフォルダに入れ、マテリアルFloorのAlbedoに設定します。
そして、FieldクラスのCreateメソッドに追記します。

public void Create(Array2D mapdata)
{
   map = mapdata;
   autoMapping.Reset(map.width, map.height);
   float floorw = map.width / floorSize;
   float floorh = map.height / floorSize;
   floor.transform.localScale = new Vector3(floorw, 1, floorh);
   // 以下1行を追記
   floor.GetComponent<MeshRenderer>().material.mainTextureScale = new Vector2(map.width, map.height);
   /*   省略   */
   for (int z = 0; z < map.height; z++)
   {
       /*   省略   */
   }
   ShowGridEffects();
}

実行すると以下のようになったかと思います(色を変えてあります)。

スクリーンショット 2020-10-21 4.52.07

最後に階段の色を変更します。マテリアルWallのアルベドの色を変更して下さい。

スクリーンショット 2020-10-21 4.58.42

これからの予定

Step11のこれからの実装予定と致しまして、以下を考えています。
(この内かっこのあるものは実装しない可能性が高いものとなっています)

・その場で足踏み
・その場で方向転換
・ダッシュ
・ゲームスタート(オーバー)画面など
・+付きアイテム
・敵を倒すとアイテムドロップ
・装備品の種類(恐らくアクセサリー)の追加
・(敵の無限湧き)
・(ちゃんとしたステータスウィンドウ)
・(宝箱)

もし他に実装して欲しいというものがあれば是非ご連絡下さい。
筆者の力の及ぶ範囲内で実装したいと思います。
(と言いますのも筆者自身余り思いつかなくて......。是非皆様のご意見をお伺いしたいです!)
ただし次ステップやその先のステップで実装するだろうものに関しては後回しになってしまうと思います。すみません、ご了承ください。

という訳で今回はここまでに致します。
次回は早速足踏みなど実装していこうと思います。

この記事が気に入ったらサポートをしてみませんか?