FOS Essentials Core
Version 1.4.0
Socle commun de tous les outils FOS Essentials pour VRChat. Unity 2022.3.22f1 · VRChat SDK Worlds 3.10.4 · UdonSharp 1.x
Ce que fournit le Core
Section intitulée « Ce que fournit le Core »| Côté | Élément | Rôle |
|---|---|---|
| Runtime | FOSBehaviour |
Classe de base de tout comportement FOS : logs, ownership, synchro |
| Runtime | FOSPlayerRegistry |
Liste de joueurs rafraîchie à l’arrivée et au départ, sans allocation par frame |
| Runtime | FOSToolAttribute |
Permet à un outil de se déclarer au Hub |
| Runtime | FOSSectionAttribute |
Remplaçant traduisible de [Header] |
| Runtime | FOSVersion |
Constantes d’identité et de version (usage éditeur seulement) |
| Éditeur | FOSBrandingSettings |
Logo, couleur, URL Discord / boutique / site / doc — un seul asset |
| Éditeur | FOSDefaultInspector |
Bandeau FOS automatique sur tout ce qui dérive de FOSBehaviour |
| Éditeur | FOSInspectorBase |
Base d’un inspecteur sur mesure qui conserve le bandeau |
| Éditeur | FOSLocalizedInspector |
Libellés, infobulles et titres de section traduits |
| Éditeur | FOSLoc |
Recherche de traduction |
| Éditeur | FOSHubWindow |
Tools > FOS Essentials > Hub : environnement, outils installés, liens |
| Éditeur | FOSProgramAssets |
Génère les UdonSharpProgramAsset manquants (voir plus bas) |
| Éditeur | FOSCoreBootstrap |
Déclare chaque assembly runtime FOS auprès d’UdonSharp |
Points d’entrée éditeur
Section intitulée « Points d’entrée éditeur »| Appel | Effet |
|---|---|
FOS Essentials > Hub |
Fenêtre d’état : environnement, prefabs, outils, liens |
FOS Essentials > <Pack> > <Prefab> |
Pose ce prefab dans la scène ouverte |
FOS Essentials > Generate Missing Program Assets |
Crée les fichiers .asset manquants |
FOS Essentials > Rebuild Prefab Menu |
Régénère les entrées de menu de chaque pack |
FOS Essentials > Check event wiring |
Liste les contrôles d’interface dont l’événement ne correspond à aucune méthode |
FOSPrefabCatalog.Scan() |
Prefabs exposés par chaque pack, groupés par pack |
FOSPrefabCatalog.InstantiateInScene(path) |
Pose un prefab dans la scène ouverte |
FOSLoc.T("cle") |
Texte traduit ; retombe sur l’anglais, puis sur la clé |
FOSLoc.Format("cle", args) |
Idem, via string.Format |
FOSLocalizedInspector.Draw(so, type) |
Inspecteur traduit, remplace DrawDefaultInspector() |
FOSProgramAssets.GenerateMissing() |
Comme l’entrée de menu, depuis du code. Renvoie le nombre créé |
FOSProgramAssets.FindMissing() |
Types qui n’ont pas encore de program asset |
FOSCoreBootstrap.EnsureUdonSharpAssemblyRegistered() |
Déclare chaque asmdef runtime FOS auprès d’UdonSharp |
FOSBranding.Settings |
Accès au branding — ne renvoie jamais null |
FOSHeader.Draw(name, version) |
Dessine le bandeau dans une fenêtre sur mesure |
Premier lancement
Section intitulée « Premier lancement »- Ouvrez Unity et laissez-le finir de compiler.
- Le Core crée tout seul ce qui suit :
Assets/FOS/<Outil>/Runtime/FOS.<Outil>.Runtime.asset— déclaration UdonSharp, pour chaque pack FOSAssets/FOS/Core/Editor/Branding/FOSBrandingSettings.asset— le branding- un
UdonSharpProgramAssetà côté de chaque comportement FOS concret
- Ouvrez
FOS Essentials > Hubet vérifiez que tous les voyants sont au vert. - Depuis le Hub, cliquez sur Open branding settings et renseignez le logo, l’URL Discord et l’URL de la boutique. Tant qu’une URL est vide, son bouton reste désactivé plutôt que d’ouvrir un lien mort.
Le Hub liste aussi tous les prefabs livrés par les packs installés, un bouton chacun, et les pose dans la scène ouverte, devant la vue de scène.
Le menu des prefabs
Section intitulée « Le menu des prefabs »Chaque prefab trouvé sous Assets/FOS/<Pack>/Prefabs/ reçoit également son entrée sous
FOS Essentials > <Pack> >, ce qui permet d’ajouter un prefab sans ouvrir le Hub.
[MenuItem] est un attribut, donc figé à la compilation : ces entrées ne peuvent pas être
construites depuis un balayage de dossier à l’exécution. FOSPrefabMenuGenerator les écrit
donc dans Assets/FOS/Core/Editor/Generated/FOSPrefabMenu.generated.cs, en n’utilisant que
de l’API publique — Unity expose bien un Menu.AddMenuItem dynamique, mais il est interne
et non documenté, ce qui n’est pas quelque chose à livrer dans un asset vendu.
Le fichier est régénéré au chargement de l’éditeur, quand un prefab est ajouté, déplacé ou
supprimé sous un dossier Prefabs/, et depuis Rebuild Prefab Menu. Il n’est réécrit
que si son contenu diffère réellement — sinon l’écrire déclencherait une compilation, qui
déclencherait la génération, qui le réécrirait encore.
N’éditez pas le fichier généré : il est écrasé. Ajoutez ou renommez des prefabs à la place.
Les packs livrés en plusieurs éditions
Section intitulée « Les packs livrés en plusieurs éditions »Un pack vendu en Standard, Pro ou en version sur mesure place chaque édition dans son propre dossier :
Assets/FOS/<Pack>/<Édition>/Prefabs/Les deux dispositions sont reconnues, et un pack qui n’a qu’une édition ne change rien.
L’édition devient un vrai sous-menu — FOS Essentials > FOS Tablet System > Standard > … —
et son propre bloc dans le Hub.
Un prefab rangé ailleurs reste invisible pour les deux. C’est toute la règle de découverte :
Prefabs/ se trouve soit directement sous le pack, soit un dossier plus bas.
Écrire un comportement FOS
Section intitulée « Écrire un comportement 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(); // l'ownership est obligatoire avant d'écrire une variable synchronisée _isOn = !_isOn; Apply(); FOSSync(); // RequestSerialization(), mais seulement si nous sommes propriétaire }
public override void OnDeserialization() { Apply(); // rejoue l'état sur les distants ET sur les arrivants tardifs }
private void Apply() { target.SetActive(_isOn); FOSLog("Switch: " + _isOn); } }}Le bandeau avec le logo et les boutons Discord / Boutique apparaît automatiquement : aucune classe éditeur à écrire.
Inspecteur sur mesure
Section intitulée « Inspecteur sur mesure »using FOS.Core.EditorTools;using UnityEditor;
[CustomEditor(typeof(MySwitch))]public class MySwitchEditor : FOSInspectorBase{ protected override void OnFOSInspectorGUI() { base.OnFOSInspectorGUI(); // champs traduits EditorGUILayout.HelpBox("What this tool does.", MessageType.Info); }}UdonSharp n’accepte qu’un seul
[CustomEditor]par type. En déclarer deux journalise une erreur et l’un des deux est ignoré.N’appelez pas
DrawDefaultInspector()ici : il perd la traduction.base.OnFOSInspectorGUI()dessine les champs viaFOSLocalizedInspector.
Créer un nouvel outil qui dépend du Core
Section intitulée « Créer un nouvel outil qui dépend du Core »Assets/FOS/<Outil>/├── Runtime/│ ├── FOS.<Outil>.Runtime.asmdef → références : FOS.Core.Runtime, UdonSharp.Runtime,│ │ VRC.Udon, VRC.SDK3, VRC.SDKBase│ ├── AssemblyInfo.cs → [assembly: FOSTool("FOS <Outil>", "1.0.0", ...)]│ └── ...├── Editor/ (facultatif)├── Prefabs/└── README.mdL’asmdef doit s’appeler FOS.<Outil>.Runtime : c’est ainsi que le Core le trouve et le
déclare automatiquement auprès d’UdonSharp.
Un outil livré en plusieurs éditions répète cette disposition un dossier plus bas, et
chaque édition obtient son assembly et sa propre déclaration [FOSTool] :
Assets/FOS/<Outil>/├── Standard/│ ├── Runtime/ → FOS.<Outil>.Standard.Runtime.asmdef│ ├── Editor/ → FOS.<Outil>.Standard.Editor.asmdef│ └── Prefabs/└── Pro/ ├── Runtime/ → FOS.<Outil>.Pro.Runtime.asmdef, référence FOS.<Outil>.Standard.Runtime ├── Editor/ └── Prefabs/Le nom doit toujours se terminer par .Runtime, ce qui est tout ce que le balayage du
Core regarde — la profondeur n’a pas d’importance. Chaque édition apparaît alors dans le
Hub comme un outil à part entière, avec son propre numéro de version.
AssemblyInfo.cs :
using FOS.Core;
[assembly: FOSTool( "FOS Doors", "1.0.0", Description = "doors.tool.description", // une clé de traduction, résolue par le Hub MinimumCoreVersion = "1.0.0")]L’outil apparaît alors dans le Hub avec sa version, et le Hub avertit quand le Core
installé est trop ancien pour lui. Description accepte aussi bien une clé de traduction
qu’un texte brut ; la clé est préférable, pour que le Hub puisse l’afficher dans la langue
de l’utilisateur.
Le piège asmdef × UdonSharp
Section intitulée « Le piège asmdef × UdonSharp »UdonSharp ne compile que les scripts d’Assembly-CSharp et ceux des assemblies déclarées
par un asset UdonSharpAssemblyDefinition (CompilationContext.IsUdonSharpAssembly).
Un UdonSharpBehaviour placé dans un asmdef non déclaré est ignoré en silence : aucune
erreur, le composant se pose dans la scène, l’inspecteur paraît normal, et le comportement
ne fait strictement rien en jeu.
FOSCoreBootstrap s’en charge pour tous les packs : il balaie Assets/FOS à la
recherche d’asmdefs nommés FOS.*.Runtime et crée les assets de déclaration manquants. Un
nouvel outil ne demande donc aucune étape manuelle.
Le Hub affiche l’état de cette déclaration, avec un bouton Repair now.
Le piège du program asset
Section intitulée « Le piège du program asset »UdonSharp ne génère pas les fichiers UdonSharpProgramAsset. Le compilateur ne traite
que les scripts qui en ont déjà un (UdonSharpCompilerV1, module.programAsset) — d’où
les paires .cs + .asset livrées avec le SDK. Sans son asset, un comportement ne peut
tout simplement pas être ajouté à un GameObject.
Le Core s’en occupe : FOSProgramAssets balaie les types dérivant de FOSBehaviour et
crée le .asset manquant à côté du .cs. Il s’exécute au chargement de l’éditeur, et à la
demande depuis le Hub ou le menu.
Conséquences pratiques :
- Écrire un nouveau comportement FOS ne demande aucune étape manuelle. Laissez Unity compiler, l’asset apparaît.
- Les classes abstraites et génériques sont volontairement ignorées : UdonSharp
refuse de leur associer un program asset (
Abstract U# behaviours cannot have an associated U# program asset). C’est pourquoiFOSBehaviourest abstraite. - Une classe doit porter exactement le nom de son fichier
.cs, sans quoi la correspondance échoue et U# lèveUdonSharpBehaviour classes must have the same name as their containing .cs file. - Un fichier
.assetexistant n’est jamais écrasé.
Localisation
Section intitulée « Localisation »L’interface de l’éditeur est traduite en anglais, français, espagnol et allemand. La
langue se choisit dans le Hub ou dans les réglages de branding ; elle est stockée dans les
EditorPrefs, donc par machine, et vaut Auto par défaut (suit la langue d’Unity, avec
repli sur l’anglais).
Traduire une nouvelle chaîne
Section intitulée « Traduire une nouvelle chaîne »- Ajoutez la clé aux quatre tables de
FOSCoreStrings.cs. - Appelez-la avec
FOSLoc.T("ma.cle"), ouFOSLoc.Format("ma.cle", arg)quand le texte contient un{0}.
Une clé absente retombe sur l’anglais, puis sur la clé brute. Voir la clé brute dans l’interface est voulu : une traduction manquante se remarque immédiatement au lieu de passer inaperçue.
Traduire les champs d’un composant
Section intitulée « Traduire les champs d’un composant »Les libellés et les infobulles ne viennent pas de [Tooltip] mais des clés
field.TypeQuiDeclare.nomDuChamp et tip.TypeQuiDeclare.nomDuChamp.
C’est le type qui déclare le champ qui compte, pas le type inspecté : un champ porté
par FOSBehaviour n’a donc qu’une seule traduction, valable pour tous les outils qui en
dérivent.
Les attributs [Tooltip] restent écrits en anglais dans le code : ils servent de repli
et documentent le champ pour qui lit les sources.
Pour un titre de section, n’utilisez pas [Header] — son texte est figé à la compilation.
Utilisez :
[FOSSection("section.core.diagnostic", "Diagnostics")][Tooltip("English fallback tooltip.")][SerializeField] private bool myField;La règle, sans exception
Section intitulée « La règle, sans exception »1. Toute chaîne visible par l’utilisateur doit être traduisible, donc passer par
FOSLoc. 2. Ce qui ne peut pas être traduit s’écrit en anglais.
Ce qui ne peut pas être traduit, et s’écrit donc en anglais
Section intitulée « Ce qui ne peut pas être traduit, et s’écrit donc en anglais »| Élément | Raison |
|---|---|
| Noms de variables, champs, méthodes et classes | Identifiants C# |
| Namespaces, noms d’asmdef et d’assembly | Identifiants |
Chemins [MenuItem] et [AddComponentMenu] |
Unity les fige à la compilation |
Texte de repli des [Tooltip] |
Utilisé quand la clé manque |
Second argument de [FOSSection("cle", "Fallback")] |
Idem |
| Logs runtime Udon | Udon n’a ni Dictionary ni généricité |
| Messages destinés à l’auteur d’un outil | Ils s’adressent au développeur |
| Noms de fichiers, de dossiers, clés de traduction | Identifiants |
Le texte affiché en jeu par un futur outil relève d’un autre mécanisme (tableaux indexés côté Udon) et n’est pas couvert par le Core à ce stade.
Ajouter une langue
Section intitulée « Ajouter une langue »Ajoutez une valeur à la fin de l’énumération FOSLanguage — jamais au milieu, les
valeurs sont écrites telles quelles dans les EditorPrefs de l’utilisateur — puis une
table dans FOSCoreStrings et un cas dans FOSLoc.LanguageLabel.
Rappels UdonSharp valables pour tout le pack
Section intitulée « Rappels UdonSharp valables pour tout le pack »- Non supporté : classes et structs utilisateur autres qu’
UdonSharpBehaviour, interfaces, génériques,delegate/event/lambdas, LINQ,try/catch,async, coroutines, tableaux[,],params, valeurs de paramètres par défaut,ref/out. - Délais :
SendCustomEventDelayedSeconds("Method", t), jamais une coroutine. - Réseau : l’ownership est obligatoire avant d’écrire une variable
[UdonSynced];SendCustomNetworkEventn’est pas rejoué pour les arrivants tardifs — l’état doit être reconstructible depuis les variables synchronisées etOnDeserialization. - Budgets : Continuous ≈ 200 octets par comportement ; Manual est plus large mais
rate-limité (≈ 1 sérialisation utile par seconde). Ne spammez jamais
RequestSerialization(). - Quest : surveillez le budget de draw calls, utilisez des shaders mobiles, évitez le post-traitement lourd.
