Uso de Don'tDestroyOnLoad en Unity para Gestión de Objetos Persistentes
Resumen
Evita duplicar objetos entre escenas en Unity con un patrón sencillo y robusto: un solo player y una sola cámara persistentes, controlados por una variable estática y el método Don't Destroy On Load. Con este enfoque, mejoras el rendimiento y evitas inconsistencias al cambiar configuraciones entre escenas.
¿Por qué usar objetos persistentes entre escenas en Unity?
Mantener un único player y una única cámara evita conflictos cuando hay varias escenas con copias del mismo objeto. Si cambias el player o la configuración de la cámara en una escena, las otras no heredan esos cambios. Con Don't Destroy On Load, ambos se conservan al cargar nuevas escenas y se evita crear instancias infinitas.
Un solo estado global del player y la cámara ahorra recursos.
Coherencia visual: la configuración de la cámara se mantiene.
Evita duplicados con una bandera estática que controla la primera carga.
Base para HUD y más: el heads up display puede seguir la misma regla.
¿Cómo implementar un único player y cámara persistentes?
La clave es una variable estática en el player controller que indica si el player ya fue creado. Si no existe, se marca como creado y se aplica Don't Destroy On Load. Si ya existe, se destruye cualquier duplicado.
¿Cómo se declara y usa la variable estática?
Crea una booleana pública y estática: indica si el player fue creado.
En Start, comprueba su estado: si es la primera vez, persiste el objeto; si no, destruye el duplicado.
publicclassPlayerController:MonoBehaviour{publicstaticbool playerCreated;// por defecto: falsevoidStart(){if(!playerCreated){ playerCreated =true;// marcar primera cargaDontDestroyOnLoad(this.transform.gameObject);// persistir entre escenas// cargar animator y rigid body si aplica.}else{Destroy(gameObject);// evitar un segundo "conejito" en escena.}}}
¿Dónde aplicar Don't Destroy On Load más allá del player?
Crea un script reutilizable para cualquier objeto que deba persistir: cámara, HUD u otros. Comprueba la bandera estática del player y actúa solo en la primera carga.
Asigna este script al prefab de la Main Camera para evitar conflictos de instancia.
Elimina Update en este script: solo actúa al cargar.
En ejecución, verás una sección especial llamada Don't Destroy On Load donde aparecen el player y la cámara persistentes.
¿Qué ocurre con el teletransporte y el spawn entre escenas?
Al resolver la persistencia, el reto pasa a la sincronización: cuando cambias de escena, el player puede aparecer en una zona distinta a la esperada. La solución será relacionar cada punto de teletransporte con su punto de spawn correspondiente.
Define puntos de entrada y salida por escena.
Sincroniza el spawn del player con el portal o puerta usada.
Mantén la persistencia del player y la cámara, y solo actualiza su posición.
Esta base con Don't Destroy On Load te permite evitar cargas dinámicas infinitas de players y cámaras, y preparar el terreno para gestionar inventarios u objetos que no deban reiniciarse en cada escena.
¿Quieres que profundicemos en la lógica de teletransporte y emparejar puntos de spawn entre escenas? Cuéntame tu caso en los comentarios.
como el código tal cual se me presentaba el error, cuando inicia el juego mi cámara se destruía simplemente cambie lo que esta en el método start por el método awake.
Perfecto. Se resuelve al cambiar Start por Awake en el script Don'tDestroyOnLoad. Creo que la razón puede ser porque el script se ejecuta antes que todo, es decir, antes que el start y por ende playerHasLoaded es false para este momento.
Creo que la solución presentada es un poco compleja, y me ha dado algunos errores en versiones posteriores de Unity 2019.3.0f por lo que les dejo mi solución, esta realmente solo tiene que ser agregada sin mas al Game Object que no queramos que sea destruido en la carga de escenas.
using System.Collections;using System.Collections.Generic;using UnityEngine;publicclassDontDestroyOnLoad:MonoBehaviour{voidAwake(){// Primero creamos un Array que busque y guarde todos los objetos que contengan la misma tagGameObject[] objectsWithSameTag =GameObject.FindGameObjectsWithTag(this.gameObject.tag);// Si el largo de nuestro array es mayor que uno destruimos el objeto que intenta instanciarse,if(objectsWithSameTag.Length>1){Destroy(this.gameObject);}// Si la condicion de arriba no se cumple llamamos a DontDestroyOnLoadDontDestroyOnLoad(this.gameObject);}}
En un inicio no entendia por que lo hacia de esta manera, pero ahora que ya entendi que es lo que sucede, es un truco buenisimo. Puesto que unicamnete tendremos que poner spwan points y no camaras y personajes en cada escena, lo cual ademas de hacerlo mas facil de programar pienso que tambien dara un mejor rendimieto :D
He seguido las intrucciones, pero no me queda claro como funcionan nuestros nuevos scripts. Es decir, entiendo que en el DontDestroyOnLoad Script, estamos verificando si el PlayerController ya se creo, pero en el PlayerController, estamos cambiando de estado la variable playerCreated a true en caso de que por alguna extraña razón esta, esté falsa No entiendo cuál es el verdadero sentido de tenerla ¿Alguien me puede ayudar a entender?
Se cambia a true para que destruya los duplicados, col el else de destroy elimina el player duplicado y el objeto que trae el script en esta caso la camara.
Entendido!!! gracias @zkmark9999
Yo le sigo dando vueltas al problema de que a veces cuando vuelve al MainScene el Player y la cámara aparecen en otro lado, o el player aparece en el spawn y la cámara en otro lado y tarda medio segundo en ir hasta el player.
En el script del CameraFollow le copie la linea que estaba en Update y se la pegue en el Start.
targetPosition = new Vector3(followTarget.transform.position.x, followTarget.transform.position.y,this.transform.position.z);
Con esto mejoro bastante, pero a veces me pasa lo arriba mencionado. Creo que si uno entra y sale rápido de las escenas es el problema. Quizá dando una pequeña animación en el cambio de escena, que inmovilice un segundo al Player mejoraría bastante el problema. Creo recordar que los primeros Pokemon de Game Boy tenían algo así.