UGUIカーネル大探究(四)SelectableとButton
7637 ワード
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に基づいて新しいカスタムコンポーネントを派生させることもできます.
[AddComponentMenu("UI/Selectable", 70)]
[ExecuteInEditMode]
[SelectionBase]
[DisallowMultipleComponent]
public class Selectable
:
UIBehaviour,
IMoveHandler,
IPointerDownHandler, IPointerUpHandler,
IPointerEnterHandler, IPointerExitHandler,
ISelectHandler, IDeselectHandler
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;
}
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;
}
}
}