天天看點

UGUI源代碼之RawImage—Set UVRect Value按鈕1、目的2、參考3、代碼閱讀4、準備修改UGUI源代碼5、增加Set UVRect Value按鈕6、最終效果7、項目工程源代碼

以下内容是根據Unity 2020.1.01f版本進行編寫的

UGUI源代碼之RawImage—Set UVRect Value按鈕

  • 1、目的
  • 2、參考
  • 3、代碼閱讀
  • 4、準備修改UGUI源代碼
  • 5、增加Set UVRect Value按鈕
  • 6、最終效果
  • 7、項目工程源代碼

1、目的

在使用RawImage元件時,發現隻能通過設定的UVRect大小來設定節點的寬高,而不能通過節點寬高設定UVRect的值,是以想試試增加一個設定UVRect Value的按鈕。最終實作出來的效果還需要進一步測試才能用于實際項目中。

2、參考

本文參考Unity官方的UGUI源代碼

Github位址:https://github.com/Unity-Technologies/uGUI

3、代碼閱讀

我們可以先參考下已有的Set Native Size按鈕

首先看看RawImageEditor的代碼(已删除注釋):

SerializedProperty m_Texture;
SerializedProperty m_UVRect;
GUIContent m_UVRectContent;

protected override void OnEnable()
{
    base.OnEnable();
    m_UVRectContent     = EditorGUIUtility.TrTextContent("UV Rect");

    m_Texture           = serializedObject.FindProperty("m_Texture");
    m_UVRect            = serializedObject.FindProperty("m_UVRect");

    SetShowNativeSize(true);
}

public override void OnInspectorGUI()
{
    serializedObject.Update();

    EditorGUILayout.PropertyField(m_Texture);

    AppearanceControlsGUI();
    RaycastControlsGUI();
    EditorGUILayout.PropertyField(m_UVRect, m_UVRectContent);
    SetShowNativeSize(false);
    NativeSizeButtonGUI();

    serializedObject.ApplyModifiedProperties();
}

void SetShowNativeSize(bool instant)
{
    base.SetShowNativeSize(m_Texture.objectReferenceValue != null, instant);
}
           

從代碼可以看出,在OnSpectorGUI函數中,NativeSizeButtonGUI就是控制顯示Set Native Size按鈕的代碼了(如果看不出來是哪段代碼控制顯示Set Native Size按鈕的話,可以嘗試逐行代碼注釋看效果,但是一般可以直接通過看函數名字知道函數大概是做什麼的)

跳轉到NativeSizeButtonGUI函數中(已删除注釋):

protected void NativeSizeButtonGUI()
{
    if (EditorGUILayout.BeginFadeGroup(m_ShowNativeSize.faded))
    {
        EditorGUILayout.BeginHorizontal();
        {
            GUILayout.Space(EditorGUIUtility.labelWidth);
            if (GUILayout.Button(m_CorrectButtonContent, EditorStyles.miniButton))
            {
                foreach (Graphic graphic in targets.Select(obj => obj as Graphic))
                {
                    Undo.RecordObject(graphic.rectTransform, "Set Native Size");
                    graphic.SetNativeSize();
                    EditorUtility.SetDirty(graphic);
                }
            }
        }
        EditorGUILayout.EndHorizontal();
    }
    EditorGUILayout.EndFadeGroup();
}
           

從代碼可以大概了解,如果Set Native Size按鈕的不透明度為1,則使用EditorGUILayout布局将其顯示出來

我們還需要知道Set Native Size按鈕是怎麼定義的,跳轉到GraphicEditor(因為RawImageEditor類繼承自GraphicEditor類,RawImageEditor類中OnEnable函數調用了父類的OnEnable函數,是以要跳轉到GraphicEditor類看OnEnable函數):

protected SerializedProperty m_Script;
protected SerializedProperty m_Color;
protected SerializedProperty m_Material;
protected SerializedProperty m_RaycastTarget;

private GUIContent m_CorrectButtonContent;
protected AnimBool m_ShowNativeSize;

protected virtual void OnDisable()
{
    Tools.hidden = false;
    m_ShowNativeSize.valueChanged.RemoveListener(Repaint);
}

protected virtual void OnEnable()
{
    m_CorrectButtonContent = EditorGUIUtility.TrTextContent("Set Native Size", "Sets the size to match the content.");

    m_Script = serializedObject.FindProperty("m_Script");
    m_Color = serializedObject.FindProperty("m_Color");
    m_Material = serializedObject.FindProperty("m_Material");
    m_RaycastTarget = serializedObject.FindProperty("m_RaycastTarget");

    m_ShowNativeSize = new AnimBool(false);
    m_ShowNativeSize.valueChanged.AddListener(Repaint);
}
           

從代碼中可以看出,m_CorrectButtonContent就是Set Native Size按鈕了,其在OnEnable函數通過EditorGUIUtility.TrTextContent函數設定按鈕内的文本,以及滑鼠停留在按鈕上面時顯示的提示文本;

M_ShowNativeSize則是bool類型資料在Inspector面闆的動畫效果,類名是AnimBool,主要就是漸隐漸顯,這個函數後面使用的時候,直接複制改個名字就行

回看RawImageEditor的代碼,其中OnInspectorGui函數中,還有SetShowNativeSize函數,此函數就是設定此按鈕是否可見,如果可見才會使用NativeSizeButtonGUI函數在Inspector面闆繪制出來,跳轉到SetShowNativeSize函數:

void SetShowNativeSize(bool instant)
{
    base.SetShowNativeSize(m_Texture.objectReferenceValue != null, instant);
}
           

這裡引用的是父類的函數,跳轉到父類的SetShowNativeSize函數:

protected void SetShowNativeSize(bool show, bool instant)
{
    if (instant)
        m_ShowNativeSize.value = show;
    else
        m_ShowNativeSize.target = show;
}
           

結合上面兩個函數來看,其實就是如果RawImage元件的Texture不為空,則設定其動畫的value為true,然後動畫的faded屬性就會變成1,當faded屬性為1時,NativeSizeButtonGUI函數就會把按鈕在Inspector面闆中繪制出來。這裡我們隻需要模仿來寫就可以了

然後我們還需要知道點選按鈕後的函數,從GraphicEditor中的SetShowNativeSize函數可以看出,graphic.SetNativeSize函數就是點選後設定節點寬高的函數了,跳轉到RawImage類找到此函數(跳轉到Graphic類中的SetNativeSize函數隻會看到虛方法,沒有函數實體):

public override void SetNativeSize()
{
    Texture tex = mainTexture;
    if (tex != null)
    {
        int w = Mathf.RoundToInt(tex.width * uvRect.width);
        int h = Mathf.RoundToInt(tex.height * uvRect.height);
        rectTransform.anchorMax = rectTransform.anchorMin;
        rectTransform.sizeDelta = new Vector2(w, h);
    }
}
           

這段代碼比較簡單,隻是擷取Sprite圖檔Texture的寬高,向上取整得到int類型的數,再将其設定為節點RectTransform元件的寬高

4、準備修改UGUI源代碼

請看這篇:UGUI源代碼之修改源代碼的前期準備

已經準備過的同學可以跳過

5、增加Set UVRect Value按鈕

按照上一個步驟得出的結果,我們隻需要模仿上述的Set Native Size按鈕,給RawImage元件增加一個Set UVRect Value按鈕即可

考慮到Graphic類以及GraphicEditor類會有不少子類,是以将所有增加的代碼都寫在RawImage類以及RawImageEditor類中

首先修改RawImageEditor類:

SerializedProperty m_Texture;
SerializedProperty m_UVRect;
GUIContent m_UVRectContent;

//增加Set UVRect Value按鈕及其Insprctor動畫
GUIContent m_SetUVRectButtonContent;
AnimBool m_ShowSetUVRect;

protected override void OnDisable()
{
    base.OnDisable();
    m_ShowSetUVRect.valueChanged.RemoveListener(Repaint);
}

protected override void OnEnable()
{
    base.OnEnable();
    m_UVRectContent     = EditorGUIUtility.TrTextContent("UV Rect");

    m_Texture           = serializedObject.FindProperty("m_Texture");
    m_UVRect            = serializedObject.FindProperty("m_UVRect");

    SetShowNativeSize(true);

    //增加根據目前節點寬高設定UVRect屬性
    m_SetUVRectButtonContent = EditorGUIUtility.TrTextContent("Set UVRect Value", "Sets the values to UVRect.");
    m_ShowSetUVRect = new AnimBool(false);
    m_ShowSetUVRect.valueChanged.AddListener(Repaint);
    SetShowUVRect(true);
}

public override void OnInspectorGUI()
{
    serializedObject.Update();

    EditorGUILayout.PropertyField(m_Texture);

    AppearanceControlsGUI();
    RaycastControlsGUI();
    EditorGUILayout.PropertyField(m_UVRect, m_UVRectContent);
    SetShowNativeSize(false);
    NativeSizeButtonGUI();

    //增加設定UVRect values的按鈕
    SetShowUVRect(false);
    if (EditorGUILayout.BeginFadeGroup(m_ShowSetUVRect.faded))
    {
        EditorGUILayout.BeginHorizontal();
        {
            GUILayout.Space(EditorGUIUtility.labelWidth);
            if (GUILayout.Button(m_SetUVRectButtonContent, EditorStyles.miniButton))
            {
                foreach (RawImage rawImage in targets.Select(obj => obj as RawImage))
                {
                    Undo.RecordObject(rawImage.rectTransform, "Set UVRect value");
                    rawImage.SetUVRectValues();
                    EditorUtility.SetDirty(rawImage);
                }
            }
        }
        EditorGUILayout.EndHorizontal();
    }
    EditorGUILayout.EndFadeGroup();

    serializedObject.ApplyModifiedProperties();
}

void SetShowUVRect(bool instant)
{
    if (instant)
        m_ShowSetUVRect.value = m_Texture.objectReferenceValue != null;
    else
        m_ShowSetUVRect.target = m_Texture.objectReferenceValue != null;
}
           

首先增加按鈕及其動畫屬性,在OnEnable函數中模仿Set Native Size按鈕進行初始化,同樣的在OnDisable函數中移除事件監聽,模仿SetShowNativeSize函數,增加了一個SetShowUVRect的函數

另外,繪制按鈕時,按鈕的響應函數也要改變:

if (EditorGUILayout.BeginFadeGroup(m_ShowSetUVRect.faded))
{
    EditorGUILayout.BeginHorizontal();
    {
        GUILayout.Space(EditorGUIUtility.labelWidth);
        if (GUILayout.Button(m_SetUVRectButtonContent, EditorStyles.miniButton))
        {
            foreach (RawImage rawImage in targets.Select(obj => obj as RawImage))
            {
                Undo.RecordObject(rawImage.rectTransform, "Set UVRect value");
                rawImage.SetUVRectValues();
                EditorUtility.SetDirty(rawImage);
            }
        }
    }
    EditorGUILayout.EndHorizontal();
}
           

其中,在GUILayout.Button中的foreach循環用的是Graphic類,但我把代碼都寫在了RawImageEditor類以及RawImage類中,是以引用的類型需要改為RawImage。另外,rawImage.SetUVRectValues就是我在RawImage類中增加的設定UVRect值的函數了

如果此時注釋掉rawImage.SetUVRectValues()這行代碼,就可以在Unity中看到新增的按鈕了,此時點選沒有反應:

UGUI源代碼之RawImage—Set UVRect Value按鈕1、目的2、參考3、代碼閱讀4、準備修改UGUI源代碼5、增加Set UVRect Value按鈕6、最終效果7、項目工程源代碼

接着在RawImage類中增加SetUVRectValues函數:

public void SetUVRectValues()
{
    m_UVRect.width = rectTransform.sizeDelta.x / mainTexture.width;
    m_UVRect.height = rectTransform.sizeDelta.y / mainTexture.height;
    SetVerticesDirty();
}
           

代碼比較簡單,就隻是用節點的寬高分别除以Texture的寬高,得到的就是目前節點寬高對應的UVRect值了。SetVerticesDirty函數是Graphic類的函數,用于修改頂點,任何修改到元件效果的函數一般都要重新修改頂點

最好記得把剛剛在RawImageEditor注釋掉的代碼取消注釋,就大功告成了!

6、最終效果

UGUI源代碼之RawImage—Set UVRect Value按鈕1、目的2、參考3、代碼閱讀4、準備修改UGUI源代碼5、增加Set UVRect Value按鈕6、最終效果7、項目工程源代碼

7、項目工程源代碼

待補~~

大佬們找到問題歡迎拍磚~