ユニティちゃんのRPGを作ってみよう7ーワールドマップの作成とシーン間の遷移ー

今回はワールドマップのシーンの作成とワールドマップシーンと村のシーンの遷移が出来るようにしていきたいと思います。

前回は村人のプレハブ化と村のBGMを設定しました。

ユニティちゃんのRPGを作ってみよう6ー村人のプレハブ可と村のBGMの設定ー
ユニティちゃんのRPGで村人の移動機能と会話機能をプレハブにしたり、村のBGMを流すようにしてみます。

ユニティちゃんのRPGを作ってみようの他の記事は

ユニティちゃんのRPGを作ってみよう
ユニティちゃんのRPGを徐々に作っていくカテゴリです。

から見ることが出来ます。

スポンサーリンク
スポンサーリンク

ワールドマップシーンの作成

まずはワールドマップのシーンを作成します。

Assets/RPG/Scenesフォルダ内で右クリックしCreate→Sceneを選択して名前をWorldMapとします。

WorldMapシーンをダブルクリックしてシーンを開きます。

ヒエラルキー上で右クリックから3D Object→Terrainを選択し、名前をWorldMapGroundとします。

作成されたAssetsフォルダ内のNew TerrainはAssets/RPG/Field/WorldMapフォルダ内に移動させます。

WorldMapGroundのインスペクタでTerrainの歯車を押し、サイズを調整します。

TerrainのMesh ResolutionのTerrain WidthとTerrain Lengthを500にし、TransformのPositionのXとZを-250にします。

ユニティちゃんRPGのワールドマップ

このRPGの世界の大きさに合わせてサイズを調整してください。

今回はワールドマップシーンでTerrainは一つですが、拡張をすることも出来ます。

TerrainのCreate Neighbor Terrainsを押します。

Terrain地面の近接部分を拡張する

シーンビューでTerrainの地面の近接する部分を押すと地面を新たに作成することが出来ます。

シーンビューで近接部分をクリックすると地面を作成する

近接する地面を作成すると新たにヒエラルキー上にTerrainが作成されTerrainのデータがAssetsフォルダ内に作成されます。

今回は近接する地面は使いません。

またワールドマップシーン自体を複数作成しワールドマップシーン間を移動させるようにするというのもいいかもしれませんね。

後は村を作成した時と同じようにワールドマップの地面を作ってみてください。

とりあえず必須なのは前回までに作った村のミニチュアが配置されていることです。

今回はCubeを使って村のミニチュアに見立てました。

ミニチュアを配置しなくてもなんとなく看板よ村の入り口とわかるようにしておくといいかもしれません。

ワールドマップ上にFirstVillageのミニチュアを置く

湖を作る

ワールドマップに湖を作りたい事もあると思います。

WorldMapGroundの地形で凹とした部分を作ります。

湖用に地形に穴を掘る

Assets/Standard Assets/Environment/Water/Water/WaterProDaytimeを地形の凹っとした部分に配置します。

WaterProDaytimeのScaleを調整し凹っとした部分を覆うようなサイズにします。

ユニティちゃんRPGのワールドマップに湖を作る

上のように湖というか小さい池みたいなものが出来上がりました。

上の画像だとわかりにくいですが最後のサンプル動画で確認します。

ワールドマップに配置した地形や村のミニチュア、湖等は空のゲームオブジェクトを作成し名前をWorldMapにしたTransformのPositionとRotationが全て0のオブジェクトの子要素に配置しました。

ワールドマップに配置したオブジェクトをWorldMapオブジェクトの子要素にする

複数のシーンの編集をする

今回はシーンの遷移をさせるので、それぞれのシーンにユニティちゃんや会話用UI等共通のゲームオブジェクトを用意する必要があります。

そこで複数のシーンを同時に編集できるようにして、Villageシーンに存在するユニティちゃんや会話用UIをWorldMapシーンに複製し移動出来るようにします。

Villageシーンを開いている状態でAssets/RPG/Scenes/WorldMapをヒエラルキー上にドラッグ&ドロップします。

ユニティちゃんRPGで複数のシーンを編集出来るようにする

するとヒエラルキー上に複数のシーンの編集が可能となります。

このままUnityを実行すると二つのシーンが読み込まれた状態でスタートしていまいます。

シーン右の部分からUnload Sceneを選択するとそのシーンはロードせずUnityを実行出来ます。

Remove Sceneを選択するとそのシーンをヒエラルキー上から消します(Assetsフォルダ内のシーンファイルを削除するわけではありません)。

編集をする時はLoad Sceneを選択するとそのシーンの編集が出来ます。

ユニティちゃんRPGの複数シーン編集時の個別のシーンの設定変更

VillageシーンのUnityChan、TalkIcon、TalkUIをCtrl+Dキーを押して複製し、それをWorldMapシーンにドラッグ&ドロップします。

同一のゲームオブジェクトなのでシーン遷移時にそのゲームオブジェクトをそのまま新しいシーンに移動させてもいいのですが、今回は同じゲームオブジェクトをそれぞれのシーンに配置することにします。

一番良さそうなやり方はUnityChanゲームオブジェクトだけシーン遷移時も残しておいて、TalkIconやTalkUIはプレハブにしておいて必要に応じてインスタンス化して利用するのがいいのかもしれません。

WorldMapシーンの地面の凸凹が大きい場合はUnityChanのCharacterControllerでSlope LimitやStep Offsetを少し大きくします。

ただ大きい値を設定するとVillageシーンと同じように山に登れてしまう問題が出るので気を付けてください。

FollowTargetとAudioSourceの取り付け

WorldMapシーンにもMain Cameraがあると思いますが、そこにVillageシーンと同じようにFollow TargetとAudio Sourceの取り付けを行います。

Follow TargetにWorldMapシーンに配置しているUnityChanをドラッグ&ドロップし、Offsetも変更します。TransformのRotationのXの角度もVillageと同じように変更します。

Audio SourceのClipにはAssets/RPG Game Music/Minuet in Dを設定します。

Play On Awakeにチェックを入れシーンが読み込まれたら再生を開始しLoopにチェックを入れループして再生します。

またSpatial Blendを左にドラッグし2D音声とします。

ユニティちゃんRPGのWorldMapのMain Cameraの設定

シーン遷移時のデータファイルを作る

シーン間を移動する時にユニティちゃんがそのシーンのどの位置から開始するかなどの情報を保持して置く必要があります。

そこでシーン遷移時のデータをScriptableObjectのファイルとして保持して置くことにします。

ScriptableObjectは何らかのゲームオブジェクトに取り付ける必要のないスクリプトで使用すると便利で、インスタンスをアセットファイルとして作成(可視化)しておくことも出来ます。

ScriptableObjectに関しては以下の記事も参照してみてください。

UnityのScriptableObjectを使う
UnityのScriptableObjectを使うと、メモリの節約が出来たり、シーン間の移動でデータを共有するのが簡単になります。この記事では基本だけを記述しています。

Assets/RPG/Scriptsフォルダ内で右クリックからCreate→C# Scriptを選択しSceneMovementDataという名前にします。

SceneTypeはどのシーンからどのシーンへの遷移をしているかを表す列挙型です。

sceneTypeはシーン遷移時に設定しておき、シーンを遷移した時のユニティちゃんの初期位置の設定に使用します。

OnEnableでsceneTypeの初期化を行っています。

SetSceneTypeはシーンタイプを設定し、GetSceneTypeはシーンタイプを返します。

スクリプトが出来たらAssets/RPG/Dataフォルダ内で右クリックからCreate→Folderを選択し、名前をSceneMovementDataとします。

SceneMovementDataフォルダ内で右クリックからCreate→CreateSceneMovementDataを選択します。

シーン遷移時にこのファイルを読み書きしてユニティちゃんの位置や角度を決めます。

シーン遷移時のユニティちゃんの位置と角度を設定するゲームオブジェクトの作成

シーン移動時にそのシーン内でのユニティちゃんの初期位置と角度を設定する必要があります。

そこでVillageシーンには空のゲームオブジェクトでInitialPositionを作成し、WorldMapシーンには空のゲームオブジェクトでInitialPositionFirstVillageToWorldMapを作成します。

InitialPositionとInitialPositionFirstVillageToWorldMapのインスペクタでTransformのPositionとRotationをそのシーンに移動した時のユニティちゃんの位置と角度をそれらのゲームオブジェクトのTransformで設定します。

例えばVillageシーンのInitialPositionは以下のように設定しました。

ユニティちゃんRPGのVillageシーンのInitialPositionのインスペクタ

名前の横のアイコンを変更し、どの位置かわかりやすくします。

Villageシーンが読み込まれたらInitialPositionのTransformのPositionとRotationにユニティちゃんのPositionとRotationを設定することになります。

Assets/RPG/Scriptsフォルダ内で右クリックしCreate→C# Scriptを選択し、名前をSceneLoadingPositionとします。

SceneLoadingPositionスクリプトをVillageシーンとWorldMapシーンのUnityChanにドラッグ&ドロップし取り付けます。

インスペクタで先ほど作成したSceneMovementDataファイルを設定します。

StartメソッドでSceneMovementDataのGetSceneTypeメソッドでシーンタイプを取得し、どのシーンからどのシーンへの遷移なのかを調べます。

シーンタイプに応じてそのシーンのユニティちゃんを置くべき場所のゲームオブジェクトのTransformを取得し、ユニティちゃんをその位置と角度に変更します。

シーン間の遷移とフェードの作成

シーン遷移時のユニティちゃんの初期位置の設定やシーン移動時のデータの作成等が出来ました。

次は実際のシーン間の遷移をさせるスクリプトを作成していきます。

Villageシーンで右クリックからCreate Empty、複数シーン編集している時は空きスペースがない場合もあるのでヒエラルキー上のVillageのシーン名の所で右クリックからCreate Emptyをすることも出来ます。

名前をSceneManagerとします。

Assets/RPG/Scriptsフォルダ内で右クリックからCreate→C# Scriptを選択し、名前をLoadSceneManagerという名前にしSceneManagerゲームオブジェクトに取り付けます。

自身を表すLoadSceneManagerを保持して置くloadSceneManagerフィールドを作成しstaticを付けてインスタンス化することなく使用出来るようにします。

AwakeメソッドではloadSceneManagerが設定されていなければ自身のスクリプトを設定し、DontDestroyOnLoadメソッドを使って自身が取り付けてあるSceneManagerゲームオブジェクトをシーンを移動しても残すようにします。

シーン遷移した時はloadSceneManagerが既に設定されているはずなのでDestoryでゲームオブジェクトを削除し、SceneManagerゲームオブジェクトが複数存在しないようにします。

GoToNextSceneメソッドではコルーチンを使ってFadeAndLoadSceneメソッドにシーンタイプを渡して呼び出します。

FadeAndLoadSceneメソッドでは最初にフェードアウト→シーンの読み込み→フェードインという処理を作成しています。

フェードに使用するfadePrefabは後で作成します。

FadeメソッドではfadePrefabからインスタンス化したfadeInstanceのImageであるfadeImangeのアルファ値を変化させ、fadeImageのアルファ値と目的のアルファ値であるalphaを引いてMathf.Absで絶対値を求め、その差が0.01より大きい間はフェード処理を続けます。

それ以外の時はコルーチンが終了し次の処理にいきます。

LoadSceneメソッドではSceneManager.LoadSceneAsyncを使って引数で受け取ったシーンの非同期な読み込みをします。

SceneManager.LoadSceneAsyncの戻り値はAsyncOperationでそのisDoneプロパティで読み込みが終了したかどうかが判定出来ます。

シーンの読み込みやフェード処理の詳しい内容は

UnityのRPGでワールドマップから戦闘シーンへの遷移を作成する
UnityのRPGゲームでワールドマップから戦闘シーンへの遷移を作成し、シーンを移動した時のデータはScriptableObjectで作成しデータを共有できるようにします。

に記述しているので、そちらを参照してみてください。

フェード画像の作成

LoadSceneManagerスクリプトで使用するフェード画像を作成します。

Villageシーン内で右クリックからUI→Imageを選択し、Canvasの名前をFadeとします。

子要素のImageを選択し、インスペクタのAnchor Presetでstretch stretchを選択し、画面いっぱいにImageが表示されるようにします。

ユニティちゃんRPGのフェード画像のインスペクタ

ImageのColorを押し、RGBAの全てを0にします。

ユニティちゃんRPGのフェード画像のColorの設定

これでフェード画像が出来たので

Assets/RPG/Prefabsフォルダ内で右クリックからCreate→Folderを選択しUIという名前にします。

FadeをUIフォルダ内にドラッグ&ドロップしプレハブにします。

SceneManagerゲームオブジェクトのインスペクタでLoadSceneManagerのFadePrefabにAssets/RPG/Prefabs/UI/Fadeを設定します。

ユニティちゃんRPGのLoadSceneManagerにFadeプレハブを設定

シーン遷移する範囲を作成する

シーンを移動する機能が出来ましたが、どの場所に移動したらシーン移動するかという機能を作成していません。

そこでVillageシーンに空のゲームオブジェクトを作り名前をGoToWorldMapとして作成し、WorldMapシーンには空のゲームオブジェクトを作成し、GoToFirstVillageという名前にします。

それぞれのインスペクタでAdd ComponentからPhysics→Box Colliderを取り付け、Is Triggerにチェックを入れます。

Box ColliderのCenterやSizeを調整し、村の入り口や村からワールドマップへと移動する範囲をカバーするようにします。

ユニティちゃんRPGの村の出口の範囲

Assets/RPG/Scriptsフォルダ内で右クリックからCreate→C# Scriptを選択し、名前をGoToOtherSceneとします。

GoToWorldMapとGoToFirstVillageゲームオブジェクトのそれぞれにGoToOtherSceneスクリプトを取り付けます。

sceneはどのシーンからどのシーンへと遷移するかのシーンタイプをインスペクタで設定出来るようにしています。

isTransitionはシーン遷移範囲内に一旦入ったらisTransitionをtrueにし、シーン遷移する前に範囲を出て再度範囲内に移動してもシーン遷移処理をしないようにする為のフラグです。

AwakeメソッドでLoadSceneManagerを取得します。

OnTriggerEnterで範囲内に入ったのがユニティちゃんでシーン遷移途中でない時はisTransitionをtrueにします。

そのあとLoadSceneManagerスクリプトのGoToNextSceneメソッドにシーンタイプを引数として渡してシーン遷移をさせています。

シーン登場時のユニティちゃんの位置と角度が安定しない!?

シーン間の移動機能は出来ましたが、実際にスタンドアロン形式でビルドして試してみると、シーンを移動した後のユニティちゃんの位置と角度がInitialPositionゲームオブジェクトやInitialPositionFirstVillageToWorldMapゲームオブジェクトの位置や角度にならず、デフォルトの位置になってしまうことがあります。

正直わたくしこれで2週間近くハマってしまいました・・・・( ノД`)シクシク…

しかもこの現象は起きたり起きなかったりと毎回出るわけでもないのでさらに厳しいエラーです。

解像度を落としたり、グラフィックのクオリティを下げると発生する割合が増えるようです。

処理が早いと発生するんですかね?

スクリプトが間違っているのか?ずーっと試していたんですが、どうやらCharacterControllerの移動機能のMoveメソッドを呼び出すと駄目っぽい事がわかりました。

というわけでUnityChanScriptを少し変更します。

まずは新しい状態Waitを作成します。

次にStartメソッドで初期状態をState.Waitにします。

Updateメソッド内のユニティちゃんの状態で処理を分岐している個所でState.Wait状態だった時に会話相手がいれば会話状態に出来るようにします。

またCharacterControllerのMoveメソッドを使っている個所でユニティちゃんの状態がState.Wait以外の時は重力処理と移動処理を行います。

それ以外の時は移動キーを押した時にState.Normal状態へと遷移させます。

なんだかしっくりこない感じですが、とりあえずこんな感じにしました。

これで機能が出来ました!

ここまでのVillageシーンとWorldMapシーンの階層は

ユニティちゃんRPGを作ってみよう7の記事までのVillageとWorldMapシーンの階層

上のような感じになっています。

シーンの登録

シーン間の遷移を確認する時やゲームをビルドして確認する時にあらかじめシーンを登録しておく必要があります。

UnityのFileメニュー→Build Settingsを選択し、Assets/RPG/ScenesフォルダのVillageシーンファイルとWorldMapシーンファイルをScenes In Buildの領域にドラッグ&ドロップします。

ゲーム開始時にロードするシーンを一番上にします。

今回はVillageシーンから始めるのでVillageシーンを上にしておきます。

ユニティちゃんRPGで使用するシーンファイルを登録する

これでシーンの登録が終わりました。

シーン間の移動機能を確認する

シーン間の移動が出来るか確認してみましょう。

Unityを実行して試してみると、

上のようになります。

終わりに

今回のシーン遷移の機能は色々やる事があるので難しいかもしれません。

他にもっと良いやり方もあるかもしれませんので色々改造してみてください。(´Д`)

ユニティちゃんライセンス

この作品はユニティちゃんライセンス条項の元に提供されています

コメント

タイトルとURLをコピーしました