天天看点

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、项目工程源代码

待补~~

大佬们找到问题欢迎拍砖~