この記事のレビューを査読してくれたVincent Quarlesに感謝します。 このチュートリアルでは、ゲームの保存とロード機能の実装を終了します。プレイヤーゲームデータの拡張に関する以前のチュートリアルでは、統計や在庫などのプレーヤー関連データを保存およびロードしたことに成功しましたが、今では最も困難な部分である世界オブジェクトに取り組みます。最終的なシステムは、長老のスクロールゲームを連想させる必要があります。すべてのオブジェクトは、無期限に保存されています。
練習するためにプロジェクトが必要な場合は、最後のチュートリアルで完了したプロジェクトのバージョンを次に示します。 1つのポーションと1つの剣のアイテムを生成するゲーム内の相互作用可能なオブジェクトでアップグレードされました。それらを生み出すことができ、(> spawned)を拾うことができ、状態を正しく保存してロードする必要があります。プロジェクトの完成バージョン(Save Systemを完全に実装した)は、この記事の下部にあります。
プロジェクトの開始ファイルをダウンロード
キーテイクアウト
基本的に、ロジック全体がオブジェクトのリストをハードドライブに保存しています。これは、次にレベルがロードされると、移動し、プレイを開始するとすべてのオブジェクトが生成されます。簡単に聞こえますが、悪魔は詳細にあります:どのオブジェクトを保存するオブジェクトをどのようにして、どのようにしてそれらを引き戻すことができますか?
代表者とイベント公式の代表団のドキュメントと公式イベントのドキュメントを読むことができます。しかし、心配しないでください:公式文書のテクノバブルの多くを理解していないので、私はそれを平易な英語に入れます:
Delegate
この代表者は、何も返されない関数(void)を記述し、.NET/Monoフレームワークの2つの標準引数を受け入れます。イベントの送信者を表す一般的なオブジェクトと、さまざまなデータを渡すために使用できる最終的な引数です。あなたは本当にこれについて心配する必要はありません、私たちは議論として(null、null)に合格することができますが、彼らはそこにいなければなりません。
public delegate void SaveDelegate(object sender, EventArgs args);
では、これはイベントとどのように結びついていますか?
イベント
。デリゲート(青写真)に一致する関数のみを受け入れ、実行時に機能を配置および削除できます。
その後、いつでもイベントをトリガーできます。つまり、現在ボックスにあるすべての機能を一度に実行することを意味します。次のイベント宣言を検討してください
この構文は次のように述べています:パブリックイベントを宣言します(誰でも購読できます - 後で説明します)。
イベントへの購読は、基本的に「ボックスに関数を置く」ことを意味します。構文は非常に簡単です。私たちのイベントがよく知られているGlobalObjectクラスで宣言されており、イベントに登録する必要がある
potiondroppablepublic event SaveDelegate SaveEvent;
ここで構文を説明しましょう。最初に、説明された委任標準に適合する関数が必要です。オブジェクトのスクリプトには、SaveFunctionという名前のこのような関数があります。後で自分で書きますが、今のところ、オブジェクトをハードドライブに保存するための機能機能であると仮定して、後でロードできます。
それがあるとき、私たちは単にその関数を開始時にボックスに入れたり、スクリプトを覚醒させたり、破壊したときに削除したりします。 (登録解除は非常に重要です:破壊されたオブジェクトの関数を呼び出すと、実行時にNULL参照例外が得られます)。宣言されたイベントオブジェクトにアクセスし、追加演算子を使用して機能名を使用してこれを行います。
注:ブラケットや引数を使用して関数を呼び出していません。関数の名前を使用しているだけです。他に何も使用していません
それでは、ゲームのフローの例で最終的にこれが何をするか説明しましょう。ロジックフロー
これらの4つのオブジェクトは、セーブイベントで機能を登録します。
次に、プレイヤーが世界から1つの剣と1つのポーションを拾うと仮定しましょう。オブジェクトが「ピックアップ」されると、彼らはプレーヤーの在庫の変化を効果的にトリガーし、それから自分自身を破壊します(魔法を台無しにします、私は知っています):
そして、プレイヤーがゲームを保存することにしたとしましょう。たぶん、彼らは本当に3回鳴ったその電話に答える必要があるかもしれません(ねえ、あなたは素晴らしいゲームを作りました):
基本的に、ボックス内の関数が1つずつトリガーされ、すべての機能が完了するまでゲームはどこにも行かないということです。 「自らを保存する」すべてのオブジェクトは、基本的にリストに自体を書き留めています。次のゲームロードでは、レベルマスターオブジェクトによって検査され、リストにあるすべてのオブジェクトがインスタンス化されます(スポーン)。それだけです。これまでのところ、これまでの記事に従っていたら、基本的にすぐに実装を開始する準備ができています。それにもかかわらず、私たちはここでいくつかの具体的なコードの例に行きます、そしていつものように、あなたが記事の最後にあなたを待っている完成したプロジェクトがあなたが全体がどのように見えるかを見たいならあなたを待っています。
code
最初に既存のプロジェクトを進めて、すでに中にあるものに慣れてみましょう。
ご覧のとおり、既に述べた2つのオブジェクトのスポーナーとして機能するこれらの美しく設計されたボックスがあります。 2つのシーンのそれぞれにペアがあります。すぐに問題を見ることができます。シーンを移行したり、
f5/f9を使用して保存/負荷をかけたりすると、産卵したオブジェクトが消えます。
ポーションオブジェクトのリストを作成します
なぜ私たちが基本クラスを使用しているのではないのか疑問に思うかもしれません。答えは、私たちができるということですが、節約が必要な特定のアイテムプロパティを追加または変更する必要があることを実際に知ることはありません。さらに、これはコードの読みやすさの方がはるかに簡単です
今では、私がdroppable
今、プレイヤーのデータとは異なり、実際にはいつでもプレーヤーが1人しかいないことがわかっているため、ポーションのような複数のオブジェクトを持つことができます。動的リストを作成し、このリストがどのシーンに属しているかを示す必要があります。レベル1でレベル2のオブジェクトをスポーンすることはできません。これは、Serializablesで再び簡単に行うことができます。これを最後のクラスの下に書いてください:
public delegate void SaveDelegate(object sender, EventArgs args);
これらのリストのインスタンスを作成するのに最適な場所は、GlobalControlクラスです。
public event SaveDelegate SaveEvent;
委任とイベント
グローバルコントロールの
:
//In PotionDroppable's Start or Awake function: GlobalObject.Instance.SaveEvent += SaveFunction; //In PotionDroppable's OnDestroy() function: GlobalObject.Instance.SaveEvent -= SaveFunction; //[...] public void SaveFunction (object sender, EventArgs args) { //Here is code that saves this instance of an object. }
になります
そして、それはイベントの実装のためです!それにいくつかのものを購読しましょう!イベントサブスクリプション
イベントが発生したので、オブジェクトはそれを購読する必要があります。つまり、イベントを聞き始めて、発射したときに反応する必要があります。
potiondroppableで、これを追加してください。 サブスクライブを正しく行い、イベントに登録解除しました。今、疑問が残っています、
このオブジェクトをリストに保存する方法、正確に?
最初に、現在のシーンの初期化されたオブジェクトのリストがあることを確認する必要があります。
GlobalControl.cs:
この関数は、レベルごとに1回起動する必要があります。問題は、GlobalControlがレベルを通過し、その開始および目覚めの機能が1回だけ発射されることです。すぐに作成するレベルマスターオブジェクトからこの関数を呼び出すだけで、それを回避します。
現在のアクティブシーンリストも返すために、小さなヘルパー機能が必要になります。 GlobalControl.cs:
今、私たちは常にアイテムを保存するリストを持っていると確信しています。ポーションスクリプトに戻りましょう:
これは、すべての構文砂糖コーティングが本当に輝く場所です。これは非常に読みやすく、理解しやすく、必要なときに自分のニーズに合わせて変更しやすいです!要するに、新しい「ポーション」表現を作成し、実際のリストに保存します。
最初に、少しの準備。既存のプロジェクトには、シーンがロードされているかどうかを示すグローバル変数があります。しかし、ドアを使用してシーンが
に移行されているかどうかを示すような変数はありません。前の部屋に戻っても、その間にゲームを保存/ロードしなかったとしても、すべてのドロップされたオブジェクトがまだそこにあることを期待しています。それを行うには、グローバルコントロールに小さな変更を加える必要があります:
public delegate void SaveDelegate(object sender, EventArgs args);
transitionScript:
public event SaveDelegate SaveEvent;
正常に機能するレベルマスターオブジェクトを作成する準備ができています。
これで、リストを読み取り、ゲームをロードしているときにオブジェクトをスポーンするだけです。これは、レベルマスターオブジェクトが行うことです。新しいスクリプトを作成して、levelmaster:
と呼びましょう
//In PotionDroppable's Start or Awake function: GlobalObject.Instance.SaveEvent += SaveFunction; //In PotionDroppable's OnDestroy() function: GlobalObject.Instance.SaveEvent -= SaveFunction; //[...] public void SaveFunction (object sender, EventArgs args) { //Here is code that saves this instance of an object. }
コードは開始時にのみ実行され、必要に応じてGlobalControlの保存されたリストを初期化するために使用します。次に、シーンを読み込んだり移行したりするかどうかをGlobalControlに尋ねます。新しいゲームなどのシーンを新たに開始している場合、それは問題ではありません。オブジェクトはありません。
シーンの読み込みがある場合、保存されたオブジェクトのリストのローカルコピーを取得する必要があります(GlobalControl、
、の繰り返しアクセスでパフォーマンスを少し保存するために構文をより読みやすくするため)。 次に、リストを通過し、すべてのポーションオブジェクトを内部に出現させます。産卵の正確な構文は、基本的にインスタンスメソッドオーバーロードの1つです。インスタンス化メソッドの結果をgameObject (何らかの理由で、デフォルトのリターンタイプは単純なオブジェクトです)にキャストする必要があります。これにより、変換にアクセスして位置を変更できます。
これは、オブジェクトが生成される場所です。スポーン時に他の値を変更する必要がある場合、これはそれを行う場所です。各シーンにレベルマスターを置き、有効なプレハブを割り当てる必要があります。
重要な作品は1つだけありません。実際にイベントを起動し、リストをハードドライブまでシリアル化して読み取る必要があります。 GlobalControlの既存の保存および負荷関数でそれを行うだけです:
これも多くのコードがあるように見えますが、その大部分はすでにそこにありました。 (私の以前のチュートリアルに従えば、バイナリシリアル化コマンドを認識します。ここでの新しいものはFireSaveEvent関数とリストを保存する1つの追加ファイルです。
初期テスト
f5
および[Serializable] public class SavedDroppablePotion { public float PositionX, PositionY, PositionZ; } [Serializable] public class SavedDroppableSword { public float PositionX, PositionY, PositionZ; }
にヒットするか、ドアを通り抜けます(および任意の組み合わせの組み合わせそのような)。
これは、新しい
現在生成されている剣のプレハブは、Assets> Prefabsフォルダーにあります。
それを機能させましょう。 Assets> Scripts> Pickupsに移動すると、potiondroppableスクリプトが表示されます。その隣に、新しいSwordDropPableスクリプトを作成します:
「インタラクティブ」インターフェイスの実装を忘れないでください。それは非常に重要です。それがなければ、あなたの剣はカメラレイキャストによって認識されず、逆効果のままです。また、剣のプレハブがアイテムレイヤーに属していることを再確認してください。それ以外の場合は、Raycastによって再び無視されます。次に、このスクリプトをSword Prefabの最初の子供に追加します(実際にはメッシュレンダラーとその他のコンポーネントがあります):
public delegate void SaveDelegate(object sender, EventArgs args);
さて、それらを生成する必要があります。レベルマスターでは、ポーションを生成するループのために私たちのために:
public event SaveDelegate SaveEvent;
を追加します
イベントを保存するためにサブスクライブするアイテムのスクリプトを作成しますでも、ゲームでセーブ/ロードメカニックを行うための非常に信頼性の高い堅実な方法ですが、これらの
例とはとは異なる場合があります。
約束されているように、参照のために必要な場合、またはどこかに立ち往生したために、完成したプロジェクトが完成しました。保存システムは、このチュートリアルの指示と同じ命名スキームで実装されます。
実行順序は、Unity 5の保存および負荷機能にどのように影響しますか?どのスクリプトが実行されますか。これは、保存およびロード機能に大きな影響を与える可能性があります。たとえば、データをロードするスクリプトがデータを保存するスクリプトの前に実行される場合、ゲームは時代遅れのデータをロードする可能性があります。したがって、保存と読み込みデータに関連するスクリプトが実行順序で正しく順序付けられるようにすることが重要です。 5は、バイナリ形式または暗号化を使用して実現できます。バイナリフォーマットは、データを簡単に読み取りできないバイナリ形式に変換します。暗号化は、特定のキーでのみデコードできる方法でデータをエンコードすることにより、セキュリティの追加レイヤーを追加します。 🎜> PlayerPrefsは、Unity 5に保存とロード機能を実装するためのシンプルで便利な方法ですが、いくつかの制限があります。まず、整数、フロート、弦のみをサポートします。第二に、安全ではなく、簡単に操作できます。最後に、PlayerPrefsにはサイズ制限があり、これは大量のデータを備えたゲームの問題になる可能性があります。 Unity 5の複雑なデータ構造は、JSONシリアル化またはバイナリ形式を使用して実現できます。 JSONシリアル化を使用すると、複雑なデータ構造を簡単に保存およびロードできる文字列形式に変換できます。バイナリフォーマットは、データをバイナリ形式に変換するより安全な方法です。完成したプロジェクトをダウンロード:
Project Githubページ
プロジェクトzipダウンロード
Unityの保存とロード機能の習得に関するよくある質問(FAQ)
Unity 5に保存システムを実装する最良の方法は何ですか?
Unity 5に保存システムを実装する最良の方法は、PlayerPrefsクラスを使用することです。 PlayerPrefsは、ゲームセッション間でデータを保存および取得する簡単な方法です。これにより、整数、フロート、文字列の形でデータを保存およびロードできます。ただし、PlayerPrefsは安全ではなく、機密データに使用されるべきではないことに注意することが重要です。より複雑なデータまたは安全なデータについては、バイナリフォーマッターまたはJSONシリアナーの使用を検討することをお勧めします。 Unityでは、PlayerPrefs、JSON Serialization、またはバイナリフォーマットを使用して5を実現できます。 PlayerPrefsは最も簡単な方法であり、整数、フロート、文字列を保存およびロードできます。 JSONのシリアル化はもう少し複雑ですが、より柔軟性とセキュリティを可能にします。バイナリフォーマットは最も安全な方法ですが、最も複雑です。
以上がUnity 5の保存とロード機能のマスタリング5の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。