VRTK_Pointer腳本簡(jiǎn)析(VRTK_v3.3.0版)

該腳本的父類(lèi)是VRTK_DestinationMarker
提供能夠從指定的GameObject發(fā)出指針怀偷;

必備組建

渲染類(lèi) VRTK_BasePointerRenderer:一般是繼承它的兩個(gè)子類(lèi),一個(gè)是直線(xiàn)渲染举反,一個(gè)是貝塞爾曲線(xiàn)渲染刺啦;

可選組建

VRTK_ControllerEvents :監(jiān)聽(tīng)按鈕宫补,
VRTK_InteractUse:

事件

ControllerInteractionEventHandler ActivationButtonPressed
ControllerInteractionEventHandler ActivationButtonReleased
ControllerInteractionEventHandler SelectionButtonPressed
ControllerInteractionEventHandler SelectionButtonReleased
ControllerInteractionEventHandler 委托來(lái)自VRTK_ControllerEvents類(lèi)划滋;
DestinationMarkerEventHandler PointerStateValid
DestinationMarkerEventHandler PointerStateInvalid
DestinationMarkerEventHandler委托來(lái)自VRTK_DestinationMarker父類(lèi);

方法

bool IsActivationButtonPressed()
IsSelectionButtonPressed()

Pointer(RaycastHit givenHit)
當(dāng)指針首次進(jìn)入有效對(duì)象時(shí)极景,PointerEnter方法會(huì)發(fā)出DestinationMarkerEnter事件,它會(huì)為指針停留在有效對(duì)象上的每個(gè)后續(xù)幀發(fā)出一個(gè)DestinationMarkerHover驾茴。
PointerExit(RaycastHit givenHit)
當(dāng)指針離開(kāi)先前輸入的對(duì)象時(shí)盼樟,PointerExit方法會(huì)發(fā)出DestinationMarkerExit事件
bool CanActivate()
用于確定指針是否已超過(guò)激活時(shí)間限制。
bool CanSelect()
用于確定指針是否已超過(guò)選擇時(shí)間限制锈至。
bool IsPointerActive()
用于確定指針的當(dāng)前狀態(tài)是否處于活動(dòng)狀態(tài)晨缴。
ResetActivationTimer(bool forceZero = false)
用于將指針激活計(jì)時(shí)器重置為下一個(gè)有效的激活時(shí)間
ResetSelectionTimer(bool forceZero = false)
用于將指針選擇計(jì)時(shí)器重置為下一個(gè)有效的激活時(shí)間
Toggle(bool state)
用于啟用或禁用指針。
bool IsStateValid()
用于確定指針當(dāng)前是否處于有效狀態(tài)(即峡捡,它的有效顏色);

私有方法

Awake() 調(diào)用VRTK_SDKManager類(lèi)的一個(gè)Add方法
OnEnable()
OnDisable()
OnDestroy()

// Pointer|Pointers|10020
 namespace VRTK
 {
using UnityEngine;

/// <summary>
/// Provides a basis of being able to emit a pointer from a specified GameObject.
/// </summary>
/// <remarks>
/// **Required Components:**
///  * `VRTK_BasePointerRenderer` - The visual representation of the pointer when activated.
///
/// **Optional Components:**
///  * `VRTK_ControllerEvents` - The events component to listen for the button presses on. This must be applied on the same GameObject as this script if one is not provided via the `Controller` parameter.
///  * `VRTK_InteractUse` - The use component to utilise when the pointer is to activate the use action on an Interactable Object. This must be applied on the same GameObject as this script if one is not provided via the `Interact Use` parameter.
/// 
/// **Script Usage:**
///  * Place the `VRTK_Pointer` script on either:
///    * The controller script alias GameObject of the controller to emit the pointer from (e.g. Right Controller Script Alias).
///    * Any other scene GameObject and provide a valid `Transform` component to the `Custom Origin` parameter of this script. This does not have to be a controller and can be any GameObject that will emit the pointer.
///  * Link the required Base Pointer Renderer script to the `Pointer Renderer` parameter of this script.
/// </remarks>
[AddComponentMenu("VRTK/Scripts/Pointers/VRTK_Pointer")]
public class VRTK_Pointer : VRTK_DestinationMarker
{
    [Header("Pointer Activation Settings")]

    [Tooltip("The specific renderer to use when the pointer is activated. The renderer also determines how the pointer reaches it's destination (e.g. straight line, bezier curve).")]
    public VRTK_BasePointerRenderer pointerRenderer;//指針?shù)秩酒?    [Tooltip("The button used to activate/deactivate the pointer.")]
    public VRTK_ControllerEvents.ButtonAlias activationButton = VRTK_ControllerEvents.ButtonAlias.TouchpadPress;//指針激活按鈕
    [Tooltip("If this is checked then the Activation Button needs to be continuously held down to keep the pointer active. If this is unchecked then the Activation Button works as a toggle, the first press/release enables the pointer and the second press/release disables the pointer.")]
    public bool holdButtonToActivate = true;//true是保持按鈕激活击碗,false反之;
    [Tooltip("If this is checked then the pointer will be toggled on when the script is enabled.")]
    public bool activateOnEnable = false;//指針是否激活
    [Tooltip("The time in seconds to delay the pointer being able to be active again.")]
    public float activationDelay = 0f;//延遲時(shí)間

    [Header("Pointer Selection Settings")]

    [Tooltip("The button used to execute the select action at the pointer's target position.")]
    public VRTK_ControllerEvents.ButtonAlias selectionButton = VRTK_ControllerEvents.ButtonAlias.TouchpadPress;//選則按鈕用于在指針的目標(biāo)位置執(zhí)行選擇操作的按鈕们拙。
    [Tooltip("If this is checked then the pointer selection action is executed when the Selection Button is pressed down. If this is unchecked then the selection action is executed when the Selection Button is released.")]
    public bool selectOnPress = false;//如果選中此選項(xiàng)稍途,則在按下選擇按鈕時(shí)執(zhí)行指針選擇操作。如果未選中此選項(xiàng)砚婆,則在釋放選擇按鈕時(shí)執(zhí)行選擇操作械拍。
    [Tooltip("The time in seconds to delay the pointer being able to execute the select action again.")]
    public float selectionDelay = 0f;//選擇延遲:延遲指針再次執(zhí)行選擇操作的時(shí)間(以秒為單位)
    [Tooltip("The amount of time the pointer can be over the same collider before it automatically attempts to select it. 0f means no selection attempt will be made.")]
    public float selectAfterHoverDuration = 0f;//指針在自動(dòng)嘗試選擇它之前可以在同一個(gè)對(duì)撞機(jī)上的時(shí)間。0f表示不會(huì)進(jìn)行任何選擇嘗試。

    [Header("Pointer Interaction Settings")]

    [Tooltip("If this is checked then the pointer will be an extension of the controller and able to interact with Interactable Objects.")]
    public bool interactWithObjects = false;//如果選中此項(xiàng)坷虑,則指針將成為控制器的擴(kuò)展甲馋,并且能夠與Interactable對(duì)象進(jìn)行交互
    [Tooltip("If `Interact With Objects` is checked and this is checked then when an object is grabbed with the pointer touching it, the object will attach to the pointer tip and not snap to the controller.")]
    public bool grabToPointerTip = false;//如果Interact With Objects選中并且已選中此選項(xiàng),則當(dāng)指針觸摸它時(shí)抓取對(duì)象時(shí)猖吴,對(duì)象將附加到指針尖端而不會(huì)捕捉到控制器摔刁。

    [Header("Pointer Customisation Settings")]

    [Tooltip("An optional GameObject that determines what the pointer is to be attached to. If this is left blank then the GameObject the script is on will be used.")]
    public GameObject attachedTo;//用于確定指針要附加到的內(nèi)容挥转。如果將其留空海蔽,則將使用腳本所在的GameObject
    [Tooltip("An optional Controller Events that will be used to toggle the pointer. If the script is being applied onto a controller then this parameter can be left blank as it will be auto populated by the controller the script is on at runtime.")]
    public VRTK_ControllerEvents controllerEvents;//控制器事件
    [Tooltip("An optional InteractUse script that will be used when using interactable objects with pointer. If this is left blank then it will attempt to get the InteractUse script from the same GameObject and if it cannot find one then it will attempt to get it from the attached controller.")]
    public VRTK_InteractUse interactUse;//可選的InteractUse腳本,在將可交互對(duì)象與指針一起使用時(shí)將使用绑谣。如果這是空白党窜,那么它將嘗試從同一個(gè)GameObject獲取InteractUse腳本,如果它找不到借宵,那么它將嘗試從連接的控制器獲取它幌衣。
    [Tooltip("A custom transform to use as the origin of the pointer. If no pointer origin transform is provided then the transform the script is attached to is used.")]
    public Transform customOrigin;//用作指針原點(diǎn)的自定義變換。如果未提供指針原點(diǎn)變換壤玫,則使用腳本附加到的變換豁护。

    [Header("Obsolete Settings")]

    [System.Obsolete("`VRTK_Pointer.controller` has been replaced with `VRTK_Pointer.controllerEvents`. This parameter will be removed in a future version of VRTK.")]
    [ObsoleteInspector]
    public VRTK_ControllerEvents controller;

    /// <summary>
    /// Emitted when the pointer activation button is pressed.
    /// </summary>
    public event ControllerInteractionEventHandler ActivationButtonPressed;//按下指針激活按鈕時(shí)發(fā)出
    /// <summary>
    /// Emitted when the pointer activation button is released.
    /// </summary>
    public event ControllerInteractionEventHandler ActivationButtonReleased;//釋放指針激活按鈕時(shí)發(fā)出。
    /// <summary>
    /// Emitted when the pointer selection button is pressed.
    /// </summary>
    public event ControllerInteractionEventHandler SelectionButtonPressed;//按下指針選擇按鈕時(shí)發(fā)出欲间。
    /// <summary>
    /// Emitted when the pointer selection button is released.
    /// </summary>
    public event ControllerInteractionEventHandler SelectionButtonReleased; //釋放指針選擇按鈕時(shí)發(fā)出楚里。
    /// <summary>
    /// Emitted when the pointer is in a valid state.
    /// </summary>
    public event DestinationMarkerEventHandler PointerStateValid;//指針處于有效狀態(tài)時(shí)發(fā)出。
    /// <summary>
    /// Emitted when the pointer is in an invalid state.
    /// </summary>
    public event DestinationMarkerEventHandler PointerStateInvalid;//指針處于無(wú)效狀態(tài)時(shí)發(fā)出猎贴。

    protected VRTK_ControllerEvents.ButtonAlias subscribedActivationButton = VRTK_ControllerEvents.ButtonAlias.Undefined;
    protected VRTK_ControllerEvents.ButtonAlias subscribedSelectionButton = VRTK_ControllerEvents.ButtonAlias.Undefined;
    protected bool currentSelectOnPress;
    protected float activateDelayTimer;
    protected float selectDelayTimer;
    protected float hoverDurationTimer;
    protected int currentActivationState;
    protected bool willDeactivate;
    protected bool wasActivated;
    protected VRTK_ControllerReference controllerReference;
    protected VRTK_InteractableObject pointerInteractableObject = null;
    protected Collider currentCollider;
    protected bool canClickOnHover;
    protected bool activationButtonPressed;
    protected bool selectionButtonPressed;
    protected bool attemptControllerSetup;
    protected VRTK_StraightPointerRenderer autogenPointerRenderer;

    public virtual void OnActivationButtonPressed(ControllerInteractionEventArgs e)
    {
        if (ActivationButtonPressed != null)
        {
            ActivationButtonPressed(this, e);
        }
    }

    public virtual void OnActivationButtonReleased(ControllerInteractionEventArgs e)
    {
        if (ActivationButtonReleased != null)
        {
            ActivationButtonReleased(this, e);
        }
    }

    public virtual void OnSelectionButtonPressed(ControllerInteractionEventArgs e)
    {
        if (SelectionButtonPressed != null)
        {
            SelectionButtonPressed(this, e);
        }
    }

    public virtual void OnSelectionButtonReleased(ControllerInteractionEventArgs e)
    {
        if (SelectionButtonReleased != null)
        {
            SelectionButtonReleased(this, e);
        }
    }

    public virtual void OnPointerStateValid()
    {
        if (PointerStateValid != null)
        {
            PointerStateValid(this, GetStateEventPayload());
        }
    }

    public virtual void OnPointerStateInvalid()
    {
        if (PointerStateInvalid != null)
        {
            PointerStateInvalid(this, GetStateEventPayload());
        }
    }

    /// <summary>
    /// The IsActivationButtonPressed method returns whether the configured activation button is being pressed.
    /// </summary>
    /// <returns>Returns `true` if the activationButton is being pressed.</returns>
    public virtual bool IsActivationButtonPressed()//返回是否按下配置的激活按鈕
    {
        return activationButtonPressed;
    }

    /// <summary>
    /// The IsSelectionButtonPressed method returns whether the configured activation button is being pressed.
    /// </summary>
    /// <returns>Returns `true` if the selectionButton is being pressed.</returns>
    public virtual bool IsSelectionButtonPressed()//返回是否按下配置的選擇按鈕
    {
        return selectionButtonPressed;
    }


    /// <summary>
    /// The PointerEnter method emits a DestinationMarkerEnter event when the pointer first enters a valid object, it emits a DestinationMarkerHover for every following frame that the pointer stays over the valid object.
    /// </summary>
    /// <param name="givenHit">The valid collision.有效的碰撞</param>
    public virtual void PointerEnter(RaycastHit givenHit)
    {
        if (enabled && givenHit.transform != null && (!ControllerRequired() || VRTK_ControllerReference.IsValid(controllerReference)))
        {
            SetHoverSelectionTimer(givenHit.collider);
            DestinationMarkerEventArgs destinationEventArgs = SetDestinationMarkerEvent(givenHit.distance, givenHit.transform, givenHit, givenHit.point, controllerReference, false, GetCursorRotation());
            if (pointerRenderer != null && givenHit.collider != pointerRenderer.GetDestinationHit().collider)
            {
                OnDestinationMarkerEnter(destinationEventArgs);
            }
            else
            {
                OnDestinationMarkerHover(destinationEventArgs);
            }
            StartUseAction(givenHit.transform);
        }
    }

    /// <summary>
    /// The PointerExit method emits a DestinationMarkerExit event when the pointer leaves a previously entered object.
    /// </summary>
    /// <param name="givenHit">The previous valid collision.</param>
    public virtual void PointerExit(RaycastHit givenHit)
    {
        ResetHoverSelectionTimer(givenHit.collider);
        if (givenHit.transform != null && (!ControllerRequired() || VRTK_ControllerReference.IsValid(controllerReference)))
        {
            OnDestinationMarkerExit(SetDestinationMarkerEvent(givenHit.distance, givenHit.transform, givenHit, givenHit.point, controllerReference, false, GetCursorRotation()));
            StopUseAction();
        }
    }

    /// <summary>
    /// The CanActivate method is used to determine if the pointer has passed the activation time limit.
    /// </summary>
    /// <returns>Returns `true` if the pointer can be activated.</returns>
    public virtual bool CanActivate()
    {
        return (Time.time >= activateDelayTimer);
    }

    /// <summary>
    /// The CanSelect method is used to determine if the pointer has passed the selection time limit.
    /// </summary>
    /// <returns>Returns `true` if the pointer can execute the select action.</returns>
    public virtual bool CanSelect()
    {
        return (Time.time >= selectDelayTimer);
    }

    /// <summary>
    /// The IsPointerActive method is used to determine if the pointer's current state is active or not.
    /// </summary>
    /// <returns>Returns `true` if the pointer is currently active.</returns>
    public virtual bool IsPointerActive()
    {
        return (currentActivationState != 0);
    }

    /// <summary>
    /// The ResetActivationTimer method is used to reset the pointer activation timer to the next valid activation time.
    /// </summary>
    /// <param name="forceZero">If this is `true` then the next activation time will be 0.</param>
    public virtual void ResetActivationTimer(bool forceZero = false)
    {
        activateDelayTimer = (forceZero ? 0f : Time.time + activationDelay);
    }

    /// <summary>
    /// The ResetSelectionTimer method is used to reset the pointer selection timer to the next valid activation time.
    /// </summary>
    /// <param name="forceZero">If this is `true` then the next activation time will be 0.</param>
    public virtual void ResetSelectionTimer(bool forceZero = false)
    {
        selectDelayTimer = (forceZero ? 0f : Time.time + selectionDelay);
    }

    /// <summary>
    /// The Toggle method is used to enable or disable the pointer.
    /// </summary>
    /// <param name="state">If `true` the pointer will be enabled if possible, if `false` the pointer will be disabled if possible.</param>
    public virtual void Toggle(bool state)
    {
        if (!CanActivate() || NoPointerRenderer() || CanActivateOnToggleButton(state) || (state && IsPointerActive()) || (!state && !IsPointerActive()))
        {
            return;
        }

        ManageActivationState(willDeactivate ? true : state);
        pointerRenderer.Toggle(IsPointerActive(), state);
        willDeactivate = false;
        if (!state)
        {
            StopUseAction();
        }
    }

    /// <summary>
    /// The IsStateValid method is used to determine if the pointer is currently in a valid state (i.e. on it's valid colour).
    /// </summary>
    /// <returns>Returns `true` if the pointer is in the valid state (showing the valid colour), returns `false` if the pointer is in the invalid state (showing the invalid colour).</returns>
    public virtual bool IsStateValid()
    {
        return (EnabledPointerRenderer() && pointerRenderer.IsValidCollision());
    }

    protected virtual void Awake()
    {
        VRTK_SDKManager.AttemptAddBehaviourToToggleOnLoadedSetupChange(this);
    }

    protected override void OnEnable()
    {
    #pragma warning disable 0618
        controllerEvents = (controller != null && controllerEvents == null ? controller : controllerEvents);
    #pragma warning restore 0618
        base.OnEnable();
        attachedTo = (attachedTo == null ? gameObject : attachedTo);
        if (!VRTK_PlayerObject.IsPlayerObject(gameObject))
        {
            VRTK_PlayerObject.SetPlayerObject(gameObject, VRTK_PlayerObject.ObjectTypes.Pointer);
        }
        SetDefaultValues();
    }

    protected override void OnDisable()
    {
        base.OnDisable();
        Toggle(false);
        if (pointerRenderer != null)
        {
            pointerRenderer.Toggle(false, false);
        }
        UnsubscribeActivationButton();
        UnsubscribeSelectionButton();
        if (autogenPointerRenderer != null)
        {
            Destroy(autogenPointerRenderer);
        }
    }

    protected virtual void OnDestroy()
    {
        VRTK_SDKManager.AttemptRemoveBehaviourToToggleOnLoadedSetupChange(this);
    }

    protected virtual void Update()
    {
        AttemptControllerSetup();
        CheckButtonSubscriptions();
        HandleEnabledPointer();
    }

    protected virtual void SetDefaultValues()
    {
        SetupRenderer();
        activateDelayTimer = 0f;
        selectDelayTimer = 0f;
        hoverDurationTimer = 0f;
        currentActivationState = 0;
        wasActivated = false;
        willDeactivate = false;
        canClickOnHover = false;
        attemptControllerSetup = true;
    }

    protected virtual void AttemptControllerSetup()
    {
        if (attemptControllerSetup)
        {
            if (FindController())
            {
                attemptControllerSetup = false;
                SetupController();
                SetupRenderer();
                if (activateOnEnable)
                {
                    Toggle(true);
                }
            }
        }
    }

    protected virtual void HandleEnabledPointer()
    {
        if (EnabledPointerRenderer())
        {
            pointerRenderer.InitalizePointer(this, targetListPolicy, navmeshData, headsetPositionCompensation);
            pointerRenderer.UpdateRenderer();
            if (!IsPointerActive())
            {
                bool currentPointerVisibility = pointerRenderer.IsVisible();
                pointerRenderer.ToggleInteraction(currentPointerVisibility);
            }
            CheckHoverSelect();
        }
        else
        {
            Toggle(false);
            currentActivationState = 0;
        }
    }

    protected virtual Quaternion? GetCursorRotation()
    {
        if (EnabledPointerRenderer() && pointerRenderer.directionIndicator != null && pointerRenderer.directionIndicator.gameObject.activeInHierarchy)
        {
            return pointerRenderer.directionIndicator.GetRotation();
        }
        return null;
    }

    protected virtual bool EnabledPointerRenderer()
    {
        return (pointerRenderer != null && pointerRenderer.enabled);
    }

    protected virtual bool NoPointerRenderer()
    {
        return (pointerRenderer == null || !pointerRenderer.enabled);
    }

    protected virtual bool CanActivateOnToggleButton(bool state)
    {
        bool result = (state && !holdButtonToActivate && IsPointerActive());
        if (result)
        {
            willDeactivate = true;
        }
        return result;
    }

    protected virtual bool ControllerRequired()
    {
        return (activationButton != VRTK_ControllerEvents.ButtonAlias.Undefined || selectionButton != VRTK_ControllerEvents.ButtonAlias.Undefined);
    }

    protected virtual bool FindController()
    {
        controllerEvents = (controllerEvents == null ? GetComponentInParent<VRTK_ControllerEvents>() : controllerEvents);
        controllerReference = VRTK_ControllerReference.GetControllerReference((controllerEvents != null ? controllerEvents.gameObject : null));

        if (ControllerRequired() && controllerEvents == null)
        {
            VRTK_Logger.Warn(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.REQUIRED_COMPONENT_MISSING_FROM_GAMEOBJECT, "VRTK_Pointer", "VRTK_ControllerEvents", "the Controller Alias", ". To omit this warning, set the `Activation Button` and `Selection Button` to `Undefined`"));
            return false;
        }

        GetInteractUse();

        return true;
    }

    protected virtual void GetInteractUse()
    {
        interactUse = (interactUse != null ? interactUse : GetComponentInChildren<VRTK_InteractUse>());
        interactUse = (interactUse == null && controllerEvents != null ? controllerEvents.GetComponentInChildren<VRTK_InteractUse>() : interactUse);
    }

    protected virtual void SetupController()
    {
        if (controllerEvents != null)
        {
            CheckButtonMappingConflict();
            SubscribeSelectionButton();
            SubscribeActivationButton();
        }
    }

    protected virtual void SetupRenderer()
    {
        if (pointerRenderer == null)
        {
            pointerRenderer = GeneratePointerRenderer();
        }
        if (EnabledPointerRenderer())
        {
            pointerRenderer.InitalizePointer(this, targetListPolicy, navmeshData, headsetPositionCompensation);
        }
    }

    protected virtual VRTK_BasePointerRenderer GeneratePointerRenderer()
    {
        VRTK_BasePointerRenderer returnRenderer = GetComponentInChildren<VRTK_BasePointerRenderer>();
        if (returnRenderer == null)
        {
            returnRenderer = gameObject.AddComponent<VRTK_StraightPointerRenderer>();
            autogenPointerRenderer = (VRTK_StraightPointerRenderer)returnRenderer;
        }
        return returnRenderer;
    }

    protected virtual bool ButtonMappingIsUndefined(VRTK_ControllerEvents.ButtonAlias givenButton, VRTK_ControllerEvents.ButtonAlias givenSubscribedButton)
    {
        return (givenSubscribedButton != VRTK_ControllerEvents.ButtonAlias.Undefined && givenButton == VRTK_ControllerEvents.ButtonAlias.Undefined);
    }

    protected virtual void CheckButtonMappingConflict()
    {
        if (activationButton == selectionButton)
        {
            if (selectOnPress && holdButtonToActivate)
            {
                VRTK_Logger.Warn("`Hold Button To Activate` and `Select On Press` cannot both be checked when using the same button for Activation and Selection. Fixing by setting `Select On Press` to `false`.");
            }

            if (!selectOnPress && !holdButtonToActivate)
            {
                VRTK_Logger.Warn("`Hold Button To Activate` and `Select On Press` cannot both be unchecked when using the same button for Activation and Selection. Fixing by setting `Select On Press` to `true`.");
            }
            selectOnPress = !holdButtonToActivate;
        }
    }

    protected virtual void CheckButtonSubscriptions()
    {
        CheckButtonMappingConflict();

        if (ButtonMappingIsUndefined(selectionButton, subscribedSelectionButton) || selectOnPress != currentSelectOnPress)
        {
            UnsubscribeSelectionButton();
        }

        if (selectionButton != subscribedSelectionButton)
        {
            SubscribeSelectionButton();
            UnsubscribeActivationButton();
        }

        if (ButtonMappingIsUndefined(activationButton, subscribedActivationButton))
        {
            UnsubscribeActivationButton();
        }

        if (activationButton != subscribedActivationButton)
        {
            SubscribeActivationButton();
        }
    }

    protected virtual void SubscribeActivationButton()
    {
        if (subscribedActivationButton != VRTK_ControllerEvents.ButtonAlias.Undefined)
        {
            UnsubscribeActivationButton();
        }

        if (controllerEvents != null)
        {
            controllerEvents.SubscribeToButtonAliasEvent(activationButton, true, DoActivationButtonPressed);
            controllerEvents.SubscribeToButtonAliasEvent(activationButton, false, DoActivationButtonReleased);
            subscribedActivationButton = activationButton;
        }
    }

    protected virtual void UnsubscribeActivationButton()
    {
        if (controllerEvents != null && subscribedActivationButton != VRTK_ControllerEvents.ButtonAlias.Undefined)
        {
            controllerEvents.UnsubscribeToButtonAliasEvent(subscribedActivationButton, true, DoActivationButtonPressed);
            controllerEvents.UnsubscribeToButtonAliasEvent(subscribedActivationButton, false, DoActivationButtonReleased);
            subscribedActivationButton = VRTK_ControllerEvents.ButtonAlias.Undefined;
        }
    }

    protected virtual void PointerActivated()
    {
        if (EnabledPointerRenderer())
        {
            Toggle(true);
        }
    }

    protected virtual void PointerDeactivated()
    {
        if (EnabledPointerRenderer())
        {
            if (IsPointerActive())
            {
                Toggle(false);
            }
        }
    }

    protected virtual void DoActivationButtonPressed(object sender, ControllerInteractionEventArgs e)
    {
        controllerReference = e.controllerReference;
        OnActivationButtonPressed(controllerEvents.SetControllerEvent(ref activationButtonPressed, true));
        PointerActivated();
    }

    protected virtual void DoActivationButtonReleased(object sender, ControllerInteractionEventArgs e)
    {
        controllerReference = e.controllerReference;
        PointerDeactivated();
        OnActivationButtonReleased(controllerEvents.SetControllerEvent(ref activationButtonPressed, false));
    }

    protected virtual void SubscribeSelectionButton()
    {
        if (subscribedSelectionButton != VRTK_ControllerEvents.ButtonAlias.Undefined)
        {
            UnsubscribeSelectionButton();
        }

        if (controllerEvents != null)
        {
            controllerEvents.SubscribeToButtonAliasEvent(selectionButton, true, DoSelectionButtonPressed);
            controllerEvents.SubscribeToButtonAliasEvent(selectionButton, false, DoSelectionButtonReleased);
            controllerEvents.SubscribeToButtonAliasEvent(selectionButton, selectOnPress, SelectionButtonAction);
            subscribedSelectionButton = selectionButton;
            currentSelectOnPress = selectOnPress;
        }
    }

    protected virtual void UnsubscribeSelectionButton()
    {
        if (controllerEvents != null && subscribedSelectionButton != VRTK_ControllerEvents.ButtonAlias.Undefined)
        {
            controllerEvents.UnsubscribeToButtonAliasEvent(selectionButton, true, DoSelectionButtonPressed);
            controllerEvents.UnsubscribeToButtonAliasEvent(selectionButton, false, DoSelectionButtonReleased);
            controllerEvents.UnsubscribeToButtonAliasEvent(subscribedSelectionButton, currentSelectOnPress, SelectionButtonAction);
            subscribedSelectionButton = VRTK_ControllerEvents.ButtonAlias.Undefined;
        }
    }

    protected virtual void DoSelectionButtonPressed(object sender, ControllerInteractionEventArgs e)
    {
        OnSelectionButtonPressed(controllerEvents.SetControllerEvent(ref selectionButtonPressed, true));
    }

    protected virtual void DoSelectionButtonReleased(object sender, ControllerInteractionEventArgs e)
    {
        OnSelectionButtonReleased(controllerEvents.SetControllerEvent(ref selectionButtonPressed, false));
    }

    protected virtual void SelectionButtonAction(object sender, ControllerInteractionEventArgs e)
    {
        controllerReference = e.controllerReference;
        ExecuteSelectionButtonAction();
    }

    protected virtual void ExecuteSelectionButtonAction()
    {
        if (EnabledPointerRenderer() && CanSelect() && (IsPointerActive() || wasActivated))
        {
            wasActivated = false;
            RaycastHit pointerRendererDestinationHit = pointerRenderer.GetDestinationHit();
            AttemptUseOnSet(pointerRendererDestinationHit.transform);
            if (pointerRendererDestinationHit.transform && IsPointerActive() && pointerRenderer.ValidPlayArea() && !PointerActivatesUseAction(pointerInteractableObject) && pointerRenderer.IsValidCollision())
            {
                ResetHoverSelectionTimer(pointerRendererDestinationHit.collider);
                ResetSelectionTimer();
                OnDestinationMarkerSet(SetDestinationMarkerEvent(pointerRendererDestinationHit.distance, pointerRendererDestinationHit.transform, pointerRendererDestinationHit, pointerRendererDestinationHit.point, controllerReference, false, GetCursorRotation()));
            }
        }
    }

    protected virtual bool CanResetActivationState(bool givenState)
    {
        return ((!givenState && holdButtonToActivate) || (givenState && !holdButtonToActivate && currentActivationState >= 2));
    }

    protected virtual void ManageActivationState(bool state)
    {
        if (state)
        {
            currentActivationState++;
        }

        wasActivated = (currentActivationState == 2);

        if (CanResetActivationState(state))
        {
            currentActivationState = 0;
        }
    }

    protected virtual bool PointerActivatesUseAction(VRTK_InteractableObject givenInteractableObject)
    {
        return (givenInteractableObject != null && givenInteractableObject.pointerActivatesUseAction && (!ControllerRequired() || givenInteractableObject.IsValidInteractableController(controllerEvents.gameObject, givenInteractableObject.allowedUseControllers)));
    }

    protected virtual void StartUseAction(Transform target)
    {
        pointerInteractableObject = target.GetComponent<VRTK_InteractableObject>();
        bool cannotUseBecauseNotGrabbed = (pointerInteractableObject && pointerInteractableObject.useOnlyIfGrabbed && !pointerInteractableObject.IsGrabbed());

        if (interactUse != null && PointerActivatesUseAction(pointerInteractableObject) && pointerInteractableObject.holdButtonToUse && !cannotUseBecauseNotGrabbed && pointerInteractableObject.usingState == 0)
        {
            pointerInteractableObject.StartUsing(interactUse);
            pointerInteractableObject.usingState++;
        }
    }

    protected virtual void StopUseAction()
    {
        if (interactUse != null && PointerActivatesUseAction(pointerInteractableObject) && pointerInteractableObject.holdButtonToUse && pointerInteractableObject.IsUsing())
        {
            pointerInteractableObject.StopUsing(interactUse);
            pointerInteractableObject.usingState = 0;
        }
    }

    protected virtual void AttemptUseOnSet(Transform target)
    {
        if (pointerInteractableObject != null && target != null && interactUse != null && PointerActivatesUseAction(pointerInteractableObject))
        {
            if (pointerInteractableObject.IsUsing())
            {
                pointerInteractableObject.StopUsing(interactUse);
                pointerInteractableObject.usingState = 0;
            }
            else if (!pointerInteractableObject.holdButtonToUse)
            {
                pointerInteractableObject.StartUsing(interactUse);
                pointerInteractableObject.usingState++;
            }
        }
    }

    protected virtual void SetHoverSelectionTimer(Collider collider)
    {
        if (collider != currentCollider)
        {
            hoverDurationTimer = 0f;
        }

        if (selectAfterHoverDuration > 0f && hoverDurationTimer <= 0f)
        {
            canClickOnHover = true;
            hoverDurationTimer = selectAfterHoverDuration;
        }

        currentCollider = collider;
    }

    protected virtual void ResetHoverSelectionTimer(Collider collider)
    {
        canClickOnHover = false;
        hoverDurationTimer = (collider == currentCollider ? 0f : hoverDurationTimer);
    }

    protected virtual void CheckHoverSelect()
    {
        if (hoverDurationTimer > 0f)
        {
            hoverDurationTimer -= Time.deltaTime;
        }

        if (canClickOnHover && hoverDurationTimer <= 0f)
        {
            canClickOnHover = false;
            ExecuteSelectionButtonAction();
        }
    }

    protected virtual DestinationMarkerEventArgs GetStateEventPayload()
    {
        DestinationMarkerEventArgs eventPayload = new DestinationMarkerEventArgs();
        if (EnabledPointerRenderer())
        {
            RaycastHit pointerRendererDestinationHit = pointerRenderer.GetDestinationHit();
            eventPayload = SetDestinationMarkerEvent(pointerRendererDestinationHit.distance, pointerRendererDestinationHit.transform, pointerRendererDestinationHit, pointerRendererDestinationHit.point, controllerReference, false, GetCursorRotation());
        }
        return eventPayload;
    }
}

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末班缎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子她渴,更是在濱河造成了極大的恐慌达址,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趁耗,死亡現(xiàn)場(chǎng)離奇詭異沉唠,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)苛败,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)满葛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人著拭,你說(shuō)我怎么就攤上這事纱扭。” “怎么了儡遮?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵乳蛾,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)肃叶,這世上最難降的妖魔是什么蹂随? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮因惭,結(jié)果婚禮上岳锁,老公的妹妹穿的比我還像新娘。我一直安慰自己蹦魔,他們只是感情好激率,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著勿决,像睡著了一般乒躺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上低缩,一...
    開(kāi)封第一講書(shū)人閱讀 51,190評(píng)論 1 299
  • 那天嘉冒,我揣著相機(jī)與錄音,去河邊找鬼咆繁。 笑死讳推,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的玩般。 我是一名探鬼主播银觅,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼壤短!你這毒婦竟也來(lái)了设拟?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤久脯,失蹤者是張志新(化名)和其女友劉穎纳胧,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體帘撰,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡跑慕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了摧找。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片核行。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蹬耘,靈堂內(nèi)的尸體忽然破棺而出芝雪,到底是詐尸還是另有隱情,我是刑警寧澤综苔,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布惩系,位于F島的核電站位岔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏堡牡。R本人自食惡果不足惜抒抬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望晤柄。 院中可真熱鬧擦剑,春花似錦、人聲如沸芥颈。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)浇借。三九已至捉撮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間妇垢,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工肉康, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留闯估,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓吼和,卻偏偏與公主長(zhǎng)得像涨薪,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子炫乓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容