1.17 Aprendizaje del desarrollo de juegos de Unity desde 0: cambio de escena

Hemos desarrollado todos los artículos anteriores en una escena de juego fija. Lo mencionamos cuando introdujimos por primera vez el concepto de escena. Esta escena puede ser un mapa, o una sala de batalla, etc., así que obviamente esta escena Puede haber más de una , y puede cambiar de una escena a otra, así que cómo cambiar escenas en Unity y cómo lidiar con la lógica del cambio de escena, este capítulo lo explicará en detalle.

crear una segunda escena

¿Recuerdas cómo crear recursos de escena primero?

En la ventana Proyecto, haga clic con el botón derecho en Crear->Escena en cualquier lugar que desee para crear un nuevo recurso de escena. Ya tenemos una escena de demostración, por lo que creamos una nueva escena llamada Otra demostración.

Bien, a continuación debemos editar el contenido de esta escena, es decir, hacer doble clic en el archivo de recursos de la escena, tenga en cuenta que si la escena actualmente abierta es Demo y tiene algunas modificaciones que no se han guardado (como el nodo raíz en la ventana Jerarquía de Demo está con un asterisco), luego Unity le preguntará si desea guardar, usted decide si guardar y luego abre el archivo de escena en el que acaba de hacer doble clic.

Después de hacer doble clic, entramos en esta escena, y todo volvió al punto original:

La única diferencia es que el nodo raíz de la ventana Jerarquía se reemplaza por nuestro nombre recién creado AnotherDemo.

Ok, entonces entendemos cómo crear una nueva escena y editar el contenido. Volvamos primero a la escena de demostración, porque necesitamos cambiar de la escena de demostración a otra demostración a través de la lógica del código.

cambio de escena

Dado que queremos cambiar de escena, debe haber algunos disparadores lógicos. Podemos escribir un componente separado y agregarlo a un GameObject vacío. El componente cambiará de escena 3 segundos después de que se active Inicio, y la API para el cambio de escena es:

https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager.LoadScene.html

Dejamos que gpt nos ayude a escribir:

using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneSwitcher : MonoBehaviour
{
    public string sceneName;
    public float delay = 3f;
    private float startTime = 0f;
    private bool isSwitching = false;

    private void Start()
    {
        startTime = Time.time;
    }

    private void Update()
    {
        if (!isSwitching)
        {
            float elapsedTime = Time.time - startTime;
            if (elapsedTime >= delay)
            {
                SceneManager.LoadScene(sceneName);
                isSwitching = true;
            }
        }
    }
}

La escritura no es mala, el retraso indica cuántos segundos esperar para que la escena cambie después de que el objeto comience a ejecutarse, sceneName es el nombre de nuestro archivo de recursos de escena, Unity es equivalente a recopilar todos nuestros recursos de archivos de escena, por lo que puede cambiar al pasar directamente el nombre, si es inteligente, definitivamente pensará qué hacer si el nombre está duplicado. Después de todo, los archivos pueden tener el mismo nombre en diferentes carpetas. La API de Unity también brinda instrucciones detalladas:

El dado sceneName puede ser solo el nombre de la escena, sin la .unity extensión, o la ruta como se muestra en la ventana BuildSettings aún sin la .unity extensión. Si solo se proporciona el nombre de la escena, se cargará la primera escena de la lista que coincida. Si tiene varias escenas con el mismo nombre pero diferentes rutas, debe usar la ruta completa.
Tenga en cuenta que sceneName no distingue entre mayúsculas y minúsculas, excepto cuando carga la escena desde un AssetBundle.

Es más claro, no traiga el sufijo del nombre de archivo, sabemos que el sufijo del nombre de archivo real del archivo de recursos de escena en el sistema operativo es .unity, no necesitamos pasar este sufijo, por ejemplo, AnotherDemo no necesita otra demostración. Si se pasa un nombre, se usará el primero que se encuentre. Si se pasa la ruta, se encontrará de acuerdo con la ruta, y la ruta es la ruta debajo de la carpeta Activos, como Escenas/Escena de muestra (de por supuesto, el que creamos no está en este clip de archivo a continuación)

Finalmente, tenga en cuenta que la ruta o el nombre aquí no distingue entre mayúsculas y minúsculas, a menos que se cargue desde un paquete de recursos En cuanto a lo que es el paquete de recursos, hablaremos de ello más adelante en el capítulo de carga de recursos.

Entonces podemos tener dos métodos de llenado para sceneName aquí:

  1. Rellene AnotherDemo directamente
  2. Rellene la ruta OtraDemo

Ambos están bien, pero primero creemos un objeto para colgar nuestros componentes:

Bien, corramos y echemos un vistazo, oye, ¿por qué no cambiaste? No se preocupe, ¿ha notado un mensaje de error rojo en la parte inferior de la interfaz del editor? Abra la ventana Consola para ver el resultado completo:

De hecho, puede ver que la escena que necesitamos para cambiar a AnotherDemo no se ha agregado a la configuración de compilación. En el artículo sobre el paquete del juego, mencionamos cómo agregar la escena a nuestro contenido empaquetado. De hecho, esta es la operación .

Abrimos desde el menú File->Build Settings en la parte superior de la ventana del editor de Unity:

No agregamos AnotherDemo, así que abramos el archivo de escena AnotherDemo de acuerdo con el método anterior y luego haga clic en el botón Add Open Scenes en el lado derecho de la ventana anterior para agregar la escena:

Bien, volvamos a abrir la escena de la demostración y luego volvamos a ejecutarla. Después de esperar 3 segundos, cambiamos al mundo nihilista en AnotherDemo.

Los objetos de cambio de escena permanecen

Después de que cambiamos con éxito, podemos entender claramente que todos los GameObjects en la escena anterior se destruyen automáticamente, lo cual es bueno, pero hay algunos GameObjects que queremos cruzar escenas, el más común es la interfaz de usuario, obviamente no podemos Porque cambiar un scene hará que toda nuestra interfaz de usuario desaparezca.

Luego, debemos marcar el GameObject que debe permanecer en existencia después de que se cambie la escena usando una API especial:

Objeto.DontDestroyOnLoad​docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html

Así es, es así de simple, solo necesitamos usar este DontDestoryOnLoad para pasar el GameObject que debemos conservar. Modifiquemos el script de cambio de escena para permitirnos establecer qué GameObjects se pueden conservar:

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneSwitcher : MonoBehaviour
{
    public string sceneName;
    public float delay = 3f;
    public List<GameObject> gameObjectToStayAlive;
    private float startTime = 0f;
    private bool isSwitching = false;

    private void Start()
    {
        startTime = Time.time;
    }

    private void Update()
    {
        if (!isSwitching)
        {
            float elapsedTime = Time.time - startTime;
            if (elapsedTime >= delay)
            {
                if (gameObjectToStayAlive != null)
                {
                    foreach (GameObject go in gameObjectToStayAlive)
                    {
                        DontDestroyOnLoad(go);
                    }
                }
                
                SceneManager.LoadScene(sceneName);
                isSwitching = true;
            }
        }
    }
}

Se puede ver que hemos agregado una matriz de GameObject gameObjectToStayAlive para almacenar los GameObjects que queremos que continúen en la siguiente escena al cambiar de escena. Antes de cambiar de escena, configuramos estos GameObjects para que no se destruyan debido al cambio de escena a través de DontDestroyOnLoad.

Luego podemos ver que en el panel del editor, podemos agregar un elemento de matriz haciendo clic en los símbolos más y menos de la matriz:

Este es en realidad un método de edición más conveniente que el editor de Unity dibuja para los miembros de la matriz de forma predeterminada.Si ha usado una versión anterior de Unity, debe saber que el método de edición anterior es muy atrasado y no es fácil de usar.

Aquí agregamos los elementos necesarios para la visualización de la interfaz de usuario. Tenga en cuenta que la configuración del elemento principal DontDestroyOnLoad también es efectiva para los elementos secundarios, por lo que no necesitamos agregar todos los elementos secundarios debajo del objeto Canvas aquí.

Ok, ¿vamos a correr y ver?

Después de cambiar la escena, podemos ver que la interfaz de usuario aún puede tener efecto y podemos observar que el GameObject que hemos configurado se ha ido a un lugar extraño:

Todos se ejecutaron bajo DontDestroyOnLoad, que en realidad es la implementación de la solución DontDestroyOnLoad de Unity, es decir, en realidad abrimos dos escenas, una es la escena AnotherDemo y la otra es la escena DontDestroyOnLoad. Estas dos escenas surten efecto al mismo tiempo. tiempo, y configuramos el GameObject de DontDestroyOnLoad. Permanecerá en la escena DontDestroyOnLoad, por lo que incluso si cambiamos a otras escenas, no afectará la lógica de destrucción de los objetos en la escena DontDestroyOnLoad.

Pero ahora hemos encontrado otro problema, la ventana de la consola está reportando errores con locura:

Parece que todos son el mismo error. Si observa más de cerca, puede comprender que este es nuestro punto de mira de la interfaz de usuario, y un componente AimController está colgado. Este componente hace referencia a la cámara en la escena para determinar la dirección de nuestro fuego, y cambiamos La escena se ha ido, la cámara en la escena de demostración citada antes ya no existe, por lo que si continuamos citando, definitivamente informará un error. Lo mismo es cierto para la referencia del componente FireController.Después de que cambiamos la escena, el GameObject donde se encuentra el componente al que se hace referencia también ha sido destruido.

Entonces, si queremos resolver este problema, debemos limpiar las referencias antiguas al cambiar de escena y hacer un buen trabajo juzgando la lógica:

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneSwitcher : MonoBehaviour
{
    public string sceneName;
    public float delay = 3f;
    public List<GameObject> gameObjectToStayAlive;
    public AimController aimController;
    private float startTime = 0f;
    private bool isSwitching = false;

    private void Start()
    {
        startTime = Time.time;
    }

    private void Update()
    {
        if (!isSwitching)
        {
            float elapsedTime = Time.time - startTime;
            if (elapsedTime >= delay)
            {
                if (gameObjectToStayAlive != null)
                {
                    foreach (GameObject go in gameObjectToStayAlive)
                    {
                        DontDestroyOnLoad(go);
                    }
                }

                if (aimController != null)
                {
                    aimController.mainCamera = null;
                    aimController.fireController = null;
                }
                
                SceneManager.LoadScene(sceneName);
                isSwitching = true;
            }
        }
    }
}

Simplemente agregamos un miembro aimController y luego esperamos que alguien pueda asignar este componente en el editor, para que podamos limpiar sin problemas las referencias que no se pueden quitar cuando se cambia la escena.

De manera similar, en el script AimController, también debemos realizar un juicio nulo en los dos miembros:

using UnityEngine;

public class AimController : MonoBehaviour
{
    public Camera mainCamera;
    public FireController fireController;

    private RectTransform rectTransform;

    private void Start()
    {
        rectTransform = GetComponent<RectTransform>();
    }

    private void Update()
    {
        rectTransform.anchoredPosition = Input.mousePosition;

        if (mainCamera == null || fireController == null)
        {
            return;
        }
        
        // 获取屏幕上当前鼠标位置(也就是准心位置)所在的3D空间位置
        Vector3 aimWorldPosition = mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,
            Input.mousePosition.y, mainCamera.nearClipPlane));
        // 通过坐标相减可以得到方向向量
        Vector3 fireDirection = aimWorldPosition - mainCamera.transform.position;
        // 归一化后传递给开火控制脚本
        fireController.SetDirection(fireDirection.normalized);
    }
}

Bien, asignemos el aimController en la escena:

Ejecútalo de nuevo y el error desaparecerá.

Desde aquí podemos ver que, aunque es muy sencillo mantener la existencia de objetos en el cambio de escena, de hecho, todavía tenemos que gestionar con cuidado todas las referencias a escenas antiguas, y todos los objetos con DontDestroyOnLoad ya no serán debidos al cambio de escena. Y destruir, por lo que, por supuesto, debemos administrar cuándo destruirlo.

preguntas de pensamiento

  1. Después de cambiar de escena, descartamos la cámara y el FireController en la escena anterior, pero definitivamente esperamos poder apuntar y disparar en la nueva escena. Ya hay una cámara principal integrada en la nueva escena, entonces, ¿qué debemos hacer? ¿Puede disparar normalmente después de cambiar de escena?
  2. En el código anterior, simplemente agregamos un AimController para implementar la lógica de limpiar referencias antiguas, pero como programador calificado, lo primero que me viene a la mente es el principio de diseño de responsabilidad única, así que piénselo, un desacoplamiento razonable Cómo deberia estar escrito?
  3. En la descripción oficial de la API para cambiar escenas, en realidad hay un claro recordatorio de que esta API de cambio es una operación síncrona de bloqueo, y se debe usar una API asíncrona.Si usamos una API asíncrona, ¿cómo debemos reescribir el código?

Siguiente capítulo

En este capítulo, tenemos una comprensión detallada de cómo cambiar de escena y qué hacer si desea mantener vivo el GameObject al cambiar de escena, y algunos problemas prácticos encontrados después de hacerlo.

Al cambiar de escena, ya aprendí de los documentos oficiales que hay palabras como AssetBundle. De hecho, este es el método de administración de recursos de Unity en el paquete oficial del juego. Cuando necesitamos cargar recursos dinámicamente en lugar de usar referencias como ahora, necesita cargar recursos dinámicamente, por lo que en el próximo capítulo explicaremos varios métodos y escenarios aplicables para cargar recursos dinámicamente dentro de Unity.

おすすめ

転載: blog.csdn.net/z175269158/article/details/130231917