Saltar al contenido
SheetForge

Capacidades y límites

Esta página enumera todo lo que SheetForge no hace, todavía no puede hacer, o hace de forma diferente a lo que podrías esperar, con el motivo, la solución alternativa, y si hay margen futuro.

Formato por entrada: Qué / Por qué / Solución alternativa (+ margen futuro cuando sea relevante).


1. Plataforma y dependencias

Addressables es obligatorio — la canalización se bloquea sin él

  • Qué: la carga por dirección es la ruta de tiempo de ejecución, y el tipo AssetRef@Group necesita com.unity.addressables, así que el paquete debe estar instalado para usar el asset. El asset en sí compila sin él — todo el código que usa Addressables está protegido detrás de un version-define SHEETFORGE_ADDRESSABLES que solo se activa cuando el paquete está presente.
  • Comportamiento cuando está ausente: toda la canalización (importación · exportación · push · reflejo de creación) queda bloqueada, no degradada — ejecutar cualquier punto de entrada muestra un aviso de instalación y se detiene. No hay una ruta parcial o silenciosa (no existe, por ejemplo, un "omitir la validación de claves de asset"). Como el Editor compila sin el paquete, nunca entra en Safe Mode: la ventana Primeros pasos se abre con normalidad, y su fila de Addressables muestra ✗ con un botón Open Package Manager. El bootstrap sin dependencias SheetForge.Setup permanece como una red de orientación para otros fallos de compilación no relacionados.
  • En Data Studio sin el paquete: el botón selector ⊙ de la celda de asset y su destino de arrastrar y soltar quedan deshabilitados, con el motivo como tooltip; escribir una dirección en la celda a mano sigue funcionando.
  • Por qué no una abstracción dual Resources/Addressables: deliberadamente no se construyó — se consideró sobreingeniería.
  • Solución alternativa: instala Addressables. El aviso de importación del Asset Store lo gestiona antes de la compilación; si lo omitiste, el Editor, que compila, te guía para instalarlo.
  • Verificación: ambas ramas se ejercitan en vivo — con los version-defines eliminados (simulando "Addressables ausente"), el producto y los ensamblados de prueba compilan con 0 errores; restaurados, 0 errores y 0 advertencias. Verificado de forma independiente de extremo a extremo importando el asset en un proyecto nuevo sin Addressables instalado: el proyecto compila y la ventana de orientación de instalación aparece según lo diseñado.

Unity Localization es opcional — solo la sincronización de StringTable espera por él

  • Qué: las hojas de localización (@loc), las referencias LocRef, las constantes de clave, los informes de cobertura, Export, Push, xlsx y la app web funcionan todos sin tener com.unity.localization instalado. Lo único que espera es la salida de sincronización de StringTable: muestra un aviso de instalación (una vez por sesión) y se detiene. Todo el código que toca el paquete está detrás de un version-define SHEETFORGE_LOCALIZATION, así que cada ensamblado y cada línea de código generado compila sin el paquete — los campos generados son la struct LocRef sencilla, nunca un tipo del paquete.
  • Comportamiento en su ausencia: nada se degrada y nada se omite en silencio en ningún otro sitio — las hojas siguen siendo hojas completas; solo la salida de sincronización queda bloqueada, con el motivo mostrado.
  • Versión compatible: 1.5 o más reciente.
  • Solución alternativa: instala el paquete cuando quieras las tablas; todo lo creado antes de ese momento se sincroniza en la siguiente importación completada. Consulta Hojas de localización.

No existe una instalación programática de un solo clic

  • Qué: la ventana de red de seguridad te guía; no instala el paquete ella misma. La misma regla cubre el aviso de instalación de Unity Localization.
  • Por qué: las reglas de publicación del Asset Store restringen la modificación programática de paquetes; una ventana de orientación es la opción segura y conforme a las normas.

El define de detección de producto SHEETFORGE no se elimina automáticamente

  • Qué: el ensamblado Editor autorregistra un símbolo de definición de scripting SHEETFORGE en cada build target para que otros assets puedan detectar que SheetForge está instalado en tiempo de compilación (consulta Creación de plugins ▸ Detectar SheetForge desde otro asset). El registro es idempotente (se añade solo cuando falta — sin recompilaciones repetidas una vez presente).
  • Límite: si más adelante eliminas el asset, ese define permanece — el código que notaría la eliminación desaparece junto con él.
  • Solución alternativa: elimínalo a mano en Project Settings ▸ Player ▸ Scripting Define Symbols (por plataforma). Deliberadamente no mantenemos un watcher en segundo plano solo para limpiar un símbolo. Esto es distinto de SHEETFORGE_ADDRESSABLES, un version-define interno que solo refleja si el paquete Addressables está presente.

2. Origen de Google Sheets

El modo ExportUrl es de solo lectura

  • Qué: Push, reflejo, edición de estructura y eliminación están todos deshabilitados en el modo ExportUrl.
  • Por qué: es la ruta de exportación sin autenticación, compartida por enlace — de solo lectura por naturaleza. Push siempre requiere credenciales de SheetsApi, verificado antes de cualquier llamada de red.
  • Solución alternativa: usa el modo SheetsApi (cuenta de servicio — consulta Configuración de Hojas de Google) para cualquier escritura de vuelta.

ExportUrl requiere un mapa de gid

  • Qué: un mapa de gid vacío hace fallar la importación; los gid duplicados se rechazan.
  • Por qué: una URL de exportación sin gid devuelve silenciosamente solo la primera pestaña, una trampa de corrupción silenciosa, así que la importación la rechaza. SheetsApi descubre las pestañas automáticamente.
  • Solución alternativa: registra el valor #gid= de cada pestaña, o usa SheetsApi.

Push elimina filas por clave, y solo las verificadas

  • Qué: un registro eliminado localmente se quita de la hoja en vivo al hacer push — después de que la obtención previa al envío confirme que su clave todavía está en la fila que vio tu importación. Una fila que ya desapareció cuenta como hecha (Push idempotente al reintentar); una clave encontrada en una fila distinta se omite con un aviso, nunca se elimina por posición. Las eliminaciones se listan en su propia sección del resumen de aprobación y se envían al final, de abajo hacia arriba por pestaña.
  • Por qué: emparejar por clave contra la hoja en vivo es lo que hace segura la eliminación en una hoja que puede haberse desviado; todo lo que el emparejamiento no pueda confirmar se deja intacto.
  • Límite: un origen sin la capacidad de eliminar filas (un proveedor personalizado que nunca la adquirió) recurre al comportamiento anterior — la eliminación se reporta y la fila en vivo se deja para que tú la elimines.

Push omite las celdas en conflicto (por diseño)

  • Qué: las celdas editadas por un tercero desde tu importación, las filas cuya clave se movió de forma ambigua, las filas faltantes, o las claves duplicadas en vivo, se omiten con advertencias — no se sobrescriben.
  • Por qué: esto es la red de seguridad funcionando: las celdas enviadas son válidas; las omisiones protegen los cambios de otras personas y evitan escrituras en la fila equivocada.
  • Solución alternativa: revisa los recuentos de aplicadas/omitidas en el informe; vuelve a importar para reconciliar, y luego haz Push de nuevo. (Una UI de resolución de conflictos sería una funcionalidad separada — no está planificada.)

Push requiere una columna clave

  • Qué: una pestaña modificada sin columna clave RecordId no se puede enviar mediante Push — es un error de plan que bloquea todo el Push (cero envío para todas las pestañas; sin envío parcial).
  • Por qué: Push vuelve a localizar las filas por clave en la hoja en vivo; sin una clave, la protección contra fila equivocada no puede sostenerse.
  • Solución alternativa: añade una columna clave, o exporta a un archivo y pega.

3. Origen xlsx

  • El renombrado de pestaña excluye las pestañas de origen xlsx — protección de libros multi-hoja. Renombra en el libro, vuelve a importar.

  • La propagación de renombrado de clave hacia una pestaña de origen xlsx bloquea todo el lote — la ruta xlsx no puede hacer actualizaciones de celda quirúrgicas de forma segura, y nunca se permite un reflejo parcial. Edita esa pestaña directamente, vuelve a importar.

  • Las celdas no representables se rechazan — celdas de fórmula sin valores en caché, celdas de error, y tabulaciones/saltos de línea dentro de una celda. El lector OOXML integrado es deliberadamente mínimo (cero código de terceros). Materializa las fórmulas; usa ; para las listas.

  • Solo valores — las fórmulas, fechas y formato se interpretan, con honestidad — una celda de fórmula aporta su valor en caché (nunca se recalcula), una celda con formato de fecha se lee como texto visible yyyy-MM-dd, y los formatos numéricos, las celdas combinadas y los gráficos no se importan. El diálogo de importación de la app web nombra lo que realmente ocurrió en una nota "Cómo se leyó este libro"; en el editor la misma política se aplica en silencio por celda (los rechazos de arriba siguen reportándose por celda).

  • Algunas reglas de listas desplegables exportadas no se pueden llevar — la exportación es un solo libro, así que la lista desplegable de una columna de referencia se escribe como un rango real sobre la columna clave de la hoja destino, el mismo significado que tiene la regla de Google. Tres casos se siguen dejando fuera, nombrados juntos en una sola advertencia DropdownNotSupportedByFormat:

    • un miembro de la lista que contiene una coma (el separador en línea lo dividiría);
    • una lista en línea que supera el límite de 255 caracteres del formato (comillas incluidas);
    • un rango cuya pestaña destino no está en el libro.

    Los valores se exportan completos de todas formas. Consulta Fuentes, exportación y envío.

4. Creación — Data Studio

Una pestaña sin columna clave no admite registros nuevos, y sus ediciones de valor pierden su ancla

  • Qué: una pestaña sin columna clave RecordId se importa y se muestra con normalidad, y la edición de estructura funciona por completo — añadir, eliminar, renombrar, reordenar columnas y marcadores, además del renombrado y la eliminación a nivel de hoja. Lo que no puede ganar es un registro nuevo, ya que un registro sin clave no se puede nombrar ni referenciar:

    • el control de añadir fila está deshabilitado;
    • el selector de referencia se niega a crear ahí ("… no tiene columna clave, así que no se puede crear un registro nuevo ahí");
    • una acción de lienzo o de inspector que intenta escribir en ella no hace nada.

    Las celdas de valor son editables — pero sin una clave con la que dirigir la fila, la edición se prepara únicamente contra la posición de la fila.

  • Por qué: cada edición preparada normalmente se direcciona de forma lógica, como (tab, record key, field), y se vuelve a resolver contra la hoja justo antes de escribirse. Eso es lo que permite que una edición sobreviva a una reimportación, a un reordenamiento de filas, o a que alguien más inserte filas por encima de ella. Sin columna clave no existe esa dirección, así que la edición pasa fijada a un número de fila en su lugar — fuera de esa red de seguridad.

    Así que una edición anclada por posición puede terminar en la fila equivocada si las filas de la hoja se mueven debajo de ti antes de que escribas de vuelta (una reimportación, o alguien editando el origen directamente). Prepara y refleja esto en lotes cortos.

  • Solución alternativa: añade una columna RecordId (la edición de estructura está disponible, así que puedes hacerlo en la misma ventana), refleja, y la pestaña se vuelve completamente autorable con el ancla lógica de vuelta en su lugar. Las pestañas sin clave siguen siendo perfectamente válidas para importar — este es un límite de creación, no de esquema.

El renombrado de columna / cambio de @type rompe el código de juego que la referencia; irreversible después del reflejo

  • Qué: el nombre/tipo del campo generado cambia; el código de juego que lo referencia debe actualizarse a mano. Ctrl+Z funciona solo antes del reflejo.
  • Por qué: tipado fuerte — el campo es parte del esquema generado. Una ruptura de compilación es capturada por el aborto seguro de la cadena automática con una frase accionable. Los valores de la columna se preservan por completo (solo cambian las celdas de marcador).
  • Solución alternativa: el diálogo de confirmación advierte primero; actualiza tu código y deja que la siguiente importación se reanude.

El renombrado de pestaña rompe el código de juego que la referencia; irreversible después del reflejo

Misma mecánica que arriba — el nombre de la clase generada cambia (FooDatabaseBarDatabase); la reimportación se encarga automáticamente de toda la limpieza del lado del asset (clase antigua, SO, dirección).

Los renombrados mutuos (intercambio) y cíclicos de pestaña son compatibles

  • Qué: Alpha→Beta + Beta→Alpha (un intercambio), y ciclos más largos (A→B→C→A), se pueden preparar y reflejar en un solo lote — preparar cualquiera de las dos mitades primero funciona, y la barra de pestañas muestra los nombres intercambiados de inmediato (WYSIWYG, se puede deshacer). El control de la UI usa unicidad del conjunto de nombres final (solo se rechaza un conflicto real — dos renombrados que apuntan al mismo nombre); el reflejo lo impone estrictamente.
  • Las referencias siguen a los datos (la identidad de la pestaña), no al nombre: después de un intercambio A↔B, RecordId@A se reescribe atómicamente a RecordId@B (una sola pasada — nunca se aplica dos veces), de modo que sigue apuntando a los mismos datos, que se movieron a B.
  • Local: un intercambio permuta el contenido de los dos archivos en una sola pasada de escritura; una cadena que reutiliza un nombre con una extensión diferente elimina el archivo obsoleto de la extensión antigua (protección de eliminación basada en ruta), de modo que la reimportación nunca ve una pestaña duplicada.
  • Google: los cambios de título están ordenados topológicamente y rompen cualquier ciclo con un título temporal (A→tmp, B→A, tmp→B), de modo que la hoja en vivo nunca tiene un título duplicado momentáneo. Si un cambio de título falla a mitad de la secuencia, la pestaña que queda con un nombre temporal se reporta con orientación de recuperación.

Caso límite exclusivo de Google: las pestañas intercambiadas que se referencian entre sí no se recolocan

  • Qué: cuando las dos pestañas intercambiadas se referencian entre sí (la pestaña A tiene una columna RecordId@B y la pestaña B tiene una columna RecordId@A), la ruta de Google preserva su contenido mediante el cambio de título en el mismo lugar y no reescribe sus propias celdas @type — así que esa referencia mutua no se recoloca en Google.
  • Por qué: Google renombra una pestaña cambiando su título (el contenido no se toca, por diseño); reescribir la propia cuadrícula de la pestaña renombrada anularía eso. Los orígenes locales reescriben la proyección de la pestaña renombrada, así que lo local maneja esto por completo. Las referencias desde una tercera pestaña se recolocan en ambas rutas.
  • Solución alternativa: en Google, encamina la referencia mutua a través de una tercera pestaña, o refleja el intercambio mediante un nombre intermedio.

La superposición de SO en preparación ya no tiene botón

  • Qué: la superposición de "previsualizar valores en preparación sobre los SO" (EphemeralSoApply) la impulsaba un botón del Banco de trabajo, y esa ventana ha desaparecido. El tipo sigue siendo API pública para una herramienta que la quiera; el interruptor de edición de prueba del inspector del SO cubre el caso cotidiano de probar una cifra en tiempo de ejecución.
  • Límite si la llamas de todos modos: la superposición rechaza — con una insignia — (a) las columnas pendientes/nuevas y (b) las celdas con error de análisis. Las filas nuevas sí son compatibles. Reutiliza la ruta real de análisis+bake, así que lo que no puede calcular con veracidad lo rechaza en lugar de simularlo.
  • Solución alternativa: siempre fue solo una vista previa; refleja normalmente para el cambio real. La reimportación siempre restaura la verdad.

Las ediciones en preparación se aíslan cuando su fila se renombra externamente, se elimina, o entra en conflicto de clave

  • Qué: una edición en preparación cuya fila fue renombrada externamente, eliminada externamente, o entró en conflicto de clave entre la preparación y el reflejo, se excluye del reflejo y se marca como "aislada".
  • Por qué: su dirección lógica no se puede volver a resolver — pero no se descarta en silencio ni se le permite bloquear la sesión.
  • Solución alternativa: descártala individualmente (tras confirmación) y vuelve a prepararla.

La propagación de renombrado de clave cubre solo las celdas del baseline

  • Qué: el texto que acabas de escribir en el mismo lote que hace referencia a la clave antigua no se reescribe automáticamente.
  • Por qué: reescribir en silencio la entrada recién escrita por el usuario está prohibido; en su lugar, la validación previa detecta la referencia colgante.
  • Solución alternativa: corrige tú mismo la referencia en preparación, o refleja primero el renombrado.

Las entradas restantes tratan todas sobre Data Studio, la única ventana de creación. La antigua ventana Banco de trabajo ha sido eliminada; las tres funciones que solo ella ofrecía se trasladaron primero a Studio y al inspector de ajustes — consulta qué pasó con el Banco de trabajo.

Una hoja que falló la validación se abre para editar — pero Export, Push y las builds permanecen bloqueados

  • Qué: si el origen se leyó por completo, sus hojas se guardan como el baseline aunque la validación haya fallado, así que Studio puede abrirlas y puedes corregir los errores ahí mismo. La generación de código y el bake no se ejecutan hasta que el recuento de errores llega a cero, y Export, Push a la hoja en vivo, y las builds del jugador se rechazan todos mientras la hoja está en ese estado, cada uno indicando por qué.
  • Por qué: esas tres salidas combinan los últimos valores generados mediante bake con éxito con las hojas más nuevas. Ejecutar una ahora empalmaría valores obsoletos sobre celdas que alguien ya corrigió — una reversión silenciosa. Bloquear las salidas es lo que permite que la entrada permanezca abierta.
  • Reflejar una corrección pregunta una vez: en una hoja en cuarentena, la escritura de vuelta muestra una confirmación extra, porque pre-flight no puede ser la barrera estricta ahí (la hoja ya tiene errores). Todo lo que se encuentra se reporta como advertencia en ese reflejo, y la reimportación automática vuelve a validar toda la hoja. Las hojas sanas no se ven afectadas — pre-flight se sigue negando a escribir.
  • Solución alternativa: corrige cada error reportado y vuelve a hacer pull. El bloqueo se levanta por sí solo en el único lugar que lo borra, una ejecución que se completa pasando por el bake.

El orden y el filtro de Data Studio son solo de visualización — y deshabilitan el reordenamiento de filas mientras están activos

  • Qué: el orden por hoja de Studio (cualquier columna, asc/desc, persistido por proyecto) y el filtro de texto cambian solo el orden de visualización. El margen conserva los números de fila reales de la hoja, y ninguno de los dos afecta a la preparación, el reflejo, el push o la exportación. Mientras un orden o filtro está activo, las herramientas de reordenamiento de fila ▲▼ están deshabilitadas con un tooltip.
  • Por qué: reordenar por "vecino visible" mientras la vista está ordenada o filtrada movería filas en silencio junto a líneas que el usuario no puede ver. Los cambios reales de orden de fila son una operación de estructura — despeja primero el orden/filtro.
  • Nota: "ordenar por más reciente" solo existe si tu hoja tiene una columna que lo codifica (por ejemplo, un IntId o una columna de string tipo fecha) — la hoja en sí no almacena marcas de tiempo.

Los problemas de Data Studio son un borrador mientras un renombrado de clave está en preparación

  • Qué: mientras una celda de clave (RecordId) tiene una edición preparada, el panel Problems lleva una insignia de draft, y las entradas de referencia sin resolver en él pueden ser falsas alarmas.
  • Por qué: la vista previa en memoria no aplica la propagación de renombrado de clave — eso se ejecuta en el momento del reflejo, en todas las pestañas. En lugar de ocultar los diagnósticos o simular la propagación, la ventana te dice que la lista es un borrador hasta que el renombrado se escriba.
  • Solución alternativa: refleja el renombrado (la propagación se ejecuta con su propia confirmación), y luego lee la lista actualizada.

La tabla se virtualiza por fila por encima de 200 filas — con dos casos límite que vale la pena conocer

  • Qué: por encima de 200 filas, la tabla construye elementos de fila solo para la ventana visible (más doce filas de overscan), con espaciadores arriba y abajo que sostienen la altura total real para que la barra de scroll no mienta. Desplazarse a través de un límite de ventana reutiliza las filas que sobreviven y construye solo las que entraron. La cuadrícula del navegador hace lo mismo, con el mismo umbral.

    Dos casos siguen construyendo todo. En 200 filas o menos, cada fila se construye exactamente como antes, bit a bit. Lo mismo ocurre con una tabla cuya altura de viewport no se puede consultar en absoluto — una que está fuera de una ventana, donde el layout nunca llega — porque ahí el fallback honesto es "construir todo". Una tabla grande que simplemente todavía no se ha dispuesto espera un frame en su lugar, así que se virtualiza desde su primer pintado en lugar de construir todo y tirarlo.

  • La fila que estás editando permanece viva incluso después de desplazarse fuera de vista, así que el cursor, el foco y lo que escribiste sobreviven. Ese mantenimiento tiene un límite de distancia, y más allá de él el editor abierto confirma y pierde el foco en lugar de mantenerse indefinidamente. Nada se pierde cuando eso ocurre — el valor ya está en la sesión de preparación.

  • Solo la creación de elementos se virtualiza. El muestreo de ancho de columna, la búsqueda, el orden, las coordenadas y la superposición de preparación siguen considerando cada fila, porque cada uno de ellos daría una respuesta distinta si solo mirara lo que está en pantalla. Así que cambiar a una hoja muy grande sigue haciendo un trabajo proporcional a su tamaño; lo que ya no hace es construir miles de widgets.

  • En el navegador, el modo virtualizado mide las columnas en lugar de dejar que lo haga el layout. Los anchos de auto-layout se calcularían a partir de las filas que estuvieran en la ventana en ese momento, así que una columna temblaría al desplazarte. En modo virtualizado los anchos vienen de una estimación basada en datos sobre todas las filas y luego se fijan. El modo de renderizado completo (≤ 200 filas) sigue usando auto-layout, sin cambios.

El lienzo solo se desplaza dentro de su rango de scroll, y sus cables discontinuos de ciclo se vuelven más gruesos al crecer

  • Qué:

    • Ctrl/Cmd + rueda del ratón hace zoom en el lienzo de registros entre 25 % y 200 %, manteniendo fijo el punto bajo el cursor — un zoom anclado al centro deslizaría fuera de pantalla la tarjeta que estabas mirando. El porcentaje en el encabezado del lienzo es un botón que vuelve a 100 %. Una rueda normal sigue haciendo scroll.
    • Arrastrar con el botón central del ratón — o Alt + botón izquierdo, para hardware sin uno — desplaza la vista (pan), y el cursor marca el agarre mientras se mantiene. El arrastre con el botón izquierdo se deja para seleccionar y enlazar, así que no podía significar también "mover la vista".
    • El panel es una vista con scroll, así que el rango de paneo es el rango de scroll: se detiene en el borde del contenido en lugar de derivar hacia el espacio vacío, y cuando el contenido es más pequeño que el viewport no se mueve en absoluto. Esto no es un lienzo infinito.
    • Un cable discontinuo que marca un ciclo limita cuántos guiones dibuja y duplica su periodo de guion en un trayecto largo, así que un bucle muy largo se lee más grueso en lugar de más nítido.
  • Por qué: Unity asigna vértices de malla por llamada de dibujo (draw call) con un techo estricto de 65,535, y sobrepasarlo hace que el dibujo desaparezca por completo aunque se siga pagando por la teselación. El límite de guiones mantiene un Stroke dentro de ese presupuesto por diseño.

    La cuadrícula de puntos de fondo solía estar en el mismo precipicio y ya no lo está. Es un pequeño mosaico de fondo repetido, que cuesta cero vértices y se repinta en tiempo constante sin importar cuánto crezca el lienzo. (Un punto dibujado como un path cuesta 28 vértices medidos, no sus cuatro esquinas. Esa es la aritmética detrás del presupuesto de 1,800 puntos del fallback de dibujo, y la razón por la que el mosaico es la vía que se distribuye.)

  • Solución alternativa: ninguna necesaria para la cuadrícula. Para un vecindario grande, aleja el zoom, reduce el segmento de dirección, o abre un vecino como el nuevo término en lugar de intentar encajar todo en una sola pantalla.

Una hoja que todavía no tiene tabla se omite, no se importa

  • Qué: una pestaña que no tiene ninguno de los tres marcadores obligatorios y ninguna fila de datos — una hoja recién creada que solo contiene comentarios o una línea @style — se omite con una advertencia EmptyTabSkipped en lugar de fallar la importación por tres marcadores faltantes. Su código ya generado, su asset horneado (bake) y su dirección se preservan, no se limpian como si la pestaña se hubiera eliminado. Export y Push la omiten simétricamente, porque los tres usan el mismo predicado.
  • Por qué: una hoja sin terminar no debe poder detener la importación de todas las demás pestañas, y un autor normalmente crea la hoja antes que su fila de encabezado.
  • Límite: una hoja a medio escribir (con cualquier marcador obligatorio presente) no se omite — falla honestamente, porque omitirla en silencio ocultaría trabajo real. Una hoja tipada desde la columna A en lugar de la columna B se entrega igualmente al parser, así que su diagnóstico real ("la columna A es la columna de marcadores, los datos empiezan en B") se mantiene.

Un enum registrado desde C# de un plugin no puede ganar miembros desde una hoja

  • Qué: un Enum<T> cuyo T un plugin registró con enums.Register<T>() es propiedad del código. Una hoja de definición de enum no puede reclamar ese nombre (DuplicateEnumName), y la fila "Add a new member…" del menú desplegable de celda simplemente está ausente en esa columna.
  • Por qué: la hoja es canónica solo para lo que la hoja define. Escribir un miembro en una hoja que ya no decide el tipo compilado produciría un miembro que nunca aparece en el código — una promesa que el producto no puede cumplir. La fila ausente es la forma en que la UI lo indica, en lugar de ofrecer una acción que fallaría.
  • Solución alternativa: mueve el enum a una hoja de enum si la hoja debería ser su propietaria, o añade el miembro en el C# de tu plugin y recompila.
  • La estructura sigue la misma línea de propiedad: la estructura de una hoja de definición de enum se edita por completo en ambos hosts — definir, renombrar, eliminar, reordenar columnas, editar el tipo subyacente y la descripción — pero nada de eso puede tocar un nombre de enum propiedad del código, y una hoja de definición tampoco puede reclamar uno. El rechazo nombra el motivo.

Hojas de definición de enum: los miembros solo se añaden al final, y no hay ordenación

  • Qué: la estructura de la hoja de enum se edita en el mismo sitio tanto en el editor como en la app web, pero las filas de miembro solo se añaden al final — un hueco nunca se rellena hacia atrás — y la vista no ofrece orden ni filtro.
  • Por qué: la posición de un miembro es su valor entero. Rellenar un hueco o reordenar los miembros renumeraría en silencio valores ya horneados (bake) en assets y guardados en partidas. El orden de columna, en cambio, no tiene ningún significado, que es por lo que reordenar columnas siempre está permitido.
  • Solución alternativa: para fijar un valor explícitamente, usa la sintaxis Name=value; el orden de presentación en cualquier otro sitio es asunto del consumidor, no de la hoja.

Un enum definido en hoja siempre se genera en la carpeta de ajustes

  • Qué: los tipos de pestaña generados se regeneran en el mismo lugar donde ya viven, pero el archivo de enum (SheetForgeEnums.cs) no tiene ninguna pestaña a la que anclarse, así que siempre se escribe en la carpeta de código generado indicada en los ajustes. Si una pestaña cuyo código generado vive en la carpeta de su propio paquete usa un enum definido en hoja, ese ensamblado de paquete falla al compilar con CS0246.
  • Por qué: un solo archivo contiene todos los enums definidos en hoja, porque un enum es una salida a nivel de proyecto y no por pestaña — así que no hay una única pestaña cuyo hogar pudiera seguir.
  • Solución alternativa: pon ambas carpetas generadas en un mismo ensamblado, o registra ese enum desde código de plugin en su lugar. El fallo es un error de compilación visible con el tipo faltante nombrado, nunca una corrupción silenciosa.

Las referencias de asset tipadas se resuelven contra los tipos cargados — nombre corto solo si es único, sin tipos de ensamblado predefinido

  • Qué: AssetRef@Group<Type> acepta cualquier tipo de asset derivado de UnityEngine.Object que el proyecto pueda cargar, del motor o propio, sin lista blanca. Tres cosas se rechazan en lugar de adivinarse: un nombre corto que comparten varios tipos cargados (AmbiguousAssetTypeTextAsset puede serlo, según los paquetes instalados) debe escribirse como el nombre completo (UnityEngine.TextAsset); un nombre desconocido es UnknownAssetType con una sugerencia de coincidencia más cercana; y un tipo que vive en un ensamblado predefinido (Assembly-CSharp y sus hermanos — cualquier carpeta de scripts sin una definición de ensamblado) es AssetTypeNotReferenceable.
  • Por qué: el ensamblado complementario generado es una definición de ensamblado, y una definición de ensamblado no puede referenciar los ensamblados predefinidos — AssetReferenceT<T> para una T así no compilaría. Resolver un nombre ambiguo eligiendo uno enlazaría la columna al tipo equivocado en silencio.
  • Solución alternativa: mueve el tipo a una definición de ensamblado, o elimina la restricción <…> y conserva el AssetRef@Group sin restricción. Los componentes y los tipos de solo editor nunca son candidatos.
  • Además: el navegador no resuelve nombres de tipo (no tiene proyecto contra el cual resolver): la app web analiza <Type> y lo muestra en el tooltip de la columna, pero no produce ninguno de los tres diagnósticos de nombre de tipo ni ofrece selector o soltar. El codegen nunca emite un nombre no resuelto tal cual — un nombre que no puede resolver recae en AssetReference con una advertencia AssetTypeUnresolvedFallback.

El selector de asset, soltar y los registros preparados — qué es automático y qué no

  • Qué: soltar o elegir un asset escribe la dirección en la celda de inmediato y prepara el cambio de Addressables (añadir · mover · crear grupo) para el reflejo; el cambio se ejecuta solo después de que la escritura de la hoja tenga éxito — o, cuando los registros son lo único preparado, por sí solos, seguidos de la reimportación automática; un reflejo que no pudo escribir porque sus pestañas están respaldadas por libro los mantiene preparados. Un registro al que ya nada hace referencia se elimina cuando se vuelve a escribir la celda, y cualquiera que sobreviva hasta el reflejo se omite como no referenciado; un asset ya presente en el grupo conserva su dirección existente; un asset en otro grupo se mueve solo después de una confirmación que nombra las otras celdas que lo referencian. La dirección automática es el nombre del archivo sin extensión, y una dirección ya usada por un asset distinto en ese grupo se rechaza en lugar de renombrarse. Los grupos nuevos reciben los BundledAssetGroupSchema y ContentUpdateGroupSchema predeterminados. Los elementos aplicados y omitidos se registran en la consola con sus motivos, y para un origen de carpeta local, el diálogo de finalización del reflejo repite el resumen (Addressables: N registered, M skipped).
  • Por qué: la hoja es canónica — el proyecto nunca debe cambiar por un reflejo que no llegó a la hoja, y una entrada a la que ninguna celda apunta sería un huérfano que la hoja no explica.
  • Solución alternativa: si se omitió un registro, la siguiente reimportación reporta la celda como UnknownAssetKey; corrige la causa y vuelve a reflejar. El registro siempre requiere el paquete Addressables.

Los sub-assets se direccionan como parent[sub], y una textura en modo Sprite pasa <Sprite>

  • Qué: una entrada de sub-objeto (un sprite dentro de una textura, un material dentro de una fuente) se direcciona como la nombra Addressables — parent[sub] — y esa clave se comprueba contra el propio tipo del sub-objeto. La dirección del padre satisface su propio tipo y todos los tipos de sub-asset que contiene, que es lo que permite que una textura importada en modo Sprite pase una columna <Sprite>. Soltar un sub-asset prepara el registro de su padre y escribe parent[sub] en la celda.
  • Límite: una entrada de sub-objeto tiene que existir en el catálogo de Addressables para que parent[sub] valide; el selector lista las sub-claves que conoce después de su padre.

Los colores no tienen HDR, las tangentes de curva siguen su modo, los degradados se cuantizan — por diseño

  • Qué: Color son cuatro bytes — un canal por encima de 1 (HDR) se limita a 0…1 en Export. Una tangente de AnimationCurve cuyo lado sea Auto, Linear, Constant o ClampedAuto se recalcula a partir del modo en la importación, así que un número escrito a mano que contradiga su modo se reemplaza (el mismo recálculo que realiza Unity cuando se aplica el modo); Once se lee como ClampForever y nunca se vuelve a escribir; una curva sin claves no tiene forma de texto y existe solo como la celda vacía de una columna opcional. Los tiempos de clave de Gradient se cuantizan a 16 bits en la importación (exactamente como los almacena Unity), un degradado de una sola clave vuelve desde Unity como dos claves idénticas, y el espacio de color se escribe solo cuando se estableció.
  • Por qué: el valor que muestra la hoja debe ser el valor que tiene el motor, así que la normalización que Unity haría más tarde se realiza una sola vez, en la entrada, y cada superficie — hoja, campo del editor, vista previa web, asset generado mediante bake — muestra una sola curva y un solo degradado.
  • Solución alternativa: usa Free/Free cuando quieras que los números de tangente se tomen literalmente; almacena las intensidades HDR en una columna float separada.

Los editores de chips y los campos nativos existen solo para los tres tipos visuales

  • Qué: Data Studio muestra los escalares Color, AnimationCurve y Gradient como los propios campos de Unity y sus List<> como editores de chips; la app web muestra vistas previas con sus propios editores y listas de chips. Cualquier otra columna de lista — List<int>, List<Enum<…>>, listas de wrapper — permanece como texto canónico en ambos hosts, y un wrapper que contiene uno de los tres (Pair<Color>) también es texto.
  • Por qué: los tres tipos son los que tienen una imagen por elemento; para el resto, una sola línea canónica ya es la representación más exacta, y la notación externa de un wrapper es propiedad de su plugin.
  • Margen: un tipo de plugin que almacena uno de los tres valores puede adoptar los mismos editores declarando el arquetipo StudioCellEditorHint correspondiente (consulta Creación de plugins §4.16).

El coloreado de propiedad es solo hoja-versus-código

  • Qué: la barra lateral/leyenda distingue exactamente dos orígenes — pestañas de hoja reales y pestañas virtuales de registro de código. No hay una tercera clasificación "generada", ni coloreado de propiedad por columna.
  • Por qué: el origen se deriva de la propia pestaña, que la ventana ya conoce con certeza; una clasificación por columna necesitaría otro punto de extensión más para ser veraz, y ningún consumidor pidió uno.
  • No es lo mismo que el color de una hoja: @style permite que una hoja nombre su propio color, y ese color es metadato de visualización que el autor eligió — no dice nada sobre de dónde viene la hoja. Los dos coloreados se leen de lugares distintos y nunca se combinan.

Las listas desplegables de referencia responden pertenencia, no orden

  • Qué: una celda RecordId@Tab ahora tiene un menú desplegable con búsqueda (y una lista de verificación para List<>), pero el menú desplegable de la celda de lista solo añade y quita elementos — no puede mover uno. Reordenar una lista se hace en el lienzo, donde cada elemento tiene su propia fila.
  • Por qué: un menú desplegable responde "qué hay aquí"; "qué posición" necesita una superficie que alinee las cosas, que es lo que ya es el lienzo. Duplicarlo en dos lugares serían dos respuestas que mantener.
  • También: el menú desplegable se adjunta solo a una columna de referencia simple, sin wrapper. El texto de una celda con wrapper lleva la notación propia del wrapper, así que pegar una clave desnuda en ella destruiría el valor — llegar dentro de un wrapper es trabajo de un widget de celda registrado (consulta Creación de plugins).

La búsqueda All lee datos generados mediante bake en el editor y la sesión en vivo en el navegador

  • Qué: la entrada All de Data Studio busca en las bases de datos generadas mediante bake, así que necesita una importación exitosa primero y se actualiza sola cuando una importación se completa o cambian los ajustes activos. La entrada All de la app web busca en los valores que la sesión muestra en este momento, ediciones preparadas incluidas. La coincidencia, el orden de resultados y la página de 50 filas son el mismo código en ambas, y en ambas se hace doble clic en un resultado (o Enter sobre la fila seleccionada) para abrir esa hoja con la celda coincidente seleccionada.
  • Por qué: el navegador no tiene ScriptableObjects generados mediante bake; lo que tiene es la sesión en vivo — y responder con el valor en pantalla es justo para lo que sirve una sesión de navegador. El editor sigue leyendo la verdad horneada que ya tiene.
  • Límite: en el editor, una edición que está preparada pero todavía no reflejada no la encuentra All hasta que se ha ejecutado una importación, y un resultado de una hoja que la sesión no ha cargado no navega — un aviso debajo de la lista explica por qué. En el navegador, una edición preparada se encuentra al instante y todos los resultados se pueden abrir.

5. Rendimiento

  • La ruta normal es lineal y rápida: 50,000 filas × 20 columnas ≈ 628 ms (editor en vivo, Mono; 144 ms sin interfaz/headless); 50 pestañas × 2,000 filas con 180k celdas de referencia ≈ 294 ms. Los tamaños de proyecto típicos no son un problema.
  • La memoria es lineal pero con mucho boxing: ≈ 59 bytes/celda retenidos (≈ 138 en el pico durante la importación). En el techo de celdas de Google Sheet (~10M celdas) eso se extrapola a ~6.3 s de importación, ~590 MB retenidos, ~1.4 GB en el pico — ten cuidado con los contextos de gama baja/32 bits en tamaños extremos. (Un IR orientado a columnas es un elemento reconocido en el backlog.)
  • La tabla de creación se virtualiza por fila por encima de 200 filas, tanto en el editor como en el navegador, así que abrir una hoja grande ya no construye un widget por fila. Lo que no se virtualiza es la lógica por fila que respondería distinto si solo mirara lo que está en pantalla — el muestreo de ancho, la búsqueda, el orden, las coordenadas, la superposición de preparación. Los detalles y los dos casos límite están en §4.
  • La ruta de error también es lineal: incluso cuando las referencias se rompen en masa, el cálculo de sugerencias más cercanas permanece acotado — un presupuesto de sugerencias por campo junto con una distancia de edición prefiltrada por longitud y de terminación temprana lo mantienen aproximadamente lineal en el número de referencias rotas (≈ 45 ms con 4,000 referencias rotas, sin interfaz/headless; datos válidos en la misma escala ≈ 2.7 ms). Renombrar una pestaña referenciada no rompe las referencias en masa en primer lugar: el renombrado reescribe las celdas @type que las referencian.

6. Escena de demostración

  • Los ejemplos son importaciones selectivas — las dos demos de ejemplo y sus escenas no están presentes de forma predeterminada. Se distribuyen como paquetes de Unity (Assets/SheetForge/Examples/SheetForgePluginDemo.unitypackage y SheetForgeCoreDemo.unitypackage); importa uno (doble clic, o el botón Import Plugin/Core Demo de la ventana Primeros pasos) para restaurar Assets/SheetForge.PluginDemo/… o Assets/SheetForge.CoreDemo/…. Hasta entonces, los ejemplos no están en tu proyecto en absoluto — se distribuyen únicamente como esos paquetes — así que nunca pueden chocar con tu proyecto — el producto principal es completamente autosuficiente sin ellos.
  • Requiere una importación primero (por máquina) — las direcciones de Addressables que carga son una caché no confirmada. Antes de eso, muestra un mensaje de orientación.
  • No se necesita configurar el espacio de nombres para las demos — los tipos de ejemplo confirmados usan el espacio de nombres predeterminado SheetForge.Generated con un prefijo de clase Example*, así que reimportar una demo los regenera en el mismo lugar sin necesidad de ningún ajuste de generatedNamespace.

7. Extensión de plugins — costuras disponibles (con límites) y lo que sigue reservado

Dieciséis contratos de extensión están disponibles, todos ellos incorporándose con cero modificaciones al Core — el conjunto completo está en Creación de plugins. Los dieciséis los encuentra el descubrimiento (TypeCache de Unity; el escaneo del ensamblado subido del navegador), sin referencia de ensamblado y sin manifiesto que editar. A los once del Core se les entrega un registro donde registrar lo que añaden, mientras que los cinco del Editor — widget de grafo, acción de inspector, proveedor de editor de celda, proveedor de panel y proveedor de origen — simplemente se descubren y se usan tal cual.

Cinco de las costuras disponibles llevan límites que vale la pena exponer aquí en lugar de dejar que los descubras tú:

Referenciar tipos de celda personalizados — paridad completa con RecordId@Tab para tu propia notación

  • Qué: un parser de celda registrado que también implementa IReferencingCellType (y cuyo valor implementa IRefBearingValue) le dice al Core cómo leer y reescribir la clave enterrada en su propia notación. Esa columna entonces obtiene todo lo que obtiene una referencia integrada:

    • comprobación de integridad con sugerencias de coincidencia más cercana;
    • propagación de renombrado de clave que preserva la carga útil (attack:add:10power:add:10);
    • aristas y puertos de grafo, el selector , recuentos de referencias inversas;
    • detección de huérfanos y reglas de listas desplegables exportadas.

    El descubrimiento es un cast del parser ya registrado — no hay un nuevo canal de registro, y un tipo personalizado que no lo implementa queda sin cambios, bit a bit. Consulta Creación de plugins §4.4a.

  • Límites: una carga útil no puede contener ; — el Core divide una celda de lista en elementos antes de que tu parser vea el texto, así que un punto y coma dentro de un valor se haría pedazos en dos elementos (la misma restricción que llevan los tipos wrapper). Y @target debe nombrar una pestaña de hoja real: la pestaña virtual de un registro de código se rechaza con UnknownTargetTab, exactamente igual que para RecordId@Tab. Esa restricción es lo que permite que el propio reporte de referencias sin resolver del Core y la propagación de renombrado se apliquen sin modificar.

Tipos wrapper <> (MyWrapper<T>) — con reglas de rechazo

  • Qué: un plugin registra una forma de valor genérica (por ejemplo, Pair<int> = 1~2) mediante ICellWrapperType; el Core resuelve el tipo interno recursivamente (consulta Sintaxis de la hoja).

  • Reglas de rechazo:

    • Pair<List<T>> se rechaza — una lista no puede ir dentro de un wrapper, y List se mantiene plana y en el nivel más externo.
    • Pair<int>@Tab se rechaza — coloca el @ en la hoja interna: Pair<RecordId@Tab>.
    • Pair<int?> / Pair<int=1> se rechazan — la opcionalidad/los valores predeterminados son a nivel de campo, no parte del tipo interno.

    List<Pair<T>> está permitido, pero el delimitador propio del wrapper debe ser distinto de ; (el separador de lista) — una responsabilidad de quien crea el plugin que el Core no puede imponer.

Marcadores estructurales personalizados (@yourMarker) — solo para metadatos por columna

  • Qué: un plugin registra una fila @marker mediante IStructuralMarkerDefinition / ISheetForgeMarkerPlugin, generalizando la validación por columna de @overlap. El valor se almacena como metadatos FieldSchema.MarkerValues.
  • Límites: un marcador solo posee su validación de valor por columnano se hace cargo de analizar una forma de datos de fila completa (la normalización sigue siendo la forma de expresar formas de datos). Y el codegen no incorpora los valores de marcador en el bake: al igual que @overlap, son solo metadatos de validación/visualización, invisibles para la huella del esquema — así que nada relacionado con marcadores llega al código generado ni al SO generado mediante bake.

Preajustes de color — las superficies que pintamos nosotros, no los widgets de Unity

  • Qué: un plugin registra un preajuste de color mediante ISheetForgeThemePlugin / ThemeRegistry; aparece en Preferences ▸ SheetForge ▸ Theme junto a los preajustes integrados Default y High contrast, y se aplica solo si el usuario lo elige (registrarlo nunca secuestra la pantalla). Un preajuste sobrescribe solo los slots que nombra — todos los demás slots mantienen el valor predeterminado del producto, así que los preajustes se mantienen válidos a medida que se añaden slots.
  • Límite — se espera una apariencia mixta: el tema cubre lo que SheetForge pinta por sí mismo (fondos de ventana, encabezados, texto, acentos, colores de cuadrícula y de preparación). Los widgets nativos de Unity dibujados dentro de esas ventanas — el chrome de los botones, los bordes de campo, las flechas de los popups — siguen siguiendo el skin del editor, que Unity no deja restilizar a un paquete. Así que elegir Always light mientras el editor usa el skin oscuro da como resultado una superficie de SheetForge clara con widgets nativos oscuros encima. Ajusta el skin del editor para que coincida si quieres un aspecto uniforme.
  • Límite — solo color: los preajustes llevan colores (0xRRGGBB por slot). El espaciado, los tamaños de fuente y el layout no son configurables por tema, y los rellenos translúcidos (fondos de insignia, el scrim del modal) derivan de un color de slot más un alfa fijo, en lugar de ser configurables por separado.

Superficies de creación declarativas — un vocabulario acotado, a propósito

  • Qué: ISheetForgeStudioPlugin permite que un paquete describa verbos, paneles, insignias de columna y formas de editor de celda como datos, así que un solo registro lo dibujan tanto el editor como el navegador. El vocabulario es fijo y solo crece añadiendo: cinco ubicaciones de acción, trece tipos de nodo, siete arquetipos de editor de celda (los dos más nuevos, CurveEditor y GradientEditor, son los que usan los tipos integrados de curva y degradado).
  • Límite — no es un framework de UI. El renderizado arbitrario, la entrada compuesta y los flujos de varios pasos no tienen palabras aquí, y añadirlas significaría mantener un miniframework de UI para siempre. Para eso está IStudioPanelProvider: regístralo bajo el mismo id que un panel descrito y el editor dibuja el enriquecido mientras el navegador dibuja el descrito. No hay una vía de escape solo-web — el navegador no puede cargar un tipo de UIToolkit, y pretender lo contrario dejaría la extensión de un plugin en una sola pantalla.
  • Límite — los poderes de una acción son exactamente cuatro: preparar una celda, preparar varias celdas como un paso de Undo, enfocar un registro, pedir un redibujado. El verbo de un plugin es por lo tanto una edición preparada ordinaria que pasa por la misma barrera, pre-flight y push que una escrita a mano. La propia sesión de creación deliberadamente no se expone.
  • Sin disparo nunca, nunca disparo incorrecto, para los observadores: IPipelineObserver se dispara al final de un ciclo de importación explícito. Dos rutas nunca llegan a ese punto en absoluto — una ejecución que se detiene antes de que la canalización empiece (sin ajustes activos; Addressables no instalado) y un tramo codegen→compilación interrumpido por un error de compilación. Si necesitas "se intentó una importación", combínalo con el bus ImportEvents del lado del editor.

Todavía diseñado pero no construido (sin consumidor aún)

  • Marcadores de forma de datos de fila completa (por ejemplo, un marcador que lee una matriz 2D como un solo campo) — deliberadamente no construido: un marcador posee la validación por columna, no el análisis de fila, y la normalización (referencias + columna type + List<T>) es expresivamente completa. La costura de registro MarkerRegistry en sí está disponible; solo esta interpretación de análisis de forma está reservada.
  • Incorporar valores de marcador personalizado en el código generado mediante bake — fuera de alcance hasta que algún consumidor necesite los metadatos de marcador como constantes/atributos del codegen.
  • Contenedores de SO por registro / de carga diferida (lazy) — diseño completo, sin construir; el codegen actual produce únicamente SO de base de datos de carga completa por pestaña.

8. Evolución del esquema

  • La exportación/Push requieren un bake actualizado después de un cambio de esquema — un bake obsoleto falla con ExportSchemaMismatch (discrepancia de huella). El rechazo ahora ofrece ejecutar esa importación por ti: una confirmación la inicia, y después no se exporta ni se hace push de nada automáticamente — pulsa de nuevo la acción original en cuanto la importación termine.
  • La primera importación después de un cambio de esquema es internamente de dos etapas (codegen → compilación → bake) — automática, una sola acción del usuario; solo un fallo de compilación la detiene (aborto seguro, frase accionable, límite de 3 intentos).

9. Alcance de la localización

Los fragmentos de detalle del informe (valores causantes, sugerencias), las excepciones de bajo nivel y los registros para desarrolladores están en inglés en línea dentro del esqueleto de informe localizado — las interpolaciones en tiempo de ejecución no pueden ser claves de tabla de idioma (límite estándar de la industria). Todo lo dibujado por el editor, además del esqueleto del informe y las frases de por qué/cómo, está completamente localizado en los 10 idiomas.

Los diez idiomas están traducidos por completo. Cada clave de cada tabla de idioma lleva una traducción real — Data Studio, las preferencias de tema, los diálogos y las líneas de registro incluidos. La paridad de claves entre los diez archivos se impone mediante pruebas, así que nada recae en una clave sin traducir ni rompe un marcador de posición.

Un puñado de entradas por idioma sí se leen exactamente igual que en inglés, y eso es una decisión de traducción, no una pasada pendiente: son símbolos y strings que solo son marcadores de posición (, +), nombres propios y nombres de formato (Google Sheets, SHA-256), y palabras que un idioma realmente escribe igual que el inglés (OK, Alpha).

Las etiquetas propias de un plugin no están en absoluto en la tabla del Core: regístralas con ISheetForgeStringsPlugin para que sigan el idioma del usuario, o déjalas sin registrar y se muestran textualmente.

10. Interacciones del editor

  • Alcance de Ctrl+Z: un campo de texto enfocado consume Ctrl+Z primero (estándar del SO); después de un reflejo exitoso, el historial de preparación se borra — el deshacer nunca alcanza lo que ya se escribió en la hoja (la hoja es canónica).
  • El cambio de idioma está bloqueado durante la importación/exportación/Push (activa una recompilación de regeneración de menús). Los cambios de tema no están bloqueados — nunca recompilan, así que el brillo y el preajuste se pueden cambiar en cualquier momento, incluso durante una ejecución.
  • El tema y el idioma son por usuario (EditorPrefs), no por proyecto — cada compañero de equipo mantiene el suyo propio, y ninguno de los dos aparece en el control de versiones. Elegir un preajuste de color distinto del predeterminado escribe una hoja de estilos generada bajo Assets/SheetForge/Editor/Generated/ (en gitignore, autorreparable); el preajuste predeterminado no escribe nada y la elimina.
  • El código generado + los SO generados mediante bake + el grupo de Addressables son cachés en gitignore, específicas de cada máquina — cada máquina ejecuta una importación una vez; el código de juego carga por dirección, nunca mediante referencia directa desde una escena.

11. Licencia

El repositorio incluye un aviso LICENSE: el EULA de Unity Asset Store es el acuerdo que rige, con un aviso de visualización del repositorio (código fuente visible como referencia y para compradores con licencia; sin redistribución/reventa fuera del EULA sin permiso por escrito). Código de terceros: ninguno — incluyendo el lector/escritor de xlsx OOXML escrito a mano.

12. Estado verificado (en el lanzamiento)

  • Doble arnés de pruebas: 2.150 pruebas de .NET sin interfaz (headless) (2.150 aprobadas) + 3.021 pruebas de EditMode (3.021 aprobadas, 0 fallos, 4 omitidas). Estos dos números son el único lugar donde se indican los recuentos; todas las demás páginas enlazan aquí.
  • Las cuatro omisiones son el ciclo de ida y vuelta en vivo con Google, que solo se ejecuta cuando hay credenciales de cuenta de servicio presentes en el entorno, y se omitió en esta ejecución. Con credenciales, se ha ejercitado repetidamente contra una hoja de cálculo real — fetch → push, incluyendo la detección de conflicto de fila movida, el manejo de floats independiente de la configuración regional, un lote mixto entre pestañas, y una ejecución completa del dispatcher que verifica un informe fusionado y exactamente una reimportación automática por envío. Resultado: 4/4 en verde.
  • La app web tiene sus propias barreras, todas en verde: comprobación de tipos, lint, 414 pruebas unitarias, una publicación de WebAssembly + una ejecución de humo (que carga una DLL de plugin real), una build de producción, 56 pruebas de extremo a extremo en navegador, y una comprobación de constantes entre lenguajes (seis pares Unity↔web: versión de host, formato de plugin, versión de esquema de registro, alcance de OAuth, nombres de ensamblado de host, umbrales de ventana de filas).
  • Todos los asmdef compilados del producto: 0 errores, 0 advertencias. (Los asmdef de ejemplo — dos en cada demo — solo aparecen cuando importas un paquete de demo, y no se compilan hasta entonces.)
  • Pruebas de guardia en verde: cero literales en coreano en el código fuente del producto, cero vocabulario de dominio en las costuras del núcleo (ambas omiten /Samples~/ — el ejemplo es contenido de dominio), paridad de claves en los 10 idiomas.
  • El ensamblado de simulación de consumidor sin IVT compila únicamente contra la API pública (impuesto por el compilador). Una mini-sonda de plugin sin IVT allí implementa quince de los dieciséis contratos de extensión (ISheetForgePlugin / validador / arista / marcador / plantilla / grafo / registro de código / tema / UI de Studio / cadenas / canalización / ISheetSourceProvider / widget de Studio / acción de inspector de Studio / editor de celda de Studio) usando únicamente la superficie pública, así que la publicidad del contrato se mantiene demostrada aunque el ejemplo Plugin Demo no se compile. El decimosexto, IStudioPanelProvider, devuelve un VisualElement y se ejercita mediante una prueba del lado del editor en su lugar. La misma sonda también implementa las interfaces de capacidad opcionales, incluidas IReferencingCellType / IRefBearingValue, y las ejercita a través de la superficie pública (descubrimiento por cast, los cinco hooks, preservación del residuo).

Elementos que solo se pueden verificar en el momento de la publicación en el Asset Store (no en el repositorio): el comportamiento del aviso de instalación de .unitypackage, la declaración de dependencias de Portal, la exclusión de los ensamblados de prueba del paquete distribuido, y una nueva comprobación de 0 advertencias en un proyecto limpio.

13. Hojas de localización (texto de juego)

Los límites de Hojas de localización y el puente de Unity Localization, dichos con honestidad. (El paquete en sí es opcional — consulta §1.)

El puente es de un solo sentido, y las ediciones externas de tabla se preguntan — nunca se fusionan

  • Qué: la sincronización va solo hoja → StringTables. El puente estampa las tablas que posee y toma una huella de cada sincronización; una tabla editada por cualquier otra cosa desde entonces — la ventana Localization Tables, la propia extensión de Google Sheets de Unity, una importación XLIFF — hace que la siguiente sincronización se detenga y pregunte: sobrescribir desde la hoja, o abortar con un informe de diferencias.
  • Por qué: dos cabinas con capacidad de escritura sobre los mismos datos terminan en sobrescrituras silenciosas. La hoja es canónica, así que la otra cabina debe ser explícita, no silenciosa.
  • Solución alternativa: encamina las traducciones a través de la hoja — el libro de traducción (exportación a xlsx + reimportación parcial de solo configuración regional) existe precisamente para eso.

Una clave renombrada fuera del Studio es un borrado más una adición

  • Qué: renombrar una clave en el Data Studio renombra la entrada de tabla in situ, preservando el id interno al que se enlazan las referencias LocalizedString — las referencias de escena sobreviven. Renombrar la clave directamente en la fuente de la hoja (Google Sheets, Excel) es indistinguible de eliminar una clave y añadir otra: el puente crea una entrada nueva y la antigua se convierte en huérfana, con las referencias de escena todavía apuntando a la huérfana.
  • Por qué: el diffing a nivel de texto no puede distinguir un renombrado de un borrado-más-adición sin adivinar, y una suposición equivocada volvería a enlazar referencias en silencio.
  • Solución alternativa: renombra las claves en el Data Studio (cualquiera de los dos hosts); el informe de huérfanas detecta las secuelas de un renombrado externo.

Las claves huérfanas de tabla se preservan por defecto

  • Qué: una clave presente en la tabla pero que ya no está en la hoja se mantiene, se reporta como huérfana, y solo se elimina mediante la acción de limpieza explícita — o automáticamente, si activas el ajuste de eliminar al sincronizar. Nada se elimina como efecto secundario.
  • Por qué: una fila de hoja faltante puede ser un error a mitad de edición; destruir traducciones por eso sería irrecuperable.

Solo tablas de string — las tablas de assets no están cubiertas

  • Qué: el puente rellena colecciones StringTable. El eje AssetTable de Unity Localization (sprites, audio, prefabs localizados) no se sincroniza desde hojas. Un elemento reconocido del backlog.
  • Solución alternativa: gestiona las tablas de assets con las propias herramientas del paquete; el puente no las toca.

XLIFF y las pseudolocalizaciones no se reimplementan

  • Qué: las tablas sincronizadas son tablas de Unity Localization ordinarias, así que la exportación/importación XLIFF y la pseudolocalización propias del paquete funcionan sobre ellas sin cambios. SheetForge no añade una segunda implementación.
  • Límite: la salida que esas herramientas escriben en las tablas cuenta como una edición externa (primera entrada arriba). Mantén la hoja como fuente de verdad y lleva las traducciones a través del libro de traducción.

No se distribuyen ayudantes de Smart Format específicos de idioma

  • Qué: la columna smart marca una entrada como Smart String, pero SheetForge no proporciona formateadores gramaticales propios — la selección de partículas del coreano, por ejemplo, no está incluida, deliberadamente.
  • Solución alternativa: los puntos de extensión de Smart Format del paquete siguen totalmente disponibles para los formateadores que tú escribas.

Las constantes de clave se sanean a ASCII

  • Qué: las constantes {Tab}Keys generadas convierten cada carácter fuera de las letras ASCII, los dígitos y _ en _ (las colisiones reciben un sufijo numérico), así que una clave no ASCII produce un nombre de constante ilegible — la clave en sí sigue funcionando en todas partes.
  • Solución alternativa: mantén las claves en ASCII (ui.ok, dialog.intro) si usas las constantes. Igual que el archivo de enum definido en hoja, el archivo de constantes siempre se genera en la carpeta de ajustes — se aplica la misma nota de ensamblado.

La app web crea hojas de localización; la sincronización es del editor

  • Qué: la creación, la validación, la cobertura, la generación de claves, la lente de configuración regional y el libro de traducción funcionan todos en el navegador. Escribir StringTables no — el navegador no tiene un proyecto de Unity donde escribir.
  • Por qué: alcance honesto, no una funcionalidad ausente: las tablas viven en el proyecto.

Páginas relacionadas