Administrador de juegos de Unity. El script funciona solo una vez

Resuelto Amazing User asked hace 8 años • 5 respuestas

Estoy haciendo un administrador de juegos simple. Tengo un guión al que se podrá acceder desde todas las escenas del juego. Y necesito verificar los valores de sus variables después de cargar una nueva escena. Pero mi código se ejecuta solo una vez después de iniciar la simulación mientras existe un objeto con este script en todas las escenas. ¿Lo que está mal? ¿Por qué no funciona después de cargar una nueva escena?

Amazing User avatar Mar 09 '16 18:03 Amazing User
Aceptado

En cada proyecto de Unity debes tener UNA ESCENA DE PRECARGA.

Es bastante confuso que Unity no tenga una escena de precarga "incorporada".

Agregarán este concepto en el futuro.

Ahora tienes que hacer clic para agregar una escena de precarga tú mismo.

¡Este es el MAYOR MALENTENDIDO para los nuevos programadores que prueban Unity!


Afortunadamente, es extremadamente fácil tener una escena precargada.

Paso 1.

Crea una escena llamada "precarga". Debe ser la escena 0 en Build Manager.

ingrese la descripción de la imagen aquí

Paso 2.

En la escena de "precarga", cree un GameObject vacío llamado, por ejemplo, "__app".

Simplemente, pon DontDestroyOnLoad'__app'.

Nota:

Este es el único lugar que utiliza en todo el proyectoDontDestroyOnLoad .

Es así de simple.

ingrese la descripción de la imagen aquí

En el ejemplo: los desarrolladores han creado un script DDOL de una línea.

Coloque ese script en el objeto "__app".

Nunca más tendrás que pensar en DDOL.

Paso 3

Su aplicación tendrá (muchos) "comportamientos generales". Cosas como conectividad de bases de datos, efectos de sonido, puntuación, etc.

Debes, y sólo puedes, poner tus comportamientos generales en "_app".

Es realmente así de simple.

Los comportamientos generales están entonces, por supuesto, disponibles en todas partes del proyecto, en todo momento y en todas las escenas .

¿De qué otra manera podrías hacerlo?

En el ejemplo de imagen anterior, observe "Iap" ("compra desde la aplicación") y los demás.

Todos sus "comportamientos generalmente necesarios" (efectos de sonido, puntuación, etc.) están ahí, en ese objeto.

Importante...

Esto significa que -por supuesto, naturalmente-

...tus comportamientos generales tendrán inspectores ordinarios, como todo lo demás en Unity.

Puedes utilizar todas las funciones habituales de Unity, que utilizas en todos los demás objetos del juego. Inspector de variables, arrastrar para conectar, configuraciones, etc.

(De hecho: supongamos que lo contrataron para trabajar en un proyecto existente. Lo primero que hará será echar un vistazo a la escena de precarga. Verá todos los "comportamientos generales" en la escena de precarga: efectos de sonido, puntuación, IA. , etc, etc. Verá instantáneamente todas las configuraciones para esas cosas como variables del Inspector... volumen de voz, ID de Play Store, etc, etc.)

A continuación se muestra un ejemplo de comportamiento general de "efectos de sonido":

ingrese la descripción de la imagen aquí

Parece que también hay un comportamiento general de "voz en off" y un comportamiento general de "música".

Repetir. Respecto a tus "comportamientos generales". (Efectos de sonido, puntuación, redes sociales, etc., etc.) Estos SÓLO PUEDEN IR a un objeto del juego en la escena de precarga.

Esto no es opcional: ¡no hay alternativa!

Es fácil.

A veces, los ingenieros que vienen de otros entornos se quedan atrapados en esto, porque parece que "no puede ser tan fácil".

Para repetir, Unity simplemente se olvidó de "integrar" una escena de precarga. Entonces, simplemente haga clic para agregar su escena de precarga. No olvides agregar el DDOL.

Entonces, durante el desarrollo:

Comienza siempre tu juego desde la escena de precarga.

Es así de simple.

Importante: su aplicación seguramente tendrá escenas "tempranas". Ejemplos:

  • "Pantalla de bienvenida"
  • "menú"

Nota. NO PUEDES usar el menú bienvenida o el menú como escena de precarga. Tienes que tener literalmente una escena de precarga separada .

La escena de precarga luego cargará su presentación o menú u otra escena inicial.


La cuestión central: "encontrar" aquellos de otros guiones:

Entonces tienes una escena de precarga.

Todos sus "comportamientos generales" están simplemente en la escena de precarga.

Lo siguiente que tienes que hacer es encontrar, simplemente, "SoundEffects".

Tienes que poder encontrarlos fácilmente, desde cualquier guión, en cualquier objeto del juego, en cualquiera de tus escenas.

Afortunadamente es muy fácil , es una línea de código.

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

Hazlo en Awake, para cualquier script que lo necesite.

Sinceramente, es así de simple. Eso es todo al respecto.

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

Surge una tremenda confusión debido a los cientos de ejemplos de códigos absolutamente incorrectos que se ven en línea.

Realmente es así de fácil: ¡honesto!

Es extraño que Unity se haya olvidado de agregar una "escena de precarga" incorporada, en algún lugar para conectar sus sistemas como SoundEffects, GameManager, etc. Es solo una de esas cosas raras de Unity. Entonces, lo primero que debe hacer en cualquier proyecto de Unity es simplemente hacer clic una vez para crear una escena de precarga.

¡Eso es todo!


Un detalle...

Tenga en cuenta que, si realmente desea escribir incluso menos (!) líneas de código, es notablemente fácil: ¡puede usar un global para cada una de estas cosas!

Esto se explica en detalle aquí , mucha gente ahora usa algo como esto, un script Grid.cs...

 using Assets.scripts.network;
 using UnityEngine;
 
 static class Grid
 {
     public static Comms comms;
     public static State state;
     public static Launch launch;
     public static INetworkCommunicator iNetworkCommunicator;
     public static Sfx sfx;
 
     static Grid()
     {
         GameObject g = GameObject.Find("_app");
 
         comms = g.GetComponent<Comms>();
         state = g.GetComponent<State>();
         launch = g.GetComponent<Launch>();
         iNetworkCommunicator = g.GetComponent<INetworkCommunicator>();
         sfx = g.GetComponent<Sfx>();
     }
 }

Luego, en cualquier parte del proyecto puedes decir

Grid.sfx.Explosions();

Es así de fácil, eso es todo.

No olvide que cada uno de esos "sistemas generales" está activado, y sólo puede estar activado, el objeto del juego DDOL en la escena de precarga.


DylanB pregunta: " Durante el desarrollo , es bastante molesto tener que hacer clic en la escena de precarga cada vez antes de hacer clic en "Reproducir". ¿Se puede automatizar esto?"

Claro, cada equipo tiene una forma diferente de hacer esto. He aquí un ejemplo trivial:

// 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"); }
  }
 }

Pero no lo olvides: ¿qué más puedes hacer? Los juegos deben comenzar desde una escena de precarga. ¿Qué más puedes hacer, aparte de hacer clic para ir a la escena de precarga, para iniciar el juego? También se podría preguntar "es molesto iniciar Unity para ejecutar Unity. ¿Cómo evitar iniciar Unity?" Los juegos, por supuesto, simplemente tienen que comenzar desde una escena de precarga: ¿de qué otra manera podría ser? Por supuesto, tienes que "hacer clic en la escena de precarga antes de hacer clic en Reproducir" cuando trabajas en Unity. ¿De qué otra manera podría ser?

Fattie avatar Mar 09 '2016 12:03 Fattie

@Fattie: Gracias por explicar todo esto, ¡es genial! Sin embargo, hay un punto en el que la gente está intentando comunicarse contigo, y yo también lo intentaré:

¡No queremos que cada instancia de todo en nuestros juegos móviles haga un "FindObjectOfType" para todas y cada una de las "clases globales"!

En su lugar, puede hacer que use una instanciación de un estático/un Singleton de inmediato, ¡sin buscarlo!

Y es tan simple como esto: Escribe esto en qué clase quieres acceder desde cualquier lugar, donde XXXXX es el nombre de la clase, por ejemplo "Sonido"

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

Ahora en lugar de tu ejemplo

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

Simplemente úselo, sin mirar y sin variables adicionales, simplemente así, desde cualquier lugar:

Sound.Instance.someWickedFunction();

Alternativamente (técnicamente idéntico), simplemente use una clase global, generalmente llamada Grid, para "retener" cada uno de ellos. Cómo . Entonces,

Grid.sound.someWickedFunction();
Grid.networking.blah();
Grid.ai.blah();
Frits Lyneborg avatar Nov 02 '2017 09:11 Frits Lyneborg

Así es como puedes iniciar cualquier escena que desees y asegurarte de reintegrar tu escena _preload cada vez que presiones el botón de reproducción en el editor de Unity. Hay un nuevo atributo disponible desde Unity 2017 RuntimeInitializeOnLoadMethod, más información aquí .

Básicamente tienes una clase C# plana simple y un método estático RuntimeInitializeOnLoadMethoden ella. Ahora, cada vez que inicies el juego, este método cargará la escena de precarga por ti.

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
}

Luego, en su escena _preload tiene otro script que volverá a cargar la escena deseada (desde donde comenzó):

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

        if (LoadingSceneIntegration.otherScene > 0)
        {
            Debug.Log("Returning again to the scene: " + LoadingSceneIntegration.otherScene);
            SceneManager.LoadScene(LoadingSceneIntegration.otherScene);
        }
    }
#endif
...
kolodi avatar Aug 29 '2018 13:08 kolodi

Una solución alternativa de mayo de 2019 sin _preload:

https://low-scope.com/unity-tips-1-dont-use-your-first-scene-for-global-script-initialization/

He parafraseado el blog anterior a un instructivo a continuación:

Cargando un recurso prefabricado estático para todas las escenas

Al Project > Assetscrear una carpeta llamada Resources.

Cree una Maincasa prefabricada a partir de un espacio vacío GameObjecty colóquela en la Resourcescarpeta.

Cree un Main.csscript C# en su Assets > Scriptso donde sea.

using UnityEngine;

public class Main : MonoBehaviour
{
    // Runs before a scene gets loaded
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    public static void LoadMain()
    {
        GameObject main = GameObject.Instantiate(Resources.Load("Main")) as GameObject;
        GameObject.DontDestroyOnLoad(main);
    }
    // You can choose to add any "Service" component to the Main prefab.
    // Examples are: Input, Saving, Sound, Config, Asset Bundles, Advertisements
}

Agréguelo Main.csal MainPrefab en su Resourcescarpeta.

Observe cómo se usa RuntimeInitializeOnLoadMethodjunto con Resources.Load("Main")y DontDestroyOnLoad.

Adjunte cualquier otro script que deba ser global en todas las escenas a esta casa prefabricada.

Tenga en cuenta que si vincula otros objetos del juego de escena a esos scripts, probablemente desee usar algo como esto en la Startfunción para esos scripts:

if(score == null)
    score = FindObjectOfType<Score>();
if(playerDamage == null)
    playerDamage = GameObject.Find("Player").GetComponent<HitDamage>();

O mejor aún, utilice un sistema de gestión de activos como Addressable Assets o Asset Bundles .

phyatt avatar May 14 '2020 04:05 phyatt