feat: VR手柄提示实时翻译 - 新增VRControllerTooltipLocalizer组件,语言切换时自动更新VRTK手柄tooltip文字

This commit is contained in:
ayuan9957 2026-07-17 10:27:15 +08:00
parent 7c0c52ac9f
commit 33a1681f3d
4 changed files with 164 additions and 0 deletions

View File

@ -487,6 +487,17 @@ public class LocalizationManager : MonoBehaviour
{ "{0}与{1}边界间距为{2}M,小于200M", new[] { "{0}与{1}边界间距为{2}M,小于200M", "Distance between {0} and {1} boundary is {2}M, less than 200M", "Distance entre {0} et {1} : {2}M, inférieure à 200M", "Расстояние между {0} и {1}: {2}м, менее 200м" } },
{ "{0}与{1}边界间距为{2}M,小于100M", new[] { "{0}与{1}边界间距为{2}M,小于100M", "Distance between {0} and {1} boundary is {2}M, less than 100M", "Distance entre {0} et {1} : {2}M, inférieure à 100M", "Расстояние между {0} и {1}: {2}м, менее 100м" } },
{ "{0}距离水源距离为{1}M,超出100M", new[] { "{0}距离水源距离为{1}M,超出100M", "{0} distance to water source is {1}M, exceeds 100M", "Distance de {0} à la source d'eau : {1}M, supérieure à 100M", "Расстояние от {0} до источника воды {1}м, более 100м" } },
// VR controller tooltips (VRTK_ControllerTooltips)
{ "展/收", new[] { "展/收", "Deploy/Stow", "Déployer/Ranger", "Развернуть/Свернуть" } },
{ "选中车辆", new[] { "选中车辆", "Select Vehicle", "Sélectionner le véhicule", "Выбрать машину" } },
{ "移动", new[] { "移动", "Move", "Déplacer", "Движение" } },
{ "射线", new[] { "射线", "Ray", "Rayon", "Луч" } },
{ "移动车辆", new[] { "移动车辆", "Move Vehicle", "Déplacer le véhicule", "Переместить машину" } },
{ "选择车辆", new[] { "选择车辆", "Select Vehicle", "Sélectionner le véhicule", "Выбрать машину" } },
{ "触发物体", new[] { "触发物体", "Trigger Object", "Activer l'objet", "Активировать объект" } },
{ "抓取物体", new[] { "抓取物体", "Grab Object", "Saisir l'objet", "Захватить объект" } },
{ "传送", new[] { "传送", "Teleport", "Téléportation", "Телепорт" } },
};
[SerializeField] private int language;

View File

@ -0,0 +1,141 @@
using UnityEngine;
using UnityEngine.UI;
using VRTK;
/// <summary>
/// Localizes VRTK controller tooltip text at runtime.
/// Caches original Chinese texts from prefab fields, then updates
/// via ResetTooltip() when the language changes.
/// </summary>
public class VRControllerTooltipLocalizer : MonoBehaviour
{
private class TooltipCache
{
public VRTK_ControllerTooltips component;
public string triggerText;
public string gripText;
public string touchpadText;
public string buttonOneText;
public string buttonTwoText;
public string startMenuText;
}
private TooltipCache[] caches;
private bool initialized;
void Start()
{
CacheTooltips();
if (initialized) ApplyLocalization();
LocalizationManager.LanguageChanged += OnLanguageChanged;
}
void OnDestroy()
{
LocalizationManager.LanguageChanged -= OnLanguageChanged;
}
private void OnLanguageChanged()
{
if (initialized) ApplyLocalization();
}
private void CacheTooltips()
{
VRTK_ControllerTooltips[] tooltips = GetComponentsInChildren<VRTK_ControllerTooltips>(true);
if (tooltips == null || tooltips.Length == 0)
{
tooltips = FindObjectsOfType<VRTK_ControllerTooltips>();
}
if (tooltips == null || tooltips.Length == 0) return;
caches = new TooltipCache[tooltips.Length];
for (int i = 0; i < tooltips.Length; i++)
{
caches[i] = new TooltipCache
{
component = tooltips[i],
triggerText = tooltips[i].triggerText,
gripText = tooltips[i].gripText,
touchpadText = tooltips[i].touchpadText,
buttonOneText = tooltips[i].buttonOneText,
buttonTwoText = tooltips[i].buttonTwoText,
startMenuText = tooltips[i].startMenuText
};
}
initialized = true;
}
private void ApplyLocalization()
{
Font font = LocalizationManager.UIFont;
foreach (var cache in caches)
{
if (cache.component == null) continue;
// Set all fields with localized text, then reset once
cache.component.triggerText = LocalizeTooltipText(cache.triggerText);
cache.component.gripText = LocalizeTooltipText(cache.gripText);
cache.component.touchpadText = LocalizeTooltipText(cache.touchpadText);
cache.component.buttonOneText = LocalizeTooltipText(cache.buttonOneText);
cache.component.buttonTwoText = LocalizeTooltipText(cache.buttonTwoText);
cache.component.startMenuText = LocalizeTooltipText(cache.startMenuText);
cache.component.ResetTooltip();
// Apply localized font to tooltip Text components after ResetTooltip
// so SetText() has already run (it doesn't touch font, only material)
if (font != null)
{
VRTK_ObjectTooltip[] objTooltips = cache.component.GetComponentsInChildren<VRTK_ObjectTooltip>(true);
foreach (var ot in objTooltips)
{
ApplyFontRecursive(ot.transform, font);
}
}
}
}
private void ApplyFontRecursive(Transform parent, Font font)
{
Text[] texts = parent.GetComponentsInChildren<Text>(true);
foreach (var t in texts)
{
Color savedColor = t.color;
t.font = font;
t.color = savedColor;
t.verticalOverflow = VerticalWrapMode.Overflow;
}
}
/// <summary>
/// Translate tooltip text that may contain \\n (literal backslash-n)
/// or \n (actual newline) as multi-line separators.
/// Each line is translated independently.
/// </summary>
private static string LocalizeTooltipText(string original)
{
if (string.IsNullOrEmpty(original)) return original;
// VRTK uses literal \\n in prefab fields; SetText() converts to \n
if (original.Contains("\\n"))
{
string[] parts = original.Split(new[] { "\\n" }, System.StringSplitOptions.None);
for (int i = 0; i < parts.Length; i++)
parts[i] = LocalizationManager.Get(parts[i].Trim());
return string.Join("\\n", parts);
}
// Handle actual newlines (in case text was already converted)
if (original.Contains("\n"))
{
string[] parts = original.Split(new[] { "\n" }, System.StringSplitOptions.None);
for (int i = 0; i < parts.Length; i++)
parts[i] = LocalizationManager.Get(parts[i].Trim());
return string.Join("\n", parts);
}
return LocalizationManager.Get(original);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b6e286fc5954c4045bb66e03d68ba7bb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -22,6 +22,7 @@ public class VRManager : IVRInput
{
SDKManager = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>(ResourcesManager.GetPath(vrtkPrefabName))).GetComponent<VRTK_SDKManager>();
SDKManager.transform.position = postion;
SDKManager.gameObject.AddComponent<VRControllerTooltipLocalizer>();
SDKManager.LoadedSetupChanged += SDKManager_LoadedSetupChanged;
VRTK_Logger.instance.throwExceptions = false;
//leftInput = SDKManager.scriptAliasLeftController.GetComponent<IVRInput>();