UGUIカーネル大探究(四)SelectableとButton


SelectableはUGUIのコアコンポーネントであり、最も一般的なButtonのほか、Scrolbar、Dropdown、Slider、Toggle、InputFieldなどのコンポーネントのベースクラスでもある。本稿では,SelectableとButtonのソースコードを解析し,両者の実現原理を探究する。


慣例に従って、UGUIソースダウンロードアドレスを添付します.
Selectableの前に、4つのプロパティが追加されています.
    [AddComponentMenu("UI/Selectable", 70)]
    [ExecuteInEditMode]
    [SelectionBase]
    [DisallowMultipleComponent]

AddComponentMenuは、Componentメニューにオプションを追加し、70の順になります.GameObjectのAddComponentボタンをクリックすると、メニューがポップアップされ、UIオプションをクリックすると、最後にselectableが表示されます.
ExecuteInEditModeは、コンポーネントがエディタの下で実行されることを示します.
SelectionBaseは、このオブジェクトを選択ベースオブジェクトとしてマークします.
DisallowMultipleComponentでは、GameObjectに同じコンポーネントが表示されることは許可されていません.1つのオブジェクトに2つのSelectableコンポーネントを追加することはできません.また、1つのButtonオブジェクトにInputFieldコンポーネントを追加することはできません.
SelectableはUIBehaviourから継承され、7つのインタフェースが継承されています.
    public class Selectable
        :
        UIBehaviour,
        IMoveHandler,
        IPointerDownHandler, IPointerUpHandler,
        IPointerEnterHandler, IPointerExitHandler,
        ISelectHandler, IDeselectHandler

UIBehaviourはすべてのUIコンポーネントのベースクラスであり、EventSystemディレクトリの下に置かれている.IsDestroyed以外は虚関数であり、Awake(スクリプトインスタンスがロードされる、すなわちAddComponent)、OnRectTransformDimensionsChange(RectTransform次元が変化する)などの方法を追加してUnityEngineからのイベントを受信するイベントシステムの一部と考えられる.
SelectableのAwakeはGraphicのコンポーネントインスタンスm_を取得しました.TargetGraphic(ImageコンポーネントはGraphicから間接的に継承されます).選択したTransitionがColor Tintの場合、Selectableの状態が変化(通常、ハイライト、押下、無効)するとm_が呼び出されます.TargetGraphicのCrossFadeColorメソッドで、現在の画像を指定した色にグラデーションします.
OnEnablel(呼び出されたタイミングは、Untiy 3 Dコンポーネントの小贴士(一)OnEnabledとOnDisabledを参照)では、このインスタンスがSelectableの静的リストs_に追加されます.リストには(s_ListにUIナビゲーション用のすべての使用可能なSelectableが格納されている)、インスタンスの状態をNormalまたはHighlighted(またはDisabled)に設定します.
OnDisable(呼び出されたタイミングはUntiy 3 Dコンポーネントの小贴士(一)OnEnabledとOnDisabledを参照)では、インスタンスをクリアし(色と画像を復元し、normalアニメーションを再生する必要がある)、s_このインスタンスをListから削除します.
OnDidApplyAnimationProperties(アニメーションのプロパティを適用した場合)では、OnSetPropertyメソッドでI n t e rnalEvealuateAndTransitionToSelectionStateが呼び出され、現在のステータスがリフレッシュされます.
OnCanvasGroup Changed(CanvasGroupが変化した場合)では、新しいGanvasGroupのinteractableが判断され、GanvasGroupのinteractableがfalseの場合、Selectable自体も無効になります.次に、現在のステータスをリフレッシュします.
次に、Selectableが継承したいくつかのインタフェースを見て、UGUIカーネルの大探究(3)入力モジュールを見て、これらのインタフェースのトリガタイミングを理解することができます.
IMoveHandlerから継承するには、OnMoveメソッドを実装する必要があります.移動方向に応じて、次のSelectableコンポーネントに移動します.
IPointerDownHandlerから継承するには、OnPointerDownメソッドを実装する必要があります.EventSystemを呼び出します.current.SetSelectedGameObjectは、自分自身を現在選択されているオブジェクト(自分のOnSelectと前のオブジェクトのOnDeselectを呼び出す)に設定し、isPointerDownをtrueとマークしてステータスをリフレッシュします(isPointerInsideとisPointerDownの同僚がtrueの場合はPressedステータス).
IPointerUpHandlerから継承するには、OnPointerUpメソッドを実装する必要があります.isPointerDownをfalseとしてマークし、ステータスをリフレッシュします.
IPointerEnterHandlerから継承するには、OnPointerEnterメソッドを実装する必要があります.isPointerInsideをtrueとしてマークし、ステータスをリフレッシュします.
IPointerExitHandlerから継承するには、OnPointerExitメソッドを実装する必要があります.isPointerInsideをfalseとしてマークし、ステータスをリフレッシュします.
ISelectHandlerから継承するには、OnSelectメソッドを実装する必要があります.hasSelectionをtrueとしてマークし、ステータスをリフレッシュする(hasSelectionがtrueの場合はHighlightedステータス、またisPointerInsideとisPointerDownもHighlightedステータスを判断する根拠であり、後述する).
IDeselectHandlerから継承するには、OnDeselectメソッドを実装する必要があります.hasSelectionをfalseとしてマークし、ステータスをリフレッシュします.
IsHighlightedメソッド:
        protected bool IsHighlighted(BaseEventData eventData)
        {
            if (!IsActive())
                return false;

            if (IsPressed())
                return false;

            bool selected = hasSelection;
            if (eventData is PointerEventData)
            {
                var pointerData = eventData as PointerEventData;
                selected |=
                    (isPointerDown && !isPointerInside && pointerData.pointerPress == gameObject) // This object pressed, but pointer moved off
                    || (!isPointerDown && isPointerInside && pointerData.pointerPress == gameObject) // This object pressed, but pointer released over (PointerUp event)
                    || (!isPointerDown && isPointerInside && pointerData.pointerPress == null); // Nothing pressed, but pointer is over
            }
            else
            {
                selected |= isPointerInside;
            }
            return selected;
        }

パラメータは入力モジュールから渡されたイベントデータであり,主にイベント応答オブジェクトが本オブジェクト(またはnull)であるか否かを判断するために用いられる.
上記のOnPointerDownなどの方法は,Ever u a t e AndTransitionToSelectionState法により状態を評価してリフレッシュする.この方法では,UpdateSelectionStateがIsPressedとIsHighlightedを呼び出して現在の状態を判断する.I n t e r n a l E v a l u t e AndTransitionToSelectionStateは、現在のコンポーネントが無効になっているかどうかを判断し、DoStateTransitionメソッドを呼び出します.
        protected virtual void DoStateTransition(SelectionState state, bool instant)
        {
            Color tintColor;
            Sprite transitionSprite;
            string triggerName;

            switch (state)
            {
                case SelectionState.Normal:
                    tintColor = m_Colors.normalColor;
                    transitionSprite = null;
                    triggerName = m_AnimationTriggers.normalTrigger;
                    break;
                case SelectionState.Highlighted:
                    tintColor = m_Colors.highlightedColor;
                    transitionSprite = m_SpriteState.highlightedSprite;
                    triggerName = m_AnimationTriggers.highlightedTrigger;
                    break;
                case SelectionState.Pressed:
                    tintColor = m_Colors.pressedColor;
                    transitionSprite = m_SpriteState.pressedSprite;
                    triggerName = m_AnimationTriggers.pressedTrigger;
                    break;
                case SelectionState.Disabled:
                    tintColor = m_Colors.disabledColor;
                    transitionSprite = m_SpriteState.disabledSprite;
                    triggerName = m_AnimationTriggers.disabledTrigger;
                    break;
                default:
                    tintColor = Color.black;
                    transitionSprite = null;
                    triggerName = string.Empty;
                    break;
            }

            if (gameObject.activeInHierarchy)
            {
                switch (m_Transition)
                {
                    case Transition.ColorTint:
                        StartColorTween(tintColor * m_Colors.colorMultiplier, instant);
                        break;
                    case Transition.SpriteSwap:
                        DoSpriteSwap(transitionSprite);
                        break;
                    case Transition.Animation:
                        TriggerAnimation(triggerName);
                        break;
                }
            }
        }

状態に応じて色、ピクチャ、またはアニメーション名を設定し、StartColorTween、DoSpriteSwap、またはTriggerAnimationメソッドを使用してUIに状態の変化を反映します.
最後にButtonコンポーネントについて説明します.ButtonはSelectableから継承され、IPointerClickHandler、ISubmitHandlerの2つのインタフェースを追加的に継承します.UnityEventタイプのイベントonClickも追加されました.onClickイベントは、ユーザのカスタムリスニングを追加することができ、具体的な方法はエディタで追加することもできるし、onClickで追加することもできる.AddListener追加.
OnPointerClickはPressメソッドを呼び出してonClickをコールバックします.
OnSubmitでは、Pressメソッドも呼び出され、ステータスがPressedに切り替わり、コンテキスト呼び出しOnFinishSubmitがオンになり、ステータスが現在のステータス(UpdateSelectionsStateで取得されたステータス)にグラデーションされます.
ButtonはSelectableに対してクリックと確認イベントに応答するインタフェースを追加し,ユーザカスタムリスニングを追加できるonClickイベントを開放していることがわかる.
Selectableの役割は、マウスイベントに基づく4つの状態変化を提供することです.一方、Button、Dropdownなどの派生クラスに基礎的な論理を提供する一方で、Selectableに基づいて新しいカスタムコンポーネントを派生させることもできます.