ウィザードリィのような疑似3Dダンジョンを作る

ここまで2Dでマップを自動生成しプレイヤーを動かしてきました。
そのマップを利用して疑似3Dダンジョンを表現してみたいと思います。
疑似3Dで有名なゲームはやはりWizardryでしょうか。
他にもマイト・アンド・マジック、FC版の女神転生や、昔のUltimaシリーズのダンジョンなども疑似3Dで作られています。

そもそもUnityで3Dのゲームが作れるわけなので疑似3Dを作る必要あるのか?
と思われるかもしれませんが、私はレトロゲームや、疑似3Dのゲームが大好きなので作ってみたいと思います。
この記事はシリーズものとなっています。

疑似3Dを作る前の準備

疑似3Dに取り掛かる前に、今まで作ったものを修正しておきたいと思います。
完成形をイメージはこのような感じにしたいです。
疑似3D完成形レイアウト

2Dマップの移動

まず2Dマップの位置を移動します。
UnityEditorのHierarchyにあるMapManagerにテキストで見出しを書いておきます。これは別に必須ではありません。
そして同じ階層にmap2Dという空のゲームオブジェクトを作成します。
Hierarchyの土台
続いてスクリプトを修正していきましょう。
MapManagerでmap2DのTransformを取得し、マップタイルが生成される際にmap2Dの子要素にしていきたいと思います。
合わせてPlayer.csの方で取得しているMapGeneratorも変更していきます。
そして最後にmap2DのXの位置を修正します。
具体的にはこのような感じです。
MapGenerator.cs

Player.cs

こんな感じです。

プレイヤーの移動を修正

では次にプレイヤーの移動処理を変更しておきましょう。
今までは上下左右のいずれかのキーを押せばその方向に1マス動きましたが、以下のように修正します。

  • 上キー:向いている方向に一歩すすむ
  • 右キー:向いている方向から右を向く
  • 下キー:向いている方向の後ろを向く
  • 左キー:向いている方向から左を向く

向いてる方角がパッと見てどちらなのかわかりやすいようにPlayerPrefabを修正しておきます。
本来はプレイヤーのグラフィックを変更しても良いのですが向いてる方角を確認するだけなのでこのようにします。
PlayerのPrefab
続いてPlayer.csを修正していきます。

Player.cs

このような感じになりました。
まず矢印のTransformをSerializeFieldで取得し、上下左右の矢印のポジションを定義します。
追加する関数は2つです。
それぞれの役割は以下の通りです。

  • _setDirection関数:キー入力に応じてdirectionを変更
  • _viewArrow関数:矢印の位置を修正

これらの関数を使ってUpdate内で入力されたキーに応じて向きと矢印を変更しています。
実行してみるとこんな感じです。
2Dマップ上でプレイヤーを動かす

右キーを押したら右回り、左キーを押したら左回り、下キーを押したら反転するように向きが変わるようになりました。
これで準備OKです。
いよいよ疑似3Dの作成に入っていきます。

疑似3Dの作成

素材の準備

では早速疑似3Dの作成に入っていきましょう。
そのためにはプレイヤーの視点に入ってくる壁の画像が必要です。
図にするとこういう感じです。
疑似3Dのプレイヤーからみたマス目ベース
プレイヤーが居る位置から緑の線の部分の壁の素材を用意します。
マスの番号は描画していく順番です。奥から左、右、中と描画していきます。
私は絵心がありませんのでAsepriteというドット絵を描くツールを使って簡単な壁の画像を用意しました。
全部で15枚の壁の画像を用意することになります。

Playerの方向による座標の取得

続いてプレイヤーが向いている方向によって、マップ上のどの座標が描画対象になるのかを取得していきます。

左、右、真ん中の順で描画していく場合、プレイヤーの向きによってそれぞれのx座標、y座標は図のようになります。
疑似3DのPlayerから見たマス目の四方

この図を配列にしてみます。
Player.csにlocationsという整数の3次元配列を用意します。

このような感じになると思います。
この配列を使って現在のポジションから見える範囲までのマップ上の座標を取得してみます。
_getMapPositionsという関数を作ってみました。
Player.cs

最初のループで奥行き、中のループは幅となっています。
幅のループ内でマップ上の座標をx,yそれぞれ取得しています。
そしてマップ上の座標を元に、その座標のマップタイプを取得しています。
もう一点修正でMapGeneratorの幅と高さ用のint型の変数w,hをpublicにしておいてください。
MapGenerator.cs

この_getMapPositons関数をStart関数とUpdate関数のそれぞれのキー入力の分岐に入れていきます。
Player.cs

実行して試してみましょう。
タイルマップの種類をログで確認
このようにログでマップタイプが表示されました。
この値を用いて用意した3Dマップ用の素材を表示して疑似3Dを表現してみます。

疑似3Dダンジョン素材を配置

3Dマップ表示用の空のオブジェクトmap3DをMapManagerの子要素として新たに作成します。
続いてmap3D内に用意した疑似3D用の壁の素材を配置していきます。
3D用壁素材配置
配置し終わったら奥から手前、左、右、中という順番でOrder in Layerを1ずつ増やしてい行きます。
私の場合ですと、0_bottomが0で11_bottomが15となっています。
map3Dの位置を調整し2Dと横並びにしてみました。
現在起動するとこんな感じです。
3D素材を配置した状態でゲーム起動
まだ疑似3Dの方は配置しただけですので何も変化がありません。
次にこれらの壁素材を先程取得したマップタイプ情報を元に表示のオンオフをしていく仕組みを考えてみます。

疑似3D素材の表示

素材のオンオフをどこでやるかですが、今回はMapGeneratorでやっていこうと思います。
MapGeneratorのInspector上で素材を登録しておけるようにします。
MapGeneratorクラスの外にpublicでWallsArrというクラスを作ります。System.Serializableをつけて置きます。

続いてMapGeneratorクラス内にWallsArr型の配列 wallsArrという変数をSerializeField宣言します。
これでMapGeneratorのInspectorに2次元配列を表示させることができます。
続いてmap3Dの素材たちを手前から奥の順番でMapGeneratorのwallsArrにセットしていきます。
2次元配列にゲームオブジェクトを順番にアタッチ

次に描画する用の関数を作成します。MapGeneratorにView3Dという関数を作りました。

これはwallsArrのX番目のGameObject配列wallを一つづつ取り出しSetActive(true)として表示しています。
この関数をPlayer.csの方から呼び出してあげましょう。
Player.csの_getMapPositionsを修正します。
具体的にはこのようになりました。

先程MapGeneratorのInspectorに素材を配置する際、手前から奥の順番で配置したのはこのループ内で計算したidxとwallsArrのidxが一致するようにしてあります。
このやり方はもっと別にいい方法があると思います。あまりスマートなやり方とは思えませんが、今回はこれでよしとします。

それではPlayer.csのStart関数とUpdate関数内のInput処理の中で_getMapPositionsを呼び出します。

それでは実行してみましょう。
疑似3Dバグありキャプチャ

バグの修正

いくつかおかしいところがありました。
壁があるはずのところに壁がなかったり、進むと描画がリセットされなければいけませんが、それがされていない点。
そしてこの画像では表示されていませんでしたが、横を向いた時の上下が逆になっていましたので、こちらを修正していきます。
まず描画をリセットする関数を先に実装します。
MapGeneratorの中にResetView3Dという関数を用意します。

これでwallsArrに登録されている壁素材を全て非表示にします。
その後にView3D関数を呼び出すという流れです。
つづいてPlayer.csのStart関数とUpdate関数内でMapGeneratorのResetView3Dを呼び出します。

これで描画に関するバグが治りました。
次に横向きの時のバグを修正します。
具体的にはlocationsのyが設定されている値を反転させます。
MapGenerator側でYにマイナスをかけていますので、その事を忘れていました。

このようになりました。
最後にMapGeneratorのw(横幅)とh(高さ)を設定していたにも関わらず生成されるマップの大きさは決め打ちで13*13のマップになっていましたのでそちらも修正します。
MapGeneratorの_loadMapDataを以下のようにします。

それでは実行してみましょう!
疑似3Dバグ修正後のキャプチャ

マップの大きさもランダムの値が入り実行のたびに変わるようになりました。
また横向きの時のYの描画の反転も修正され、進んだり向きを変えたりするたびに正しく疑似3Dの壁が表示されているかと思います。

参考資料

一連のマップ生成と疑似3Dダンジョンを作成するに当たり参考にさせていただいた書籍や動画などを紹介します。

パズルゲーム講座(倉庫番)Unityゲームスタジオしまづ

2Dマップを作成するにあたりとても参考になった動画シリーズです。
特にテキストデータからマップを生成する動画がわかりやすいです。


またしまづさんはUnityのオンラインサロンも運営しています。
何を隠そう、私もサロン会員です。こちらも非常にオススメです。

14歳からはじめるC言語わくわくゲームプログラミング教室

疑似3Dのダンジョンをつくるきっかけになった書籍です。
こちらはUnityではなくDXライブラリを使ってゲームを作る本ですが、疑似3Dの仕組みを理解するにはとても参考になりました。

Algoful 迷路生成(穴掘り法)

迷路を自動で生成する方法を丁寧に解説してくれています。疑似3Dに限らず迷路を作りたい人や、仕組みを理解したい人にはオススメのサイトです。
穴掘り法以外にも棒倒し法、壁伸ばし法も解説されています。
他にもいろんなアルゴリズムの説明が載っています。

小一時間でゲームをつくる|ゲヱム道館

小一時間でいろんなゲームを作るゲヱム道館さんの書籍です。
この中にも疑似3Dダンジョンに関する内容が書かれています。

ウィザードリィを小一時間で作ってみた【C言語ゲームプログラミング実況】Programming Wizardry

私がゲヱム道館さんを知るきっかけになったYoutube動画です。
Unityではありませんが、何度も見直して参考にさせて頂きました。長い動画ですが、それを感じさせないくらい面白いです。

最後に

マップ表示から始まり疑似3Dダンジョンの生成までいかがだったでしょうか?
もしかしたら疑似3Dゲーム好きの方は「ドアシステムがないじゃないか!」とお怒りかもしれません。
一応ドアシステムの作り方は習得し、私は現在このようなゲームを開発中です。

別の機会にドアシステムの実装方法を記事にしようかと思っています。
一応作り方の参考までに過去に私がQiitaに投稿した記事のリンクを掲載しておきます。
ウィザードリィのような疑似3DダンジョンをC言語初心者のおっさんが作ってみた(マップを表示した)
こちらはUnityではなくC++で作られています。
作り方の参考にしていただければ幸いです。

関連記事

最後までご覧頂いてありがとうございました。