unityzgy/.svn/pristine/8b/8b2caa61dcc8e8631345a35c5fde1b2f5c67e210.svn-base
ayuan9957 bf12e02276 feat: 多语言本地化系统 - 支持中/英/法/俄实时切换
- LocalizationManager: 新增翻译表(SetFormatted/SetDuration/LocalizeOperateMsg)
- LoginPanel: InputField placeholder本地化、字体颜色保持
- HistoryPanel: 用时数据本地化、placeholder本地化
- RecordDetailPanel: 操作详情消息本地化(LanguageChanged重建)
- AppraiseWindowBase: 评价等级本地化、操作消息重建
- EditorAppraiser: 所有评估消息改用InjectOperateMsgLocalized
- StudentOperateRecorder: 新增InjectOperateMsgLocalized方法
- LocalizationLanguageTestBar: 单例模式、ScreenSpaceOverlay筛选
- 字体切换时保留颜色和verticalOverflow
2026-07-16 10:05:59 +08:00

145 lines
6.3 KiB
Plaintext

// Radial Menu Controller|Prefabs|0045
namespace VRTK
{
using UnityEngine;
/// <summary>
/// This adds a UI element into the world space that can be dropped into a Controller object and used to create and use Radial Menus from the touchpad.
/// </summary>
/// <remarks>
/// If the RadialMenu is placed inside a controller, it will automatically find a `VRTK_ControllerEvents` in its parent to use at the input. However, a `VRTK_ControllerEvents` can be defined explicitly by setting the `Events` parameter of the `Radial Menu Controller` script also attached to the prefab.
///
/// The RadialMenu can also be placed inside a `VRTK_InteractableObject` for the RadialMenu to be anchored to a world object instead of the controller. The `Events Manager` parameter will automatically be set if the RadialMenu is a child of an InteractableObject, but it can also be set manually in the inspector. Additionally, for the RadialMenu to be anchored in the world, the `RadialMenuController` script in the prefab must be replaced with `VRTK_IndependentRadialMenuController`. See the script information for further details on making the RadialMenu independent of the controllers.
/// </remarks>
/// <example>
/// `VRTK/Examples/030_Controls_RadialTouchpadMenu` displays a radial menu for each controller. The left controller uses the `Hide On Release` variable, so it will only be visible if the left touchpad is being touched. It also uses the `Execute On Unclick` variable to delay execution until the touchpad button is unclicked. The example scene also contains a demonstration of anchoring the RadialMenu to an interactable cube instead of a controller.
/// </example>
[RequireComponent(typeof(VRTK_RadialMenu))]
public class VRTK_RadialMenuController : MonoBehaviour
{
[Tooltip("The controller to listen to the controller events on.")]
public VRTK_ControllerEvents events;
protected VRTK_RadialMenu menu;
protected float currentAngle; //Keep track of angle for when we click
protected bool touchpadTouched;
protected virtual void Awake()
{
menu = GetComponent<VRTK_RadialMenu>();
Initialize();
}
protected virtual void Initialize()
{
if (events == null)
{
events = GetComponentInParent<VRTK_ControllerEvents>();
}
}
protected virtual void OnEnable()
{
if (events == null)
{
VRTK_Logger.Error(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.REQUIRED_COMPONENT_MISSING_NOT_INJECTED, "RadialMenuController", "VRTK_ControllerEvents", "events", "the parent"));
return;
}
else
{
events.TouchpadPressed += new ControllerInteractionEventHandler(DoTouchpadClicked);
events.TouchpadReleased += new ControllerInteractionEventHandler(DoTouchpadUnclicked);
events.TouchpadTouchStart += new ControllerInteractionEventHandler(DoTouchpadTouched);
events.TouchpadTouchEnd += new ControllerInteractionEventHandler(DoTouchpadUntouched);
events.TouchpadAxisChanged += new ControllerInteractionEventHandler(DoTouchpadAxisChanged);
menu.FireHapticPulse += new HapticPulseEventHandler(AttemptHapticPulse);
}
}
protected virtual void OnDisable()
{
events.TouchpadPressed -= new ControllerInteractionEventHandler(DoTouchpadClicked);
events.TouchpadReleased -= new ControllerInteractionEventHandler(DoTouchpadUnclicked);
events.TouchpadTouchStart -= new ControllerInteractionEventHandler(DoTouchpadTouched);
events.TouchpadTouchEnd -= new ControllerInteractionEventHandler(DoTouchpadUntouched);
events.TouchpadAxisChanged -= new ControllerInteractionEventHandler(DoTouchpadAxisChanged);
menu.FireHapticPulse -= new HapticPulseEventHandler(AttemptHapticPulse);
}
protected virtual void DoClickButton(object sender = null) // The optional argument reduces the need for middleman functions in subclasses whose events likely pass object sender
{
menu.ClickButton(currentAngle);
}
protected virtual void DoUnClickButton(object sender = null)
{
menu.UnClickButton(currentAngle);
}
protected virtual void DoShowMenu(float initialAngle, object sender = null)
{
menu.ShowMenu();
DoChangeAngle(initialAngle); // Needed to register initial touch position before the touchpad axis actually changes
}
protected virtual void DoHideMenu(bool force, object sender = null)
{
menu.StopTouching();
menu.HideMenu(force);
}
protected virtual void DoChangeAngle(float angle, object sender = null)
{
currentAngle = angle;
menu.HoverButton(currentAngle);
}
protected virtual void AttemptHapticPulse(float strength)
{
if (events)
{
VRTK_ControllerHaptics.TriggerHapticPulse(VRTK_ControllerReference.GetControllerReference(events.gameObject), strength);
}
}
protected virtual void DoTouchpadClicked(object sender, ControllerInteractionEventArgs e)
{
DoClickButton();
}
protected virtual void DoTouchpadUnclicked(object sender, ControllerInteractionEventArgs e)
{
DoUnClickButton();
}
protected virtual void DoTouchpadTouched(object sender, ControllerInteractionEventArgs e)
{
touchpadTouched = true;
DoShowMenu(CalculateAngle(e));
}
protected virtual void DoTouchpadUntouched(object sender, ControllerInteractionEventArgs e)
{
touchpadTouched = false;
DoHideMenu(false);
}
//Touchpad finger moved position
protected virtual void DoTouchpadAxisChanged(object sender, ControllerInteractionEventArgs e)
{
if (touchpadTouched)
{
DoChangeAngle(CalculateAngle(e));
}
}
protected virtual float CalculateAngle(ControllerInteractionEventArgs e)
{
return 360 - e.touchpadAngle;
}
}
}