web-dev-qa-db-ja.com

Unityゲームマネージャー。スクリプトは一度だけ動作します

私はシンプルなゲームマネージャーを作っています。ゲーム内のすべてのシーンからアクセスできるスクリプトがあります。そして、新しいシーンをロードした後、変数の値を確認する必要があります。しかし、このスクリプトを含むオブジェクトがすべてのシーンに存在する間、私のコードはシミュレーションを開始した後に一度だけ実行されます。なにが問題ですか?新しいシーンを読み込んだ後に機能しないのはなぜですか?

43
Dima Kozyr

すべてのUnityプロジェクトでは、プリロードシーンが必要です。

Unityにプリロードシーン「ビルトイン」がないのは非常に紛らわしいです。

彼らは将来この概念を追加します。

繰り返すには、文字通りプリロードシーンが必要です。

とても簡単です。

これは、Unityを試す新しいプログラマーのための単一の誤解です!


幸いなことに、プリロードシーンは非常に簡単です。

ステップ1。

「preload」という名前のシーンを作成します。 Build Managerのscene 0でなければなりません。

enter image description here

ステップ2。

「プリロード」シーンで、「__ app」などと呼ばれる空のGameObjectを作成します。

単に、「__ app」にDontDestroyOnLoadを付けます。

注意:

これはプロジェクト全体の唯一の場所DontDestroyOnLoadを使用します。

とても簡単です。

enter image description here

例では、開発者は1行のDDOLスクリプトを作成しました。

そのスクリプトを「__app」オブジェクトに配置します。

DDOLについて再度考える必要はありません。

ステップ3

アプリには(多くの)「一般的な動作」があります。そのため、データベース接続、効果音、スコアリングなどのようなもの。

一般的な動作を「_app」に配置する必要があります。

本当に簡単です。

一般的な動作は-もちろん-プロジェクトのどこでも、常に、すべてのシーンで利用可能です

他にどのようにそれを行うことができますか?

上の画像の例では、「Iap」(「アプリ内購入」)などに注目してください。

すべての「一般的に必要な動作」-サウンドエフェクト、スコアリングなど-は、そのオブジェクト上にあります。

重要...

これは-もちろん、当然-

...もちろん、あなたの一般的な振る舞いには、Unityの他のすべてのものと同じように普通のインスペクターがいます。

他のすべてのゲームオブジェクトで使用するUnityの通常の機能をすべて使用できます。

もちろん、通常のInspector変数(ドラッグして接続)、設定などを使用できます。

(実際、既存のプロジェクトで作業するために雇われたとしましょう。最初に行うことは、プリロードシーンを一目見ることです。プロジェクトのすべての「一般的な動作」を1か所で見ることができます。 、スコアリング、AIなど。これらのもののすべての設定は、インスペクター変数としてすぐに表示されます...スピーチボリューム、プレイストアIDなど)。

「サウンド効果」の一般的な動作の例を次に示します。

enter image description here

「ボイスオーバー」の一般的な行動と「音楽」の一般的な行動もあるようです。

繰り返す。 「一般的な行動」について。 (効果音、スコアリング、ソーシャルなど)これらは、事前ロードシーンのゲームオブジェクトでのみ使用できます

これはオプションではありません:代替手段はありません!

できました。

正直言ってそんなに簡単です。

文字通り何もありません!

他の環境から来た多くのエンジニアはこれに追いつきます。なぜなら、それは「簡単ではない」ように思えるからです。

繰り返しますが、全体的な問題は、(奇妙なことに)Unityがプリロードシーンを "ビルドイン"するのを忘れたことです。したがって、クリックしてプリロードシーンを追加するだけです。プリロードシーンにDDOLを追加することを忘れないでください。

したがって、開発中:

常にプリロードシーンからゲームを開始します。

とても簡単です。

(重要な点:あなたのアプリは確かに「早い」シーンを持っています。例えば、「スプラッシュスクリーン」や「メニュー」。あなたはできませんプリロードシーンとして「スプラッシュスクリーン」または「メニュー」を使用します。文字通り"プリロードシーン"。プリロードシーンはthen loadスプラッシュシーン、メニューシーン、その他あなたが望む。)


中心的な問題:他のスクリプトからそれらを「見つける」:

プリロードシーンがあります。

「一般的な動作」はすべて、単にプリロードシーンにあります。

次に、 "SoundEffects"または "GameManager"と言う問題を簡単に見つけることができます。

あらゆるシーンのあらゆるゲームオブジェクトのスクリプトから、それらを簡単に見つけられる必要があります。

幸いなことにdead easyであり、1行のコードです。

それは問題ではありません:

Sound sound = Object.FindObjectOfType<Sound>();
Game game = Object.FindObjectOfType<Game>();

必要なスクリプトについては、Awakeで実行してください。

正直言ってそんなに簡単です。これですべてです。

Sound sound = Object.FindObjectOfType<Sound>();

オンラインで見られるabsolutely wrongのコード例が何百もあるため、大きな混乱が生じています。また、新しいUnityエンジニアは「信じられない」ほど簡単です!

これは本当に簡単です-正直です!

Unityが組み込みの「プリロードシーン」を追加するのを忘れたのはまったく奇妙です。SoundEffects、GameManagerなどのシステムを接続する場所です。Unityの奇妙なことの1つにすぎません。したがって、Unityプロジェクトで最初に行うことは、1回クリックするだけでプリロードシーンを作成することです。

それでおしまい!


詳細...

もっと少ない(!)行のコードを本当に入力したい場合は、非常に簡単です-これらのそれぞれに対して単にグローバルを使用できることに注意してください!

これについて詳しく説明します here (私の人気のあるGrid.csスクリプトを使用できます)および以下の@Fritsの回答で。

(グローバルを使用するか、特定のコンポーネントを必要とする各コンポーネントにローカル変数があるかは、コードスタイルの問題です。非常にまれな状況では、「グローバル」アプローチが(グローバルと同様に)実行可能です。


DylanBからの質問:(開発中「再生」をクリックする前に毎回プリロードシーンをクリックする必要があるのは非常に面倒です「これは自動化できますか?」

もちろん、すべてのチームはこれを行うための異なる方法を持っています。簡単な例を次に示します。

// this should run absolutely first; use script-execution-order to do so.
// (of course, normally never use the script-execution-order feature,
// this is an unusual case, just for development.)
...
public class DevPreload:MonoBehaviour
 {
 void Awake()
  {
  GameObject check = GameObject.Find("__app");
  if (check==null)
   { UnityEngine.SceneManagement.SceneManager.LoadScene("_preload"); }
  }
 }

しかし、忘れないでください:「他に何ができますか?」 ゲームはプレロードシーンから開始する必要があります。プレロードシーンに移動する以外に、他にできることは何ですかゲーム? 「Unityを実行するためにUnityを起動するのは面倒です-Unityの起動を回避する方法は?」と尋ねることもできます。ゲームはもちろん、絶対にプリロードシーンから開始する必要があります。確かに、Unityで作業するときは、「Playをクリックする前にプリロードシーンをクリックする」必要があります。

86
Fattie

@Fattie:これをすべて詳しく説明してくれてありがとう、素晴らしい!ただし、人々があなたに到達しようとしているという点があります。私もそれを試してみます:

モバイルゲームのすべてのインスタンス化で、すべての「グローバルクラス」ごとに「FindObjectOfType」を実行することは望ましくありません。

代わりに、静的/シングルトンのインスタンス化を、それを探すことなくすぐに使用させることができます!

そして、それはこれと同じくらい簡単です:どこからでもアクセスしたいクラスでこれを書いてください。XXXXXはクラスの名前です。

public static XXXXX Instance { get; private set; }
void Awake()
{
if (Instance == null) { Instance = this; } else { Debug.Log("Warning: multiple " + this + " in scene!"); }
}

今あなたの例の代わりに

Sound sound = Object.FindObjectOfType<Sound>();

見ずに、余分な変数を使用せずに、単にこのように使用するだけで、どこからでもすぐに使用できます。

Sound.Instance.someWickedFunction();

代わりに(技術的には同一)、通常はグリッドと呼ばれる1つのグローバルクラスを使用して、それぞれを「保持」します。 Howto 。そう、

Grid.sound.someWickedFunction();
Grid.networking.blah();
Grid.ai.blah();
20
Frits Lyneborg

好きなシーンを開始し、Unity Editorで再生ボタンを押すたびに_preloadシーンを再統合する方法を次に示します。 Unity 2017 RuntimeInitializeOnLoadMethod以降に利用可能な新しい属性があります。詳細は こちら です。

基本的に、単純なプレーンc#クラスと、RuntimeInitializeOnLoadMethodを含む静的メソッドがあります。これで、ゲームを開始するたびに、このメソッドはプリロードシーンをロードします。

using UnityEngine;
using UnityEngine.SceneManagement;

public class LoadingSceneIntegration {

#if UNITY_EDITOR 
    public static int otherScene = -2;

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    static void InitLoadingScene()
    {
        Debug.Log("InitLoadingScene()");
        int sceneIndex = SceneManager.GetActiveScene().buildIndex;
        if (sceneIndex == 0) return;

        Debug.Log("Loading _preload scene");
        otherScene = sceneIndex;
        //make sure your _preload scene is the first in scene build list
        SceneManager.LoadScene(0); 
    }
#endif
}

次に、_preloadシーンに、(開始した場所から)目的のシーンをロードバックする別のスクリプトがあります。

...
#if UNITY_EDITOR 
    private void Awake()
    {

        if (LoadingSceneIntegration.otherScene > 0)
        {
            Debug.Log("Returning again to the scene: " + LoadingSceneIntegration.otherScene);
            SceneManager.LoadScene(LoadingSceneIntegration.otherScene);
        }
    }
#endif
...
6
misher