unityzgy/.svn/pristine/ab/ab3c08245a4b653d660f39cec888a6e194733f21.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

315 lines
13 KiB
Plaintext

// Player Climb|Locomotion|20120
namespace VRTK
{
using GrabAttachMechanics;
using UnityEngine;
/// <summary>
/// Event Payload
/// </summary>
/// <param name="controllerIndex">**OBSOLETE** The index of the controller doing the interaction.</param>
/// <param name="controllerReference">The reference to the controller doing the interaction.</param>
/// <param name="target">The GameObject of the interactable object that is being interacted with by the controller.</param>
public struct PlayerClimbEventArgs
{
[System.Obsolete("`PlayerClimbEventArgs.controllerIndex` has been replaced with `PlayerClimbEventArgs.controllerReference`. This parameter will be removed in a future version of VRTK.")]
public uint controllerIndex;
public VRTK_ControllerReference controllerReference;
public GameObject target;
}
/// <summary>
/// Event Payload
/// </summary>
/// <param name="sender">this object</param>
/// <param name="e"><see cref="PlayerClimbEventArgs"/></param>
public delegate void PlayerClimbEventHandler(object sender, PlayerClimbEventArgs e);
/// <summary>
/// The Player Climb allows player movement based on grabbing of `VRTK_InteractableObject` objects that have a `Climbable` grab attach script. Because it works by grabbing, each controller should have a `VRTK_InteractGrab` and `VRTK_InteractTouch` component attached.
/// </summary>
/// <example>
/// `VRTK/Examples/037_CameraRig_ClimbingFalling` shows how to set up a scene with player climbing. There are many different examples showing how the same system can be used in unique ways.
/// </example>
[RequireComponent(typeof(VRTK_BodyPhysics))]
[AddComponentMenu("VRTK/Scripts/Locomotion/VRTK_PlayerClimb")]
public class VRTK_PlayerClimb : MonoBehaviour
{
[Header("Climb Settings")]
[Tooltip("Will scale movement up and down based on the player transform's scale.")]
public bool usePlayerScale = true;
[Header("Custom Settings")]
[Tooltip("The VRTK Body Physics script to use for dealing with climbing and falling. If this is left blank then the script will need to be applied to the same GameObject.")]
public VRTK_BodyPhysics bodyPhysics;
[Tooltip("The VRTK Teleport script to use when snapping to nearest floor on release. If this is left blank then a Teleport script will need to be applied to the same GameObject.")]
public VRTK_BasicTeleport teleporter;
[Tooltip("The VRTK Headset Collision script to use for determining if the user is climbing inside a collidable object. If this is left blank then the script will need to be applied to the same GameObject.")]
public VRTK_HeadsetCollision headsetCollision;
[Tooltip("The VRTK Position Rewind script to use for dealing resetting invalid positions. If this is left blank then the script will need to be applied to the same GameObject.")]
public VRTK_PositionRewind positionRewind;
/// <summary>
/// Emitted when player climbing has started.
/// </summary>
public event PlayerClimbEventHandler PlayerClimbStarted;
/// <summary>
/// Emitted when player climbing has ended.
/// </summary>
public event PlayerClimbEventHandler PlayerClimbEnded;
protected Transform playArea;
protected Vector3 startControllerScaledLocalPosition;
protected Vector3 startGrabPointLocalPosition;
protected Vector3 startPlayAreaWorldOffset;
protected GameObject grabbingController;
protected GameObject climbingObject;
protected Quaternion climbingObjectLastRotation;
protected bool isClimbing;
protected bool useGrabbedObjectRotation;
protected virtual void Awake()
{
bodyPhysics = (bodyPhysics != null ? bodyPhysics : GetComponentInChildren<VRTK_BodyPhysics>());
teleporter = (teleporter != null ? teleporter : GetComponentInChildren<VRTK_BasicTeleport>());
headsetCollision = (headsetCollision != null ? headsetCollision : GetComponentInChildren<VRTK_HeadsetCollision>());
positionRewind = (positionRewind != null ? positionRewind : GetComponentInChildren<VRTK_PositionRewind>());
VRTK_SDKManager.instance.AddBehaviourToToggleOnLoadedSetupChange(this);
}
protected virtual void OnEnable()
{
playArea = VRTK_DeviceFinder.PlayAreaTransform();
InitListeners(true);
}
protected virtual void OnDisable()
{
Ungrab(false, null, climbingObject);
InitListeners(false);
}
protected virtual void OnDestroy()
{
VRTK_SDKManager.instance.RemoveBehaviourToToggleOnLoadedSetupChange(this);
}
protected virtual void Update()
{
if (isClimbing)
{
Vector3 controllerLocalOffset = GetScaledLocalPosition(grabbingController.transform) - startControllerScaledLocalPosition;
Vector3 grabPointWorldPosition = climbingObject.transform.TransformPoint(startGrabPointLocalPosition);
playArea.position = grabPointWorldPosition + startPlayAreaWorldOffset - controllerLocalOffset;
if (useGrabbedObjectRotation)
{
Vector3 lastRotationVec = climbingObjectLastRotation * Vector3.forward;
Vector3 currentObectRotationVec = climbingObject.transform.rotation * Vector3.forward;
Vector3 axis = Vector3.Cross(lastRotationVec, currentObectRotationVec);
float angle = Vector3.Angle(lastRotationVec, currentObectRotationVec);
playArea.RotateAround(grabPointWorldPosition, axis, angle);
climbingObjectLastRotation = climbingObject.transform.rotation;
}
if (positionRewind != null && !IsHeadsetColliding())
{
positionRewind.SetLastGoodPosition();
}
}
}
protected virtual void OnPlayerClimbStarted(PlayerClimbEventArgs e)
{
if (PlayerClimbStarted != null)
{
PlayerClimbStarted(this, e);
}
}
protected virtual void OnPlayerClimbEnded(PlayerClimbEventArgs e)
{
if (PlayerClimbEnded != null)
{
PlayerClimbEnded(this, e);
}
}
protected virtual PlayerClimbEventArgs SetPlayerClimbEvent(VRTK_ControllerReference controllerReference, GameObject target)
{
PlayerClimbEventArgs e;
#pragma warning disable 0618
e.controllerIndex = VRTK_ControllerReference.GetRealIndex(controllerReference);
#pragma warning restore 0618
e.controllerReference = controllerReference;
e.target = target;
return e;
}
protected virtual void InitListeners(bool state)
{
InitControllerListeners(VRTK_DeviceFinder.GetControllerLeftHand(), state);
InitControllerListeners(VRTK_DeviceFinder.GetControllerRightHand(), state);
InitTeleportListener(state);
}
protected virtual void InitTeleportListener(bool state)
{
if (teleporter != null)
{
if (state)
{
teleporter.Teleporting += new TeleportEventHandler(OnTeleport);
}
else
{
teleporter.Teleporting -= new TeleportEventHandler(OnTeleport);
}
}
}
protected virtual void OnTeleport(object sender, DestinationMarkerEventArgs e)
{
if (isClimbing)
{
Ungrab(false, e.controllerReference, e.target.gameObject);
}
}
protected virtual Vector3 GetScaledLocalPosition(Transform objTransform)
{
if (usePlayerScale)
{
return playArea.localRotation * Vector3.Scale(objTransform.localPosition, playArea.localScale);
}
return playArea.localRotation * objTransform.localPosition;
}
protected virtual void OnGrabObject(object sender, ObjectInteractEventArgs e)
{
if (IsClimbableObject(e.target))
{
var controller = ((VRTK_InteractGrab)sender).gameObject;
var actualController = VRTK_DeviceFinder.GetActualController(controller);
Grab(actualController, e.controllerReference, e.target);
}
}
protected virtual void OnUngrabObject(object sender, ObjectInteractEventArgs e)
{
var controller = ((VRTK_InteractGrab)sender).gameObject;
var actualController = VRTK_DeviceFinder.GetActualController(controller);
if (e.target && IsClimbableObject(e.target) && IsActiveClimbingController(actualController))
{
Ungrab(true, e.controllerReference, e.target);
}
}
protected virtual void Grab(GameObject currentGrabbingController, VRTK_ControllerReference controllerReference, GameObject target)
{
bodyPhysics.ResetFalling();
bodyPhysics.TogglePreventSnapToFloor(true);
bodyPhysics.enableBodyCollisions = false;
bodyPhysics.ToggleOnGround(false);
isClimbing = true;
climbingObject = target;
grabbingController = currentGrabbingController;
startControllerScaledLocalPosition = GetScaledLocalPosition(grabbingController.transform);
startGrabPointLocalPosition = climbingObject.transform.InverseTransformPoint(grabbingController.transform.position);
startPlayAreaWorldOffset = playArea.transform.position - grabbingController.transform.position;
climbingObjectLastRotation = climbingObject.transform.rotation;
useGrabbedObjectRotation = climbingObject.GetComponent<VRTK_ClimbableGrabAttach>().useObjectRotation;
OnPlayerClimbStarted(SetPlayerClimbEvent(controllerReference, climbingObject));
}
protected virtual void Ungrab(bool carryMomentum, VRTK_ControllerReference controllerReference, GameObject target)
{
isClimbing = false;
if (positionRewind != null && IsHeadsetColliding())
{
positionRewind.RewindPosition();
}
if (IsBodyColliding() && !IsHeadsetColliding())
{
bodyPhysics.ForceSnapToFloor();
}
bodyPhysics.enableBodyCollisions = true;
if (carryMomentum)
{
Vector3 velocity = Vector3.zero;
if (VRTK_ControllerReference.IsValid(controllerReference))
{
velocity = -VRTK_DeviceFinder.GetControllerVelocity(controllerReference);
if (usePlayerScale)
{
velocity = playArea.TransformVector(velocity);
}
else
{
velocity = playArea.TransformDirection(velocity);
}
}
bodyPhysics.ApplyBodyVelocity(velocity, true, true);
}
grabbingController = null;
climbingObject = null;
OnPlayerClimbEnded(SetPlayerClimbEvent(controllerReference, target));
}
protected virtual bool IsActiveClimbingController(GameObject controller)
{
return (controller == grabbingController);
}
protected virtual bool IsClimbableObject(GameObject obj)
{
var interactObject = obj.GetComponent<VRTK_InteractableObject>();
return (interactObject && interactObject.grabAttachMechanicScript && interactObject.grabAttachMechanicScript.IsClimbable());
}
protected virtual void InitControllerListeners(GameObject controller, bool state)
{
if (controller)
{
var grabScript = controller.GetComponent<VRTK_InteractGrab>();
if (grabScript)
{
if (state)
{
grabScript.ControllerGrabInteractableObject += new ObjectInteractEventHandler(OnGrabObject);
grabScript.ControllerUngrabInteractableObject += new ObjectInteractEventHandler(OnUngrabObject);
}
else
{
grabScript.ControllerGrabInteractableObject -= new ObjectInteractEventHandler(OnGrabObject);
grabScript.ControllerUngrabInteractableObject -= new ObjectInteractEventHandler(OnUngrabObject);
}
}
}
}
protected virtual bool IsBodyColliding()
{
return (bodyPhysics != null && bodyPhysics.GetCurrentCollidingObject() != null);
}
protected virtual bool IsHeadsetColliding()
{
return (headsetCollision != null && headsetCollision.IsColliding());
}
}
}