以下内容是根据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中看到新增的按钮了,此时点击没有反应:
接着在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、最终效果
7、项目工程源代码
待补~~
大佬们找到问题欢迎拍砖~