- 投稿日:2020-10-15T21:29:22+09:00
【Unity】Fungusでゲーム制作(6) セーブ・ロードを実装する
Fungusの基本的な使い方に関しては第1回の記事で解説しておりますので、まだの方はそちらからお読み下さい。
前回はアイテム欄を作る所まででした。
さて長めのゲームを作るとなったらセーブやロードの機能も必要です。ここではFungusでセーブ・ロード機能をどう実装するかについて解説します。Save Menuを作る
Fungusにはなんとセーブ・ロードメニュー用のUIが初めから入っています。Fungusのメニューから「Create」→「Save Menu」を選ぶとこんな感じで出てくるかと思います。
この状態ですでに動くので、ゲームを起動してみましょう。右上の三本棒が並んだボタンを押すとセーブ・ロードのメニューが出てきます。簡単なバックログもついてきてますし、Restartボタンを押せばゲームを最初からやり直すこともできます。しかしセーブはまだできません。
セーブポイントを設ける
これから実際にセーブできるようにしていきますが、その前にFungusにおけるセーブの仕組みについて解説していきたいと思います。
Fungusではブロックの中にセーブポイントを示すコマンド(Save Pointコマンド)を入れ、ゲームがそこまで進むとセーブが可能になる仕組みになっています。Save Pointコマンド以降にセーブを行うと、ロードした際にちょうどそのコマンドのところから処理を再開します。
また新しいSave Pointコマンドが出ればセーブポイントが更新され、以後のセーブはそこからの再開になります。また過去のセーブデータも保持されており、セーブ・ロードのメニューにある早送り・巻き戻しボタンを押すことで各セーブポイント間を行き来することができます。
またSave Pointコマンドはゲームの開始地点にも置く必要があり、これについてはスタート地点であることを示す設定を施します。そうすればゲーム起動後にそこから処理を始めてくれるのです。
(Game Start条件のブロックからゲームを始めることは今後しません。この条件はセーブポイントから再開した時にも当てはまるので、処理がおかしくなってしまいます)
以下にFungusにおけるセーブポイントの概念を図としてまとめておきました。
では実際にセーブポイントを仕込んでいってみましょう。ゲームの開始地点になっているブロックの開始条件(Execute On Event)を「<None>」に切り替え、一番最初にSave Pointコマンドを入れます。
Is Start Point欄にチェックを入れることでこのセーブポイントがゲームの開始地点であるという設定になりますので入れておきましょう。
Key Mode欄はセーブポイントにつける名前の設定です。初期状態ではBlock Nameとなっており、これを選んだ場合は該当ブロックの名称をそのまま名前として使います。Customを選ぶと自分で好きな名前をつけられます。いずれにせよ、ついた名前はコマンド一覧で下図のように「Key: ○○」と示されます。
残るFire Event欄はチェックなし、Resume On Load欄はチェック入りにしておいてください(この2つについてはこの後のロード時の処理に関する節で詳しく説明します)。
続いて、ゲーム中でセーブポイントにしたい所にSave Pointコマンドを入れましょう。こちらではIs Start Point欄のチェックは外しておきます。
ゲームを開始し、セーブとロードを試してみましょう。ロード時の処理を作る
セーブ・ロードはうまくいきましたでしょうか?
何か背景やBGMを変更しているブロックにセーブポイントを設けたら上手く再現されなかった……という方もいらっしゃると思います。これはセーブデータには背景やBGM、GameObjectの配置に関する情報は保存されないためです。特に問題が起こらなかったという方も、試しに背景・BGM変更直後にセーブポイントを入れてロードしてみてください。おかしなことになるはずです(状況が保存されていない→その後に改めて設定し直すこともしない→背景もBGMも再現されない)。
こういった場合にはロード時に発動する処理が必要ですので作ってみましょう。フローチャートに新しいブロックを作り、Execute On Event欄で「Scene/Save Point Loaded」を選択します。
Save Point Keysを開き、Size欄に何かしら値を入れるとその数だけElement欄ができます。ここに先ほどSave PointコマンドのKey Mode欄でつけた名前を入力すると、該当のセーブポイントのデータをロードした時にこのブロックが呼ばれます。Sizeを0にしておけばどこでロードしようが必ず呼ばれる処理にすることもできます。試しに、ロード時に場面に対応した背景・BGMを読み込み直すブロックを作ってみましょう。これまでに説明したことで可能なので説明は省きますが、下図のようなブロックができると思います。試してみて下さい。
ところでこの処理を応用すればロード時にこれまでのあらすじを表示するような演出も可能です。しかしその場合、場面転換の直後にセーブポイントを持ってくるやり方ですとうまくいきません(本来直後にくる処理とかぶってしまうからですね)。下図のように、背景やBGMの切り替えを終え、プレイヤーに操作を明け渡す直前のところに入れればOKです。
注意点として、該当のSave PointコマンドのFire Event欄のチェックを外しておくのを忘れないようにしておいて下さい……と、先ほど説明を省いたものになりますね。これにチェックを入れてあると、ロードではなく普通に流れでコマンドを実行した際にもロード時条件のブロックを実行するようになります。つまり初めて来たときにも勝手にあらすじを語りだしてしまいます。なので外しておきましょう。
またもう一つ説明を後回しにしたResume On Load欄についてですが、これのチェックを外した場合ロード時にそれ以降のコマンドを実行しなくなります。先ほどプレイヤーに操作を明け渡す直前にセーブポイントを……と書きましたがこれを使えば必ずしもそうする必要はなかったりします。ですが、上の図のように場面転換直後のイベントでフラグを立てるなどしている場合バグの原因になる可能性があるので、慣れないうちはあまりおすすめはできません。初めのうちはややこしいかと思いますが、セーブポイント絡みで思ったように処理をしてくれない……と思った時はFire Event、Resume On Load、そしてIs Start Point欄の設定を確認してみるといいと思います。この3つの働きについて以下に図としてまとめましたので参考にして下さい。
セーブに関する設定を変える
最後にオートセーブ・オートロードの設定について解説します。Hierarchyビューから最初に追加したSave Menuを選択し、InspectorビューでSave Menuコンポーネントを探してみて下さい。
設定はワンクリックでできます。Load On Start欄にチェックを入れればオートロードに、Auto Save欄にチェックを入れればオートセーブになります。今回はFungusにおけるセーブ・ロード機能の実装、ロード時の処理の作り方について解説いたしました。
次回は、Fungusに初めから用意されているセリフ表示・選択肢といったUIを作り変える方法について説明したいと思います。
- 投稿日:2020-10-15T19:40:09+09:00
Unityゲーム制作奮闘記 - 設計 - セーブシステム
はじめに
趣味でやっているUnityゲーム制作の理解と備忘の為に記録として残します。(まぁ成果物代わりみたいな。。)
Unityは特に設計関連の記事があまりないので、この機会に書いてみようと思います。悪戦苦闘しながら作ってるので、内容は色々穴があるかもしれませんが。。
今時点ではこれがベストだと思っていますが、同時にもっと良い方法があるのでは?とも思っています。
素人がなんか頑張ってるな、くらいの温かい面持ちで見て下さい笑
ちなみに「こういう方法もあるよ」などあれば、ぜひ教えて下さい!1. セーブシステム概要
今回イメージしているのは、冒険の書型セーブシステム(仮称)です。
プレイの断面を自由に複数保存でき、好きなデータをロードして再スタート出来るイメージ。
ロード画面では、セーブ時のプレイ画面をサムネイル表示させて、どんなデータだったか解るようにします。
なお今回作成しているのはオフラインゲームなので、データはローカルに保持させます。(ざっくりイメージはこんな感じ。まあ割とありそうなやつです)
2. 検討ポイント
今回のセーブシステムを実装するにあたって、特に検討が必要なのが以下だと思っています。
- データの検討
- セーブシステムのクラス設計
- セーブの検討
- ロードの検討
それぞれを以下で掘り下げて考えたいと思います。
3. データの検討
まずデータですが、今回のシステムでは大きく2種類必要になると思います。
- システムデータ(ゲーム全体の設定/パラメタを保持するクラス)
- プレイデータ(それぞれのプレイデータを保持するクラス)
前述のとおり、プレイデータはあくまでのゲーム断面のパラメータなので、
システム全体のパラメータ(例えば音量設定とか)は別に保存しておくべきと考えます。またプレイデータは、ロード画面でロードした際に読み込まれるイメージなので、
ロード画面に表示する各プレイデータの付随情報(セーブ日時やサムネイル画像)は
別に保存しておく必要があると考えており、これもシステムデータに保持させます。システムデータは1ファイルのみ。
プレイデータはセーブ数に応じてファイルがどんどん増える。
ただ今回は最大30くらいにします。4. セーブシステムのクラス設計
クラス設計、、圧倒的経験不足により正直よく解りません!笑
色々調べてはみましたが、謎は深まるばかりです←
とりあえずメンテナンス性が良い設計を心がけるのが大事というのは解りました。※出来るとは言ってない
なのでまずはダメ元で思うように作ってみて、今後に活かしたいと思います。なおシングルトンはあまり使わない方が良いって記事も目にしますが、
今回はゲーム内データを一括管理するので、
シングルトンで構わんのだろう?という心持ちで採用しております。ちなみにこの設計は白黒_unityさんという方の動画を参考にしています。
めっちゃ解りやすいのでそちらも是非見てみてください!←SaveSystem/PlayData/Systemdataはモノビヘイビアを継承しません。
これはシーン遷移してもインスタンスを保持するためです。
SaveSystemは自分と各データのインスタンスを保持します。5. セーブの検討
セーブの仕組み自体は色々な方が情報を公開してくれており、
インスタンスをJSONに変換して保存するだけで良さそうです。
圧縮や暗号化もかけられるので、容量節約のために圧縮は組み込みたいと思います。
今回は個人ゲームなので暗号化はどっちでも良いかなという感じです。
(チートしたければすればええやん!たぶん楽しくないけどな!という感じ)注意点を書いておくと、PlayDataとSystemDataのクラスには[System.Serializable]を記載しておく必要があります。
あと、この方法は辞書型のデータはそのまま保存できないっぽいです。(ごにょごにょすれば保存できます)
また多次元配列もそのままでは保存できないらしい。(こっちは試してないので不明ですが、最初から一次元にしといた方が楽そう。。)セーブ時の一番の考慮事項は、サムネイルに表示する画像かなと思っています。
この仕組みはセーブ画面への移動ボタンがクリックされた際に、
先にスクリーンショットを取得してから、画面遷移という感じで考えています。
⇒メソッド名は忘れましたが、スクリーンショットはUnityの機能で取れます。スクリーンショットを取った後ですが、まずは解像度を落とします。
そのままでも勿論使えますが、セーブ数分保存すると結構な容量になりそうなので、
ごにょごにょして解像度を下げて保存します。
(解像度を落とすと色が飛んで色々大変だったんですが、その辺りは機会があれば。。)次に画像をテキスト化します。
ここは画像をそのまま保存でも良いかもしれませんが、テキスト化してそのままシステムデータに保存しておけば
処理もシンプルだし、画像を別で管理する必要がないので楽かしら?という考えです。このテキスト化した画像はtmpとしてインスタンスのプロパティに入れておいて、
実際のセーブ実行ボタンが押された際に、システムデータの「プレイデータ一覧」的な所に書き込むイメージです。なおセーブボタンですが、それぞれのボタンにインデックスを振っておき、
押されたボタンに対応するプレイデータとして保存します。(playData001とか)
このインデックス番号で「サムネイルのゲームオブジェクト」「プレイデータ」「システムデータ」を紐づけて管理します。6. ロードの検討
ロード自体の処理も簡単で、セーブでやった処理を戻すだけです。
自分の場合は解凍して、テキストをインスタンス化すればOKです。ロードでの検討項目は、ロード画面にプレイデータの情報をサムネイル表示させる部分。。
まぁゲーム起動時に読み込ませるしかない訳ですが。。(どのくらい時間かかるかな。。)肝はテキストとして保存されている画像データをスプライトに変換する部分でしょうか。
処理としては変換してサムネイル用のオブジェクトに登録するだけですが、、
今回はプレイデータの最大数が30なので、多分そんなに掛からない筈。。
(遅くなるのを恐れて最大数を減らしたと言っても過言ではない。。)あ!あと明記してませんでしたが、セーブ/ロード画面は同じ画面を使います。
Flagを立てて、ロードorセーブで処理などを変えるイメージ。
(なのでロード画面にサムネイルを描画すると、必然的にセーブ画面も描画されます)おわりに
セーブシステムの検討は以上です。
スマートなやり方なのかは一旦置いておくとして、
これでイメージするセーブシステムは作成出来ます。でも、もっといいやり方が有る気するなぁ。。
結構メジャーな方式な気はするけれど、他の方々は一体どんな風に設計/実装しているのだろう。。以上
- 投稿日:2020-10-15T18:29:39+09:00
【Unity】Luaが終了するまで待機する方法【Fungus】
FungusをLuaから操作する際に、Luaが終了するまで待機する方法を説明します。
前提としてUniRx/UniTaskの導入が必要です。
ステップ1 なぜ必要か?
Luaは非同期で処理されるため、Luaの終了をC#側で受け取るには
- 終了を通知するメソッドをC#側で用意する必要がある。
- Secene内のLuaEnvironmentの操作が必要である。
- Luaに終了通知するC#のメソッドを記述する必要がある。
など手間がかかります。そこでFungus側のソースコードを修正して、上記の手間をなくそうと思います。
ステップ2 LuaEnvironment.cs
LuaEnvironment.csに以下のメソッドを追加します。
public IEnumerator RunLuaFunction(Closure closure) { DynValue co = interpreter.CreateCoroutine(closure); while (co.Coroutine.State != CoroutineState.Dead) { try { co.Coroutine.Resume(); } catch (InterpreterException ex) { LogException(ex.DecoratedMessage, GetSourceCode()); } yield return null; } }ステップ3 LuaScript.cs
LuaScript.csに以下のメソッドを追加します。
using Cysharp.Threading.Tasks;※usingの追加が必要です。
public async UniTask OnExecuteAsync() { InitLuaScript(); await luaEnvironment.RunLuaFunction(luaFunction); }ステップ4 C#側の呼び出し方
Luaの実行方法はこの記事のステップ6を参照してください。
public async void OnFungusAsync() { Debug.Log($"開始"); // Luaが終了するまで待機する。 await _luaScript.OnExecuteAsync(); Debug.Log($"終了"); }
- 投稿日:2020-10-15T16:21:49+09:00
UnityでAR Foundationを使ってiOS向けARアプリを開発する(備忘録)
はじめに
iOS向けARアプリケーションをUnityを用いて開発したときに、詰まったり調べたことを自分への備忘録として残しておきます。
現在進行形で勉強しているので、随時変更していきます。開発環境
- Windows 10 Home
- Unity (2019.4.1f)
- AR Foundation (4.0.2)
- AR Subsystems (4.0.1)
- ARKit XR Plugin (4.0.2)
- MacBook Pro (macOS Catalina)
- XCode (12.0.1)
- iPhone7 (iOS 14.0.1)
導入
- ここからAR Foundation samplesをダウンロード
- Unityでプロジェクトファイルを開く
- File -> Build Settings から PlatformをiOSに変更
- Buildした後、XCodeでUnity-Phone.xcodeprojを開き、実行
必須オブジェクト
ARSession
オブジェクト
- AR Session スクリプト
- AR Foundationの機能を使うために。ARシーンで1つ必要。
- AR Input Manager スクリプト
- World Trackingのために必要。
ARSessionOrigin
オブジェクト
- AR座標とUnity Spaceの座標変換。ARアプリ起動時の座標が(0, 0, 0)になる。
ARSessionOrigin
オブジェクトの子要素にAR Cameraを設定。- 検出面は
ARSessionOrigin
クラスのtrackablesParent
プロパティで取得可能平面検出
GameObject → XR → ARDefaultPlane
からARDefaultPlaneオブジェクト
をシーンに追加- Prefab化して、
ARSessionOriginオブジェクト→ ARPlaneManagerコンポーネント
のPlane Prefab
にARDefaultPlane
オブジェクトを追加- Hierarchyから
ARDefaultPlane
オブジェクトを削除Planeオブジェクトには
ARPlaneVisualizer
コンポーネントを追加
MeshRenderer
コンポーネントのMaterialを変更することで好きな柄にすることが出来る(参考)AR Foundation Sampleについて
AR Foundation Menu
AR Foundationに含まれているSample Sceneを一覧で表示してくれる。助かる。
Meshing
SimpleAR
- 平面の検出、オブジェクトの設置、ARSessionの操作ができる簡単なサンプルシーン
Interaction
- 検知した平面上にオブジェクトを設置できる。ジェスチャで移動、拡大、回転が可能
Face Tracking
- TrueDepth front-facing Cameraが必要なため、iPhoneX以降またはiPad Proが必須。
Segmentation
Light Estimation
- カメラから光情報を推定し、明るさ・色温度・色補正・光源の方向・光強度・光の色・Spherical Harmonicsを表示
- 上記の環境では全てUnavailableでした
Image Tracking
Plane Detection
- Feathered Plane
- Planeがドット柄になる。
ARPlaneManager
コンポーネントのPlane Prefab
にARFeatheredPlane
オブジェクトを設定- Plane Classification
- iPhone XS, iPhone XS Max, and iPhone XRで実行可能
- 平面を「ドア(door)」「座席(seat)」「窓(window)」「床(floor)」などのカテゴリに分類
- Toggle Plane Detection
- Plane Detectionのオンオフを切り替える
- ボタンが押されると
ARSessionOrigin
オブジェクトのPlaneDetectionController.ToggulePlaneaDetection()
を実行Env Probes
Anchors
Object Tracking
CPU Images
AR World Map
- iOS12が必要
- ARWorldMapはNSSecureCodingに準拠しているので、NSKeyedArchiverを使ってシリアライズ
- リロードはNSKeyedUnarchiver
- 既存のARWorldMapからセッションを開始するには、ワールドトラッキング設定の initialWorldMap プロパティを設定してrunメソッドを実行する
Scale
- 表示したオブジェクトの見かけのスケールを変更
- AR Foundationではcontentのスケールを変えずに、
ARSessionOrigin
のTransform
を変更するAR Collaboration
Point Cloud
Plane Occulusion
Coaching Overlay
Check Support
- デバイスがARに対応しているか表示
Sample UX
エラーまとめ
Unity
error CS0117: 'Collab' does not contain a definition for 'ShowChangesWindow'
nity CollaboratePackageのバージョンが合っていません。
Xcode
MapFileParser.sh: Permission denied
MapFileParser.shに実行権限がありません。ターミナルで以下のコードを実行して権限を付与します。
chmod +x MapFileParser.sh
Code signing is required for product type 'Application' in SDK 'iOS 14.0'
Xcode上でTeamが設定されていません。プロジェクトからSigning & Capabilitiesタブを開いてTeamを設定したら直りました。
Teamが変更できない場合は、Automatically manage signingがONにします。
"Unity-iPhone" requires a provisioning profile. Select a provisioning profile in the Signing & Capabilities editor.
上に同じ。
Your maximum App ID limit has been reached. You may create up to 10 App IDs every 7 days
Apple Developper Programの無料版ではAppIDは10個/週しか作れません。なので以下のどれかの手順を踏む必要があります。(参考)
1. 新たなAppleIDをXcodeに登録する
2. 今まで使っていたAppID(BundleID)を使う
3. Apple Developer Programの有料バージョンに登録する参考文献
- 投稿日:2020-10-15T16:20:55+09:00
Failed to load window layoutが出てUnityが起動しない現象の解決
初歩的に見える根深いトラブル
プロジェクトを作成してUnityを開くと,突然このダイアログが出てくるようになりました.
グレゴール・ザムザも早起きするレベルの頻度で発生していて,立ち上がるUnityの数が多いほどこのトラブルと向き合うのだなと残念な思いをしています.
課題の解決方法
Unityのプロジェクトを新規作成した,ロードした,というタイミングでこのダイアログが出てきたら以下の方法で救出を試みてください.
落ち着く,焦らない
「設定を戻そう」「デフォルトのレイアウトにしよう」と焦るとかえってどこまで操作したのかわからなくなります.冷静になりましょう.
プロジェクトを保存したフォルダを開く
まずは,当該エラーの出てしまったプロジェクトを保存したフォルダを開いてください.プロジェクトの中にLibraryフォルダがあります.
Libraryフォルダの中に実は件のダイアログが表示される原因が存在しています.Libraryフォルダから,
CurrentLayout-default.dwlt
を探してください.本当にぶっ壊れているかどうかに興味ある人は,ファイルサイズを憶えておきましょう.
Load Default Layoutをクリックする
例のダイアログに戻って,
Load Default Layout
をクリックしてください.1回だけクリックするだけでいいです.何度もこのダイアログは表示されます.だからと言って何度もクリックして良い方向になるとは限りません.
このボタンをクリックすると,
CurrentLayout-default.dwlt
が一時的に修復されます.CurrentLayout-default.dwltをバックアップする
ここで必要な操作は
CurrentLayout-default.dwlt
のバックアップです.バックアップする方法はいろいろありますが,一番簡単な方法はデスクトップにコピーすることです.ドラッグ&ドロップでデスクトップに放り込みましょう.Unityをダイアログから終了させる
またダイアログに戻って
Quit
ボタンをクリックしてください.するとUnityは正常に終了します.もう一度,Layoutフォルダを見てみましょう.
釈然としませんね.
CurrentLayout-default.dwlt
のファイルサイズが1KBに減っています.正常終了した結果,たったの1KBになるわけで釈然としませんが.バックアップファイルを壊れたファイルへ上書きする
先ほどバックアップした
CurrentLayout-default.dwlt
を,LibraryフォルダのCurrentLayout-default.dwlt
に上書きしてください.上書きの許可についてダイアログが出てきます.
上書き対象を間違えていないかチェックしてください.上部赤枠の部分でデスクトップからLibraryになっているかチェックしてください.古今東西ありがちなのですが,本番をバックアップで上書きするなんてよくあることです.逆になってないか確認しましょう.
Unityを起動する
これでUnity Hub等々でプロジェクトを再度開くと,Unityがちゃんと起動します.
なんでCurrentLayoutが壊れるのか?
理由はわかりません.
CurrentLayout-default.dwlt
を開くと,ただのYAMLファイルであることがわかります.差分を取っていないし,様々なところで同様の現象が発生しているそうなので,きっと次のバージョンで直るんだと期待しています.参考文献
https://www.xlsoft.com/jp/blog/blog/2020/09/30/post-13872/
UnityのIssueを見ると,結構いろいろなバージョンで発生していることがわかります.根深い…….