FOS Essentials Core
Versión 1.4.0
Base común de todas las herramientas FOS Essentials para VRChat. Unity 2022.3.22f1 · VRChat SDK Worlds 3.10.4 · UdonSharp 1.x
Qué aporta el Core
Sección titulada «Qué aporta el Core»| Lado | Elemento | Función |
|---|---|---|
| Runtime | FOSBehaviour |
Clase base de todo comportamiento FOS: registros, propiedad, sincronización |
| Runtime | FOSPlayerRegistry |
Lista de jugadores refrescada al entrar y salir, sin asignación por fotograma |
| Runtime | FOSToolAttribute |
Permite que una herramienta se declare al Hub |
| Runtime | FOSSectionAttribute |
Sustituto traducible de [Header] |
| Runtime | FOSVersion |
Constantes de identidad y versión (uso solo en el editor) |
| Editor | FOSBrandingSettings |
Logo, color, URL de Discord / tienda / web / documentación — un solo asset |
| Editor | FOSDefaultInspector |
Banner FOS automático en todo lo que derive de FOSBehaviour |
| Editor | FOSInspectorBase |
Base de un inspector a medida que conserva el banner |
| Editor | FOSLocalizedInspector |
Etiquetas, descripciones emergentes y títulos de sección traducidos |
| Editor | FOSLoc |
Búsqueda de traducciones |
| Editor | FOSHubWindow |
Tools > FOS Essentials > Hub: entorno, herramientas instaladas, enlaces |
| Editor | FOSProgramAssets |
Genera los UdonSharpProgramAsset que falten (ver abajo) |
| Editor | FOSCoreBootstrap |
Registra cada ensamblado runtime de FOS en UdonSharp |
Puntos de entrada del editor
Sección titulada «Puntos de entrada del editor»| Llamada | Efecto |
|---|---|
FOS Essentials > Hub |
Ventana de estado: entorno, prefabs, herramientas, enlaces |
FOS Essentials > <Pack> > <Prefab> |
Coloca ese prefab en la escena abierta |
FOS Essentials > Generate Missing Program Assets |
Crea los archivos .asset que falten |
FOS Essentials > Rebuild Prefab Menu |
Regenera las entradas de menú de cada pack |
FOS Essentials > Check event wiring |
Lista los controles de interfaz cuyo evento no corresponde a ningún método |
FOSPrefabCatalog.Scan() |
Prefabs expuestos por cada pack, agrupados por pack |
FOSPrefabCatalog.InstantiateInScene(path) |
Coloca un prefab en la escena abierta |
FOSLoc.T("clave") |
Texto traducido; recurre al inglés y luego a la clave |
FOSLoc.Format("clave", args) |
Igual, a través de string.Format |
FOSLocalizedInspector.Draw(so, type) |
Inspector traducido, sustituye a DrawDefaultInspector() |
FOSProgramAssets.GenerateMissing() |
Como la entrada de menú, desde código. Devuelve cuántos se crearon |
FOSProgramAssets.FindMissing() |
Tipos que aún no tienen program asset |
FOSCoreBootstrap.EnsureUdonSharpAssemblyRegistered() |
Registra cada asmdef runtime de FOS en UdonSharp |
FOSBranding.Settings |
Acceso al branding — nunca devuelve null |
FOSHeader.Draw(name, version) |
Dibuja el banner dentro de una ventana a medida |
Primer arranque
Sección titulada «Primer arranque»- Abre Unity y deja que termine de compilar.
- El Core crea por su cuenta lo siguiente:
Assets/FOS/<Herramienta>/Runtime/FOS.<Herramienta>.Runtime.asset— registro de UdonSharp, para cada pack de FOSAssets/FOS/Core/Editor/Branding/FOSBrandingSettings.asset— el branding- un
UdonSharpProgramAssetjunto a cada comportamiento FOS concreto
- Abre
FOS Essentials > Huby comprueba que todos los indicadores estén en verde. - Desde el Hub, pulsa Open branding settings y rellena el logo, la URL de Discord y la URL de la tienda. Mientras una URL esté vacía, su botón permanece desactivado en lugar de abrir un enlace muerto.
El Hub también lista todos los prefabs que traen los packs instalados, con un botón cada uno, y los coloca en la escena abierta, delante de la vista de escena.
El menú de prefabs
Sección titulada «El menú de prefabs»Cada prefab encontrado bajo Assets/FOS/<Pack>/Prefabs/ recibe además su propia entrada en
FOS Essentials > <Pack> >, de modo que se puede añadir un prefab sin abrir el Hub.
[MenuItem] es un atributo y por tanto está congelado en tiempo de compilación: esas
entradas no pueden construirse a partir de un escaneo de carpetas en ejecución.
FOSPrefabMenuGenerator las escribe en
Assets/FOS/Core/Editor/Generated/FOSPrefabMenu.generated.cs, usando únicamente API
pública — Unity sí expone un Menu.AddMenuItem dinámico, pero es interno y no está
documentado, y eso no es algo que se entregue en un asset vendido.
El archivo se regenera al cargar el editor, cuando se añade, mueve o borra un prefab bajo
una carpeta Prefabs/, y desde Rebuild Prefab Menu. Solo se reescribe si su contenido
difiere de verdad: de lo contrario, escribirlo dispararía una compilación, que dispararía
la generación, que volvería a escribirlo.
No edites el archivo generado: se sobrescribe. Añade o renombra prefabs en su lugar.
Packs entregados en varias ediciones
Sección titulada «Packs entregados en varias ediciones»Un pack vendido como Standard, Pro o en versión a medida pone cada edición en su propia carpeta:
Assets/FOS/<Pack>/<Edición>/Prefabs/Ambas disposiciones se reconocen, y un pack con una sola edición no cambia nada. La edición
se convierte en un submenú de verdad — FOS Essentials > FOS Tablet System > Standard > …
— y en su propio bloque dentro del Hub.
Un prefab colocado en cualquier otro sitio sigue siendo invisible para ambos. Esa es toda
la regla de descubrimiento: Prefabs/ está o bien directamente bajo el pack, o bien una
carpeta más abajo.
Escribir un comportamiento FOS
Sección titulada «Escribir un comportamiento FOS»using FOS.Core;using UdonSharp;using UnityEngine;
namespace FOS.MyTool{ [UdonBehaviourSyncMode(BehaviourSyncMode.Manual)] public class MySwitch : FOSBehaviour { [SerializeField] private GameObject target;
[UdonSynced] private bool _isOn;
public override void Interact() { FOSTakeOwnership(); // la propiedad es obligatoria antes de escribir una variable sincronizada _isOn = !_isOn; Apply(); FOSSync(); // RequestSerialization(), pero solo si somos el propietario }
public override void OnDeserialization() { Apply(); // repite el estado en los remotos Y en quienes llegan tarde }
private void Apply() { target.SetActive(_isOn); FOSLog("Switch: " + _isOn); } }}El banner con el logo y los botones de Discord / tienda aparece automáticamente: no hay que escribir ninguna clase de editor.
Inspector a medida
Sección titulada «Inspector a medida»using FOS.Core.EditorTools;using UnityEditor;
[CustomEditor(typeof(MySwitch))]public class MySwitchEditor : FOSInspectorBase{ protected override void OnFOSInspectorGUI() { base.OnFOSInspectorGUI(); // campos traducidos EditorGUILayout.HelpBox("What this tool does.", MessageType.Info); }}UdonSharp acepta solo un
[CustomEditor]por tipo. Declarar dos registra un error y uno de ellos se ignora.No llames aquí a
DrawDefaultInspector(): pierde la traducción.base.OnFOSInspectorGUI()dibuja los campos medianteFOSLocalizedInspector.
Crear una herramienta nueva que dependa del Core
Sección titulada «Crear una herramienta nueva que dependa del Core»Assets/FOS/<Herramienta>/├── Runtime/│ ├── FOS.<Herramienta>.Runtime.asmdef → referencias: FOS.Core.Runtime, UdonSharp.Runtime,│ │ VRC.Udon, VRC.SDK3, VRC.SDKBase│ ├── AssemblyInfo.cs → [assembly: FOSTool("FOS <Herramienta>", "1.0.0", ...)]│ └── ...├── Editor/ (opcional)├── Prefabs/└── README.mdEl asmdef debe llamarse FOS.<Herramienta>.Runtime: así es como el Core lo encuentra y
lo registra automáticamente en UdonSharp.
Una herramienta entregada en varias ediciones repite esa disposición una carpeta más abajo,
y cada edición obtiene su ensamblado y su propia declaración [FOSTool]:
Assets/FOS/<Herramienta>/├── Standard/│ ├── Runtime/ → FOS.<Herramienta>.Standard.Runtime.asmdef│ ├── Editor/ → FOS.<Herramienta>.Standard.Editor.asmdef│ └── Prefabs/└── Pro/ ├── Runtime/ → FOS.<Herramienta>.Pro.Runtime.asmdef, referencia FOS.<Herramienta>.Standard.Runtime ├── Editor/ └── Prefabs/El nombre sigue teniendo que terminar en .Runtime, que es lo único que mira el escaneo
del Core; la profundidad da igual. Cada edición aparece entonces en el Hub como una
herramienta propia, con su propio número de versión.
AssemblyInfo.cs:
using FOS.Core;
[assembly: FOSTool( "FOS Doors", "1.0.0", Description = "doors.tool.description", // una clave de traducción, resuelta por el Hub MinimumCoreVersion = "1.0.0")]La herramienta aparece entonces en el Hub con su versión, y el Hub avisa cuando el Core
instalado es demasiado antiguo para ella. Description acepta tanto una clave de
traducción como texto plano; es preferible la clave, para que el Hub pueda mostrarla en el
idioma del usuario.
La trampa asmdef × UdonSharp
Sección titulada «La trampa asmdef × UdonSharp»UdonSharp solo compila los scripts de Assembly-CSharp y los de ensamblados declarados por
un asset UdonSharpAssemblyDefinition (CompilationContext.IsUdonSharpAssembly).
Un UdonSharpBehaviour situado en un asmdef no declarado se ignora en silencio: sin
error, el componente se coloca en la escena, el inspector parece normal y el comportamiento
no hace absolutamente nada en el juego.
FOSCoreBootstrap se encarga de esto para todos los packs: recorre Assets/FOS
buscando asmdefs llamados FOS.*.Runtime y crea el asset de registro que falte. Una
herramienta nueva no requiere por tanto ningún paso manual.
El Hub muestra el estado de ese registro, con un botón Repair now.
La trampa del program asset
Sección titulada «La trampa del program asset»UdonSharp no genera los archivos UdonSharpProgramAsset. El compilador solo trata los
scripts que ya tienen uno (UdonSharpCompilerV1, module.programAsset), y por eso cada
script que viene con el SDK llega como pareja .cs + .asset. Sin su asset, un
comportamiento no se puede añadir siquiera a un GameObject.
El Core se ocupa: FOSProgramAssets recorre los tipos que derivan de FOSBehaviour y crea
el .asset que falte junto al .cs. Se ejecuta al cargar el editor y a petición desde el
Hub o el menú.
Consecuencias prácticas:
- Escribir un comportamiento FOS nuevo no requiere ningún paso manual. Deja que Unity compile y el asset aparece.
- Las clases abstractas y genéricas se omiten deliberadamente: UdonSharp se niega a
asociarles un program asset (
Abstract U# behaviours cannot have an associated U# program asset). Por esoFOSBehavioures abstracta. - Una clase debe llamarse exactamente como su archivo
.cs; si no, la correspondencia falla y U# lanzaUdonSharpBehaviour classes must have the same name as their containing .cs file. - Un archivo
.assetexistente nunca se sobrescribe.
Localización
Sección titulada «Localización»La interfaz del editor está traducida al inglés, francés, español y alemán. El idioma se
elige en el Hub o en los ajustes de branding; se guarda en EditorPrefs, así que es por
máquina, y su valor por defecto es Auto (sigue el idioma de Unity y recurre al inglés).
Traducir una cadena nueva
Sección titulada «Traducir una cadena nueva»- Añade la clave a las cuatro tablas de
FOSCoreStrings.cs. - Llámala con
FOSLoc.T("mi.clave"), oFOSLoc.Format("mi.clave", arg)cuando el texto contenga un{0}.
Una clave ausente recurre al inglés y luego a la clave en bruto. Ver la clave en bruto en la interfaz es intencionado: una traducción que falta se detecta al instante en vez de pasar inadvertida.
Traducir los campos de un componente
Sección titulada «Traducir los campos de un componente»Las etiquetas y las descripciones emergentes no vienen de [Tooltip] sino de las claves
field.TipoQueDeclara.nombreDelCampo y tip.TipoQueDeclara.nombreDelCampo.
Lo que cuenta es el tipo que declara el campo, no el tipo inspeccionado: un campo de
FOSBehaviour tiene por tanto una sola traducción, válida para todas las herramientas que
deriven de él.
Los atributos [Tooltip] se escriben en inglés en el código: sirven de reserva y
documentan el campo para quien lea las fuentes.
Para un título de sección, no uses [Header]: su texto queda congelado en compilación. Usa:
[FOSSection("section.core.diagnostic", "Diagnostics")][Tooltip("English fallback tooltip.")][SerializeField] private bool myField;La regla, sin excepciones
Sección titulada «La regla, sin excepciones»1. Toda cadena visible para el usuario debe poder traducirse, así que debe pasar por
FOSLoc. 2. Lo que no puede traducirse se escribe en inglés.
Lo que no puede traducirse, y por tanto va en inglés
Sección titulada «Lo que no puede traducirse, y por tanto va en inglés»| Elemento | Motivo |
|---|---|
| Nombres de variables, campos, métodos y clases | Identificadores de C# |
| Namespaces, nombres de asmdef y de ensamblado | Identificadores |
Rutas de [MenuItem] y [AddComponentMenu] |
Unity las congela en compilación |
Texto de reserva de los [Tooltip] |
Se usa cuando falta la clave |
Segundo argumento de [FOSSection("clave", "Fallback")] |
Ídem |
| Registros de Udon en ejecución | Udon no tiene Dictionary ni genéricos |
| Mensajes dirigidos al autor de una herramienta | Se dirigen a quien programa |
| Nombres de archivo, de carpeta, claves de traducción | Identificadores |
El texto mostrado en el juego por una futura herramienta es otro mecanismo (arrays indexados del lado de Udon) y de momento no lo cubre el Core.
Añadir un idioma
Sección titulada «Añadir un idioma»Añade un valor al final de la enumeración FOSLanguage — nunca en medio, los valores se
escriben tal cual en los EditorPrefs del usuario — y luego una tabla en FOSCoreStrings y
un caso en FOSLoc.LanguageLabel.
Recordatorios de UdonSharp válidos para todo el pack
Sección titulada «Recordatorios de UdonSharp válidos para todo el pack»- No soportado: clases y structs propios distintos de
UdonSharpBehaviour, interfaces, genéricos,delegate/event/lambdas, LINQ,try/catch,async, corrutinas, arrays[,],params, valores por defecto en parámetros,ref/out. - Retardos:
SendCustomEventDelayedSeconds("Method", t), nunca una corrutina. - Red: la propiedad es obligatoria antes de escribir una variable
[UdonSynced];SendCustomNetworkEventno se repite para quienes llegan tarde — el estado debe poder reconstruirse a partir de las variables sincronizadas y deOnDeserialization. - Presupuestos: Continuous ≈ 200 bytes por comportamiento; Manual es más amplio pero está
limitado en frecuencia (≈ 1 serialización útil por segundo). Nunca satures
RequestSerialization(). - Quest: vigila el presupuesto de draw calls, usa shaders móviles y evita el postprocesado pesado.
