La capacidad de cargar módulos de código de forma dinámica en tiempo de ejecución es crucial para implementar sistemas de actualización en caliente (hot patching) o para adaptar aplicaciones a diferentes plataformas sin reconstrucciones completas en Unity. Esta técnica permite extender la funcionalidad de una aplicación o corregir errores distribuyendo pequeños paquetes de código (DLLs) que pueden ser cargados por el motor en tiempo real. En Unity, esto se puede lograr manipulando el dominio de aplicación actual y la carga de ensamblados.
1. Creación del Módulo de Código Dinámico (Librería de Clases C#)
El primer paso es construir un ensamblado independiente que contenga la lógica que deseamos cargar dinámicamente. Para este ejemplo, crearemos una librería de clases C# que incluirá un componente de Unity (MonoBehaviour) simple. Este componente interactuará con la UI para demostrar la funcionalidad.
Asegúrese de añadir referencias a los ensamblados de Unity, específicamente UnityEngine.dll y UnityEngine.UI.dll, en su proyecto de librería de clases para poder utilizar las clases de Unity.
A continuación, se presenta un ejemplo de código para la librería dinámica:
using System;
using UnityEngine;
using UnityEngine.UI;
namespace ModuloDinamico
{
/// <summary>
/// Componente MonoBehaviour que se cargará dinámicamente.
/// Actualiza un Text UI al presionar una tecla.
/// </summary>
public class ControladorContadorUI : MonoBehaviour
{
// Campo público que será asignado desde el código de carga
public Text displayLabel;
private int currentCount = 0;
void Update()
{
// Incrementa el contador y actualiza el texto si se presiona la tecla 'B'
if (Input.GetKeyDown(KeyCode.B))
{
currentCount++;
if (displayLabel != null)
{
displayLabel.text = $"Contador Dinámico: {currentCount}";
}
}
}
}
}
Compile este proyecto para generar el archivo DLL (por ejemplo, ModuloDinamico.dll). Este archivo DLL debe ser copiado a la carpeta StreamingAssets de su proyecto de Unity. La carpeta StreamingAssets es ideal para recursos que deben ser accedidos directamente por su ruta de archivo en tiempo de ejecución.
2. Implementación del Cargador Dinámico en Unity
Una vez que el DLL está en la carpeta StreamingAssets, el siguiente paso es crear un script en Unity que se encargue de cargarlo en tiempo de ejecución, instanciar las clases necesarias y asignarles los recursos adecuados. Utilizaremos UnityWebRequest para cargar los bytes del DLL y la API de Reflexión de .NET para inspeccionar y utilizar el ensamblado cargado.
Cree un nuevo script de Unity, por ejemplo, CargadorEnsamblados, y añádalo a un GameObject en su escena. Asegúrese de tener un componente Text en su Canvas para visualizar la salida.
using System;
using System.Collections;
using System.Reflection;
using UnityEngine;
using UnityEngine.Networking; // Importante para UnityWebRequest
using UnityEngine.UI;
public class CargadorEnsamblados : MonoBehaviour
{
[SerializeField]
private Text targetUIText; // Referencia al componente Text en la UI
private const string dllFileName = "ModuloDinamico.dll";
IEnumerator Start()
{
string dllPath = System.IO.Path.Combine(Application.streamingAssetsPath, dllFileName);
Debug.Log($"Intentando cargar DLL desde: {dllPath}");
// Usamos UnityWebRequest para cargar los bytes del DLL
using (UnityWebRequest request = UnityWebRequest.Get(dllPath))
{
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.ProtocolError)
{
Debug.LogError($"Error al cargar el DLL: {request.error}");
yield break;
}
Debug.Log("DLL cargado exitosamente. Procesando ensamblado...");
Debug.Log($"Dominio de aplicación actual: {AppDomain.CurrentDomain.FriendlyName}");
// Cargar el ensamblado desde los bytes obtenidos
Assembly loadedAssembly = Assembly.Load(request.downloadHandler.data);
// Recorrer los tipos dentro del ensamblado cargado
foreach (Type type in loadedAssembly.GetTypes())
{
Debug.Log($"Tipo encontrado: {type.Namespace}.{type.Name}");
// Si encontramos una clase que hereda de MonoBehaviour
if (type.IsSubclassOf(typeof(MonoBehaviour)))
{
Debug.Log($"El tipo '{type.Name}' es un MonoBehaviour. Añadiéndolo al GameObject actual.");
// Añadir el componente MonoBehaviour dinámicamente a este GameObject
MonoBehaviour dynamicComponent = gameObject.AddComponent(type) as MonoBehaviour;
if (dynamicComponent != null)
{
// Intentar obtener el campo 'displayLabel' del componente dinámico
FieldInfo field = type.GetField("displayLabel");
if (field != null && field.FieldType == typeof(Text))
{
Debug.Log("Campo 'displayLabel' encontrado. Asignando referencia UI.");
field.SetValue(dynamicComponent, targetUIText);
}
else
{
Debug.LogError($"Campo 'displayLabel' no encontrado o no es de tipo Text en {type.Name}.");
}
}
else
{
Debug.LogError($"No se pudo añadir el componente '{type.Name}'.");
}
}
}
}
}
}
3. Configuración en la Escena de Unity y Prueba
Para probar este sistema:
- Cree un nuevo GameObject vacío en su escena de Unity (por ejemplo, "DLL Loader").
- Asigne el script
CargadorEnsambladosa este GameObject. - En la jerarquía de su escena, cree un Canvas y dentro de él un componente UI Text (por ejemplo, "Dynamic Text Display").
- Arrastre este componente Text al campo
Target UI Textdel scriptCargadorEnsambladosen el Inspector del GameObject "DLL Loader". - Asegúrese de que
ModuloDinamico.dllesté en la carpetaAssets/StreamingAssetsde su proyecto de Unity.
Al ejecutar la escena, el script CargadorEnsamblados cargará el DLL, encontrará el ControladorContadorUI y lo adjuntará al mismo GameObject. Si presiona la tecla 'B' durante la ejecución, verá cómo el texto en su UI se actualiza con el contador gestionado por el código cargado dinámicamente.