C贸mo reinventamos el trabajo con escenas en Unity

Unity, como motor, tiene una serie de desventajas que, gracias a sus capacidades de personalizaci贸n y herramientas de generaci贸n de c贸digo, se pueden solucionar.

Ahora les contar茅 c贸mo escribimos un complemento para Unity basado en proyectos de posprocesamiento y el generador de c贸digo CodeDom.

Problema

En Unity, la carga de escenas se realiza mediante un identificador de cadena. No es estable, lo que significa que se puede cambiar f谩cilmente sin consecuencias obvias. Por ejemplo, cuando cambia el nombre de una escena, todo volar谩, pero se revelar谩 solo al final de la etapa de ejecuci贸n.

El problema aparece r谩pidamente en escenas de uso frecuente, pero puede ser dif铆cil de detectar cuando se trata de escenas aditivas peque帽as o escenas que se usan con poca frecuencia.

Decisi贸n

Al agregar una escena a un proyecto, se genera una clase del mismo nombre con el m茅todo Load.

Si agregamos una escena de Men煤, se generar谩 la clase de Men煤 en el proyecto, y en el futuro podemos iniciar la escena de la siguiente manera:

Menu.Load();

S铆, el m茅todo est谩tico no es el mejor dise帽o. Pero me pareci贸 un dise帽o lac贸nico y conveniente. La generaci贸n ocurre autom谩ticamente, el c贸digo fuente de esta clase:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace IJunior.TypedScenes
{   
    public class Menu : TypedScene
    {
        private const string GUID = "a3ac3ba38209c7744b9e05301cbfa453";
        
        public static void Load()
        {
            LoadScene(GUID);
        }
    }
}

De manera amistosa, la clase debe ser est谩tica, ya que no se supone que se instancia a partir de ella. Este es un error que arreglaremos. Como puede ver en este fragmento, el c贸digo no nos aferramos al nombre, sino al GUID de la escena, que es m谩s confiable. La clase base en s铆 se ve as铆:

namespace IJunior.TypedScenes
{
    public abstract class TypedScene
    {
        protected static void LoadScene(string guid)
        {
            var path = AssetDatabase.GUIDToAssetPath(guid);
            SceneManager.LoadScene(path);
        }

        protected static void LoadScene<T>(string guid, T argument)
        {
            var path = AssetDatabase.GUIDToAssetPath(guid);

            UnityAction<Scene, Scene> handler = null;
            handler = (from, to) =>
            {
                if (to.name == Path.GetFileNameWithoutExtension(path))
                {
                    SceneManager.activeSceneChanged -= handler;
                    HandleSceneLoaders(argument);
                }
            };

            SceneManager.activeSceneChanged += handler;
            SceneManager.LoadScene(path);
        }

        private static void HandleSceneLoaders<T>(T loadingModel)
        {
            foreach (var rootObjects in SceneManager.GetActiveScene().GetRootGameObjects())
            {
                foreach (var handler in rootObjects.GetComponentsInChildren<ISceneLoadHandler<T>>())
                {
                    handler.OnSceneLoaded(loadingModel);
                }
            }
        }
    }
}

- .

- ( , ), .

, .

, Game , .

.

using IJunior.TypedScenes;
using System.Collections.Generic;
using UnityEngine;

public class GameLoadHandler : MonoBehaviour, ISceneLoadHandler<IEnumerable<Player>>
{
    public void OnSceneLoaded(IEnumerable<Player> players)
    {
        foreach (var player in players)
        {
            //make avatars
        }
    }
}

, .

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace IJunior.TypedScenes
{
    public class Game : TypedScene
    {
        private const string GUID = "976661b7057d74e41abb6eb799024ada";
        
        public static void Load(System.Collections.Generic.IEnumerable<Player> argument)
        {
            LoadScene(GUID, argument);
        }
    }
}

. .. N , N . - .

, , , .

N?

YouTube .

. , , , .

, ?

. :

public class GameArguments
{
    public IEnumerable<Player> Players { get; set; }
}

, , , .

, : , . , .

ID .

PlayerPerfs

. , PlayerPrefs . , , .

ASPNet

- View ASPNet Core. ViewData ViewModel. Unity - .

Unity , - , View ASPNet. Additive ( , , ), .

, , , , .

Proof-of-concept. , .

Repositorio de GitHub: https://github.com/HolyMonkey/unity-typed-scenes

Si est谩 interesado, le pedir茅 a Vladislav en el pr贸ximo art铆culo que cuente c贸mo trabaj贸 con Code Dom en Unity y c贸mo trabajar con el posprocesamiento usando el ejemplo de lo que discutimos hoy.




All Articles