UGUI中我们常用的控件比如Button、Label、ToggleGroup,往往我们会基于这些最基本的控件来实现我们的高级控件,那么本篇文章我们来学习下我们自定义的UGUI拓展控件。
1、UIButton 单独可使用
这个脚本可以 提供外部监听按下/点击/释放的3个事件(参数附带着数据)。 源官方Button只有 点击事件
using UnityEngine.Events;
using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions
{
/// <summary>
/// UIButton
/// </summary>
[AddComponentMenu("UI/Extensions/UI Button")]
public class UIButton : Button, IPointerDownHandler, IPointerUpHandler
{
#region Sub-Classes
[System.Serializable]
public class UIButtonEvent : UnityEvent<PointerEventData.InputButton> { }
#endregion
#region Events
public UIButtonEvent OnButtonClick;
public UIButtonEvent OnButtonPress;
public UIButtonEvent OnButtonRelease;
#endregion
public override void OnPointerClick(PointerEventData eventData)
{
base.OnSubmit(eventData);
if (OnButtonClick != null)
{
OnButtonClick.Invoke(eventData.button);
}
}
void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
{
DoStateTransition(SelectionState.Pressed, false);
if (OnButtonPress != null)
{
OnButtonPress.Invoke(eventData.button);
}
}
void IPointerUpHandler.OnPointerUp(PointerEventData eventData)
{
DoStateTransition(SelectionState.Normal, false);
if (OnButtonRelease != null)
{
OnButtonRelease.Invoke(eventData.button);
}
}
}
}
2、UIFlippable 单独可使用
我需要一些时间现在翻转精灵和使用scale解决方案被证明是一个糟糕的主意,当使用布局网格等,所以试着写一个小脚本来翻转顶点相反。它目前做这份工作对我来说,我想分享它所以它在这儿……
这种翻转还有两种实现方式: 设置 Rotation x/y =>180, Scale x/y =>-1;
using System.Collections.Generic;
namespace UnityEngine.UI.Extensions
{
[RequireComponent(typeof(RectTransform), typeof(Graphic)), DisallowMultipleComponent]
[AddComponentMenu("UI/Effects/Extensions/Flippable")]
public class UIFlippable : MonoBehaviour, IMeshModifier
{
[SerializeField] private bool m_Horizontal = false;
[SerializeField] private bool m_Veritical = false;
/// <summary>
/// 获取或设置一个值,该值指示是否应水平翻转
/// </summary>
/// <value><c>true</c> if horizontal; otherwise, <c>false</c>.</value>
public bool horizontal
{
get { return this.m_Horizontal; }
set { this.m_Horizontal = value; }
}
/// <summary>
/// 获取或设置一个值,该值指示是否应垂直翻转
/// </summary>
/// <value><c>true</c> if vertical; otherwise, <c>false</c>.</value>
public bool vertical
{
get { return this.m_Veritical; }
set { this.m_Veritical = value; }
}
protected void OnValidate()
{
this.GetComponent<Graphic>().SetVerticesDirty();
}
// 从mesh 得到 顶点集
public void ModifyMesh(/*List<UIVertex> verts*/ Mesh mesh)
{
List<UIVertex> verts = new List<UIVertex> ();
using (VertexHelper vertexHelper = new VertexHelper (mesh))
{
vertexHelper.GetUIVertexStream (verts);
}
RectTransform rt = this.transform as RectTransform;
for (int i = 0; i < verts.Count; ++i)
{
UIVertex v = verts[i];
// Modify positions
v.position = new Vector3(
(this.m_Horizontal ? (v.position.x + (rt.rect.center.x - v.position.x) * 2) : v.position.x),
(this.m_Veritical ? (v.position.y + (rt.rect.center.y - v.position.y) * 2) : v.position.y),
v.position.z
);
// Apply
verts[i] = v;
}
// 在合成mesh
using (VertexHelper vertexHelper2 = new VertexHelper ())
{
vertexHelper2.AddUIVertexTriangleStream (verts);
vertexHelper2.FillMesh (mesh);
}
}
}
}
3、uGUITools 单独可使用
让你更了解 锚点、RectTransform等, Editor脚本
using UnityEditor;
namespace UnityEngine.UI.Extensions
{
public class uGUITools : MonoBehaviour
{
[MenuItem("uGUI/Anchors to Corners %[")]
static void AnchorsToCorners()
{
foreach (Transform transform in Selection.transforms)
{
RectTransform t = transform as RectTransform;
RectTransform pt = Selection.activeTransform.parent as RectTransform;
if (t == null || pt == null) return;
Vector2 newAnchorsMin = new Vector2(t.anchorMin.x + t.offsetMin.x / pt.rect.width,
t.anchorMin.y + t.offsetMin.y / pt.rect.height);
Vector2 newAnchorsMax = new Vector2(t.anchorMax.x + t.offsetMax.x / pt.rect.width,
t.anchorMax.y + t.offsetMax.y / pt.rect.height);
t.anchorMin = newAnchorsMin;
t.anchorMax = newAnchorsMax;
t.offsetMin = t.offsetMax = new Vector2(0, 0);
}
}
[MenuItem("uGUI/Corners to Anchors %]")]
static void CornersToAnchors()
{
foreach (Transform transform in Selection.transforms)
{
RectTransform t = transform as RectTransform;
if (t == null) return;
t.offsetMin = t.offsetMax = new Vector2(0, 0);
}
}
[MenuItem("uGUI/Mirror Horizontally Around Anchors %;")]
static void MirrorHorizontallyAnchors()
{
MirrorHorizontally(false);
}
[MenuItem("uGUI/Mirror Horizontally Around Parent Center %:")]
static void MirrorHorizontallyParent()
{
MirrorHorizontally(true);
}
static void MirrorHorizontally(bool mirrorAnchors)
{
foreach (Transform transform in Selection.transforms)
{
RectTransform t = transform as RectTransform;
RectTransform pt = Selection.activeTransform.parent as RectTransform;
if (t == null || pt == null) return;
if (mirrorAnchors)
{
Vector2 oldAnchorMin = t.anchorMin;
t.anchorMin = new Vector2(1 - t.anchorMax.x, t.anchorMin.y);
t.anchorMax = new Vector2(1 - oldAnchorMin.x, t.anchorMax.y);
}
Vector2 oldOffsetMin = t.offsetMin;
t.offsetMin = new Vector2(-t.offsetMax.x, t.offsetMin.y);
t.offsetMax = new Vector2(-oldOffsetMin.x, t.offsetMax.y);
t.localScale = new Vector3(-t.localScale.x, t.localScale.y, t.localScale.z);
}
}
[MenuItem("uGUI/Mirror Vertically Around Anchors %'")]
static void MirrorVerticallyAnchors()
{
MirrorVertically(false);
}
[MenuItem("uGUI/Mirror Vertically Around Parent Center %\"")]
static void MirrorVerticallyParent()
{
MirrorVertically(true);
}
static void MirrorVertically(bool mirrorAnchors)
{
foreach (Transform transform in Selection.transforms)
{
RectTransform t = transform as RectTransform;
RectTransform pt = Selection.activeTransform.parent as RectTransform;
if (t == null || pt == null) return;
if (mirrorAnchors)
{
Vector2 oldAnchorMin = t.anchorMin;
t.anchorMin = new Vector2(t.anchorMin.x, 1 - t.anchorMax.y);
t.anchorMax = new Vector2(t.anchorMax.x, 1 - oldAnchorMin.y);
}
Vector2 oldOffsetMin = t.offsetMin;
t.offsetMin = new Vector2(t.offsetMin.x, -t.offsetMax.y);
t.offsetMax = new Vector2(t.offsetMax.x, -oldOffsetMin.y);
t.localScale = new Vector3(t.localScale.x, -t.localScale.y, t.localScale.z);
}
}
}
}
本篇UGUI教程关于UGUI的拓展介绍到此结束,下篇我们来学习ToolTip 、ReturnKeyTriggersButton、NicerOutline等
4、ToolTip
【需要外部调用函数显示和隐藏内容】请注意–目前情况下仅适用于Screenspace Camera的画布设置,要包括Screenspace and Worldspace需要更新
namespace UnityEngine.UI.Extensions
{
[RequireComponent(typeof(RectTransform))]
[AddComponentMenu("UI/Extensions/Tooltip")]
public class ToolTip : MonoBehaviour
{
//text of the tooltip
private Text _text;
private RectTransform _rectTransform;
//if the tooltip is inside a UI element
private bool _inside;
private bool _xShifted, _yShifted = false;
private float width, height, canvasWidth, canvasHeight;
private int screenWidth, screenHeight;
private float YShift,xShift;
private RenderMode _guiMode;
private Camera _guiCamera;
// Use this for initialization
public void Awake()
{
var _canvas = GetComponentInParent<Canvas>();
_guiCamera = _canvas.worldCamera;
_guiMode = _canvas.renderMode;
_rectTransform = GetComponent<RectTransform>();
_text = GetComponentInChildren<Text>();
_inside = false;
//size of the screen
screenWidth = Screen.width;
screenHeight = Screen.height;
xShift = 0f;
YShift = -30f;
_xShifted = _yShifted = false;
this.gameObject.SetActive(false);
}
//Call this function externally to set the text of the template and activate the tooltip
public void SetTooltip(string ttext)
{
if (_guiMode == RenderMode.ScreenSpaceCamera)
{
//set the text and fit the tooltip panel to the text size
_text.text = ttext;
_rectTransform.sizeDelta = new Vector2(_text.preferredWidth + 40f, _text.preferredHeight + 25f);
OnScreenSpaceCamera();
}
}
//call this function on mouse exit to deactivate the template
public void HideTooltip()
{
if (_guiMode == RenderMode.ScreenSpaceCamera)
{
this.gameObject.SetActive(false);
_inside = false;
}
}
// Update is called once per frame
void FixedUpdate()
{
if (_inside)
{
if (_guiMode == RenderMode.ScreenSpaceCamera)
{
OnScreenSpaceCamera();
}
}
}
//main tooltip edge of screen guard and movement
public void OnScreenSpaceCamera()
{
Vector3 newPos = _guiCamera.ScreenToViewportPoint(Input.mousePosition - new Vector3(xShift, YShift, 0f));
Vector3 newPosWVP = _guiCamera.ViewportToWorldPoint(newPos);
width = _rectTransform.sizeDelta[0];
height = _rectTransform.sizeDelta[1];
// check and solve problems for the tooltip that goes out of the screen on the horizontal axis
float val;
Vector3 lowerLeft = _guiCamera.ViewportToWorldPoint(new Vector3(0.0f, 0.0f, 0.0f));
Vector3 upperRight = _guiCamera.ViewportToWorldPoint(new Vector3(1.0f, 1.0f, 0.0f));
//check for right edge of screen
val = (newPosWVP.x + width / 2);
if (val > upperRight.x)
{
Vector3 shifter = new Vector3(val - upperRight.x, 0f, 0f);
Vector3 newWorldPos = new Vector3(newPosWVP.x - shifter.x, newPos.y, 0f);
newPos.x = _guiCamera.WorldToViewportPoint(newWorldPos).x;
}
//check for left edge of screen
val = (newPosWVP.x - width / 2);
if (val < lowerLeft.x)
{
Vector3 shifter = new Vector3(lowerLeft.x - val, 0f, 0f);
Vector3 newWorldPos = new Vector3(newPosWVP.x + shifter.x, newPos.y, 0f);
newPos.x = _guiCamera.WorldToViewportPoint(newWorldPos).x;
}
// check and solve problems for the tooltip that goes out of the screen on the vertical axis
//check for upper edge of the screen
val = (newPosWVP.y + height / 2);
if (val > upperRight.y)
{
Vector3 shifter = new Vector3(0f, 35f + height / 2, 0f);
Vector3 newWorldPos = new Vector3(newPos.x, newPosWVP.y - shifter.y, 0f);
newPos.y = _guiCamera.WorldToViewportPoint(newWorldPos).y;
}
//check for lower edge of the screen (if the shifts of the tooltip are kept as in this code, no need for this as the tooltip always appears above the mouse bu default)
val = (newPosWVP.y - height / 2);
if (val < lowerLeft.y)
{
Vector3 shifter = new Vector3(0f, 35f + height / 2, 0f);
Vector3 newWorldPos = new Vector3(newPos.x, newPosWVP.y + shifter.y, 0f);
newPos.y = _guiCamera.WorldToViewportPoint(newWorldPos).y;
}
this.transform.position = new Vector3(newPosWVP.x, newPosWVP.y, 0f);
this.gameObject.SetActive(true);
_inside = true;
}
}
}
5、ReturnKeyTriggersButton
首先肯定是要配合 InputField 组件使用,是处理回车键, 【刚刚试了试 InputField 组件 很强大的哦】
using UnityEngine.EventSystems;
namespace UnityEngine.UI
{
[RequireComponent(typeof(InputField))]
[AddComponentMenu("UI/Extensions/Return Key Trigger")]
public class ReturnKeyTriggersButton : MonoBehaviour, ISubmitHandler
{
private EventSystem _system;
public Button button;
private bool highlight = true;
public float highlightDuration = 0.2f;
void Start()
{
_system = EventSystem.current;
}
void RemoveHighlight()
{
button.OnPointerExit(new PointerEventData(_system));
}
public void OnSubmit(BaseEventData eventData)
{
if (highlight) button.OnPointerEnter(new PointerEventData(_system));
button.OnPointerClick(new PointerEventData(_system));
if (highlight) Invoke("RemoveHighlight", highlightDuration);
}
}
}
6、NicerOutline
和系统的 Outline效果上没有区别,但是实现方式不同可以好好研究一下
using System.Collections.Generic;
namespace UnityEngine.UI.Extensions
{
//An outline that looks a bit nicer than the default one. It has less "holes" in the outline by drawing more copies of the effect
[AddComponentMenu("UI/Effects/Extensions/Nicer Outline")]
public class NicerOutline : BaseMeshEffect
{
[SerializeField]
private Color m_EffectColor = new Color (0f, 0f, 0f, 0.5f);
[SerializeField]
private Vector2 m_EffectDistance = new Vector2 (1f, -1f);
[SerializeField]
private bool m_UseGraphicAlpha = true;
//
// Properties
//
public Color effectColor
{
get
{
return this.m_EffectColor;
}
set
{
this.m_EffectColor = value;
if (base.graphic != null)
{
base.graphic.SetVerticesDirty ();
}
}
}
public Vector2 effectDistance
{
get
{
return this.m_EffectDistance;
}
set
{
if (value.x > 600f)
{
value.x = 600f;
}
if (value.x < -600f)
{
value.x = -600f;
}
if (value.y > 600f)
{
value.y = 600f;
}
if (value.y < -600f)
{
value.y = -600f;
}
if (this.m_EffectDistance == value)
{
return;
}
this.m_EffectDistance = value;
if (base.graphic != null)
{
base.graphic.SetVerticesDirty ();
}
}
}
public bool useGraphicAlpha
{
get
{
return this.m_UseGraphicAlpha;
}
set
{
this.m_UseGraphicAlpha = value;
if (base.graphic != null)
{
base.graphic.SetVerticesDirty ();
}
}
}
//
// Methods
//
protected void ApplyShadow (List<UIVertex> verts, Color32 color, int start, int end, float x, float y)
{
//Debug.Log("verts count: "+verts.Count);
int num = verts.Count * 2;
if (verts.Capacity < num)
{
verts.Capacity = num;
}
for (int i = start; i < end; i++)
{
UIVertex uIVertex = verts [i];
verts.Add (uIVertex);
Vector3 position = uIVertex.position;
//Debug.Log("vertex pos: "+position);
position.x += x;
position.y += y;
uIVertex.position = position;
Color32 color2 = color;
if (this.m_UseGraphicAlpha)
{
color2.a = (byte)(color2.a * verts [i].color.a / 255);
}
uIVertex.color = color2;
//uIVertex.color = (Color32)Color.blue;
verts [i] = uIVertex;
}
}
public override void ModifyMesh (/*List<UIVertex> verts*/ Mesh mesh)
{
if (!this.IsActive ())
{
return;
}
// 从mesh 得到 顶点集
List<UIVertex> verts = new List<UIVertex> ();
using (VertexHelper vertexHelper = new VertexHelper (mesh))
{
vertexHelper.GetUIVertexStream (verts);
}
Text foundtext = GetComponent<Text>();
float best_fit_adjustment = 1f;
if (foundtext && foundtext.resizeTextForBestFit)
{
best_fit_adjustment = (float)foundtext.cachedTextGenerator.fontSizeUsedForBestFit / (foundtext.resizeTextMaxSize-1); //max size seems to be exclusive
}
float distanceX = this.effectDistance.x * best_fit_adjustment;
float distanceY = this.effectDistance.y * best_fit_adjustment;
int start = 0;
int count = verts.Count;
this.ApplyShadow (verts, this.effectColor, start, verts.Count, distanceX, distanceY);
start = count;
count = verts.Count;
this.ApplyShadow (verts, this.effectColor, start, verts.Count, distanceX, -distanceY);
start = count;
count = verts.Count;
this.ApplyShadow (verts, this.effectColor, start, verts.Count, -distanceX, distanceY);
start = count;
count = verts.Count;
this.ApplyShadow (verts, this.effectColor, start, verts.Count, -distanceX, -distanceY);
start = count;
count = verts.Count;
this.ApplyShadow (verts, this.effectColor, start, verts.Count, distanceX, 0);
start = count;
count = verts.Count;
this.ApplyShadow (verts, this.effectColor, start, verts.Count, -distanceX, 0);
start = count;
count = verts.Count;
this.ApplyShadow (verts, this.effectColor, start, verts.Count, 0, distanceY);
start = count;
count = verts.Count;
this.ApplyShadow (verts, this.effectColor, start, verts.Count, 0, -distanceY);
// 在合成mesh
using (VertexHelper vertexHelper2 = new VertexHelper ())
{
vertexHelper2.AddUIVertexTriangleStream (verts);
vertexHelper2.FillMesh (mesh);
}
}
protected override void OnValidate ()
{
this.effectDistance = this.m_EffectDistance;
base.OnValidate ();
}
}
}
7、LetterSpacing
单独使用 每个字符间可以设置间距了!【uGUI Text 组件支持行间距,但不是字符间距。 问题是-text layout 引擎不是开源的的一部分,你无法拿到合适的 API,一种解决办法是,只需修改现有的字体,每个字符添加额外的空间。这是那样难以管理,它的声音,和我到其中有很多的不同间隔标题工作不切实际的最近的设计。我需要为每个不同的间距不同的字体。 我最好的解决办法是,只需修改现有的字体fonts ,每个字符添加额外的空间。这样难以管理,我到其中有很多的不同间隔标题工作不切实际的设计。我需要为每个不同的间距不同的字体。 然后偶然看了看Shadow 效果,使用 BaseVertexEffect 来修改绘制的源对象的几何形状的顶点列表。和这个技术相同,文本 间距(虽然它不会产生任何额外的verts)。将组件添加到 UI 文本字段,调整间距值,完成工作。但它不能很好与rich text或自动换行的文本工作】
using System.Collections.Generic;
namespace UnityEngine.UI.Extensions
{
[AddComponentMenu("UI/Effects/Extensions/Letter Spacing")]
public class LetterSpacing : BaseMeshEffect
{
[SerializeField]
private float m_spacing = 0f;
protected LetterSpacing() { }
#if UNITY_EDITOR
protected override void OnValidate()
{
spacing = m_spacing;
base.OnValidate();
}
#endif
public float spacing
{
get { return m_spacing; }
set
{
if (m_spacing == value) return;
m_spacing = value;
if (graphic != null) graphic.SetVerticesDirty();
}
}
public override void ModifyMesh(/*List<UIVertex> verts*/Mesh mesh)
{
if (! IsActive()) return;
// 从mesh 得到 顶点集
List<UIVertex> verts = new List<UIVertex> ();
using (VertexHelper vertexHelper = new VertexHelper (mesh))
{
vertexHelper.GetUIVertexStream (verts);
}
Text text = GetComponent<Text>();
if (text == null)
{
Debug.LogWarning("LetterSpacing: Missing Text component");
return;
}
string[] lines = text.text.Split('\n');
Vector3 pos;
float letterOffset = spacing * (float)text.fontSize / 100f;
float alignmentFactor = 0;
int glyphIdx = 0;
switch (text.alignment)
{
case TextAnchor.LowerLeft:
case TextAnchor.MiddleLeft:
case TextAnchor.UpperLeft:
alignmentFactor = 0f;
break;
case TextAnchor.LowerCenter:
case TextAnchor.MiddleCenter:
case TextAnchor.UpperCenter:
alignmentFactor = 0.5f;
break;
case TextAnchor.LowerRight:
case TextAnchor.MiddleRight:
case TextAnchor.UpperRight:
alignmentFactor = 1f;
break;
}
for (int lineIdx=0; lineIdx < lines.Length; lineIdx++)
{
string line = lines[lineIdx];
float lineOffset = (line.Length -1) * letterOffset * alignmentFactor;
for (int charIdx = 0; charIdx < line.Length; charIdx++)
{
int idx1 = glyphIdx * 4 + 0;
int idx2 = glyphIdx * 4 + 1;
int idx3 = glyphIdx * 4 + 2;
int idx4 = glyphIdx * 4 + 3;
// Check for truncated text (doesn't generate verts for all characters)
if (idx4 > verts.Count - 1) return;
UIVertex vert1 = verts[idx1];
UIVertex vert2 = verts[idx2];
UIVertex vert3 = verts[idx3];
UIVertex vert4 = verts[idx4];
pos = Vector3.right * (letterOffset * charIdx - lineOffset);
vert1.position += pos;
vert2.position += pos;
vert3.position += pos;
vert4.position += pos;
verts[idx1] = vert1;
verts[idx2] = vert2;
verts[idx3] = vert3;
verts[idx4] = vert4;
glyphIdx++;
}
// Offset for carriage return character that still generates verts
glyphIdx++;
}
// 在合成mesh
using (VertexHelper vertexHelper2 = new VertexHelper ())
{
vertexHelper2.AddUIVertexTriangleStream (verts);
vertexHelper2.FillMesh (mesh);
}
}
}
}
本篇UGUI教程关于UGUI的拓展介绍到此结束,下篇我们来学习 Gradient 、UIWindowBase 、lowLayoutGroup 等
8、Gradient
和之前发表的blog差不多吧,这个是可以对Text文本内容的而且有global/local模式,选项更多了【可以研究源码】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
using
System.Collections.Generic;
namespace
UnityEngine.UI.Extensions
{
// [RequireComponent(typeof(Text), typeof(RectTransform))]
[AddComponentMenu(
"UI/Effects/Extensions/Gradient"
)]
public
class
Gradient : BaseMeshEffect
{
public
GradientMode gradientMode = GradientMode.Global;
public
GradientDir gradientDir = GradientDir.Vertical;
public
bool
overwriteAllColor =
false
;
public
Color vertex1 = Color.white;
public
Color vertex2 = Color.black;
private
Graphic targetGraphic;
protected
override
void
Start()
{
targetGraphic = GetComponent<Graphic>();
}
public
override
void
ModifyMesh(
/*List<UIVertex> vertexList*/
Mesh mesh)
{
// 从mesh 得到 顶点集
List<UIVertex> vertexList =
new
List<UIVertex> ();
using
(VertexHelper vertexHelper =
new
VertexHelper (mesh))
{
vertexHelper.GetUIVertexStream (vertexList);
}
if
(!IsActive() || vertexList.Count == 0)
{
return
;
}
int
count = vertexList.Count;
UIVertex uiVertex = vertexList[0];
if
(gradientMode == GradientMode.Global)
{
if
(gradientDir == GradientDir.DiagonalLeftToRight || gradientDir == GradientDir.DiagonalRightToLeft)
{
#if UNITY_EDITOR
Debug.LogWarning(
"Diagonal dir is not supported in Global mode"
);
#endif
gradientDir = GradientDir.Vertical;
}
float
bottomY = gradientDir == GradientDir.Vertical ? vertexList[vertexList.Count - 1].position.y : vertexList[vertexList.Count - 1].position.x;
float
topY = gradientDir == GradientDir.Vertical ? vertexList[0].position.y : vertexList[0].position.x;
float
uiElementHeight = topY - bottomY;
for
(
int
i = 0; i < count; i++)
{
uiVertex = vertexList[i];
if
(!overwriteAllColor && uiVertex.color != targetGraphic.color)
continue
;
uiVertex.color *= Color.Lerp(vertex2, vertex1, ((gradientDir == GradientDir.Vertical ? uiVertex.position.y : uiVertex.position.x) - bottomY) / uiElementHeight);
vertexList[i] = uiVertex;
}
}
else
{
for
(
int
i = 0; i < count; i++)
{
uiVertex = vertexList[i];
if
(!overwriteAllColor && !CompareCarefully(uiVertex.color, targetGraphic.color))
continue
;
switch
(gradientDir)
{
case
GradientDir.Vertical:
uiVertex.color *= (i % 4 == 0 || (i - 1) % 4 == 0) ? vertex1 : vertex2;
break
;
case
GradientDir.Horizontal:
uiVertex.color *= (i % 4 == 0 || (i - 3) % 4 == 0) ? vertex1 : vertex2;
break
;
case
GradientDir.DiagonalLeftToRight:
uiVertex.color *= (i % 4 == 0) ? vertex1 : ((i - 2) % 4 == 0 ? vertex2 : Color.Lerp(vertex2, vertex1, 0.5f));
break
;
case
GradientDir.DiagonalRightToLeft:
uiVertex.color *= ((i - 1) % 4 == 0) ? vertex1 : ((i - 3) % 4 == 0 ? vertex2 : Color.Lerp(vertex2, vertex1, 0.5f));
break
;
}
vertexList[i] = uiVertex;
}
}
// 在合成mesh
using
(VertexHelper vertexHelper2 =
new
VertexHelper ())
{
vertexHelper2.AddUIVertexTriangleStream (vertexList);
vertexHelper2.FillMesh (mesh);
}
}
private
bool
CompareCarefully(Color col1, Color col2)
{
if
(Mathf.Abs(col1.r - col2.r) < 0.003f && Mathf.Abs(col1.g - col2.g) < 0.003f && Mathf.Abs(col1.b - col2.b) < 0.003f && Mathf.Abs(col1.a - col2.a) < 0.003f)
return
true
;
return
false
;
}
}
public
enum
GradientMode
{
Global,
Local
}
public
enum
GradientDir
{
Vertical,
Horizontal,
DiagonalLeftToRight,
DiagonalRightToLeft
//Free
}
//enum color mode Additive, Multiply, Overwrite
}
|
9、UIWindowBase
挂上之后, 可以拖动UI元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
using
System;
using
UnityEngine.EventSystems;
namespace
UnityEngine.UI.Extensions
{
/// <summary>
/// Includes a few fixes of my own, mainly to tidy up duplicates, remove unneeded stuff and testing. (nothing major, all the crew above did the hard work!)
/// </summary>
[RequireComponent(typeof(RectTransform))]
[AddComponentMenu(
"UI/Extensions/UI Window Base"
)]
public
class
UIWindowBase : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
RectTransform m_transform = null;
private
bool
_isDragging =
false
;
public
static
bool
ResetCoords =
false
;
private
Vector3 m_originalCoods = Vector3.zero;
private
Canvas m_canvas;
private
RectTransform m_canvasRectTransform;
public
int
KeepWindowInCanvas = 5;
// # of pixels of the window that must stay inside the canvas view.
// Use this for initialization
void
Start()
{
m_transform = GetComponent<RectTransform>();
m_originalCoods = m_transform.position;
m_canvas = GetComponentInParent<Canvas>();
m_canvasRectTransform = m_canvas.GetComponent<RectTransform>();
}
void
Update()
{
if
(ResetCoords)
resetCoordinatePosition();
}
public
void
OnDrag(PointerEventData eventData)
{
if
(_isDragging)
{
var delta = ScreenToCanvas(eventData.position) - ScreenToCanvas(eventData.position - eventData.delta);
m_transform.localPosition += delta;
}
}
//Note, the begin drag and end drag aren't actually needed to control the drag. However, I'd recommend keeping it in case you want to do somethind else when draggging starts and stops
public
void
OnBeginDrag(PointerEventData eventData)
{
if
(eventData.pointerCurrentRaycast.gameObject == null)
return
;
if
(eventData.pointerCurrentRaycast.gameObject.name == name)
{
_isDragging =
true
;
}
}
public
void
OnEndDrag(PointerEventData eventData)
{
_isDragging =
false
;
}
void
resetCoordinatePosition()
{
m_transform.position = m_originalCoods;
ResetCoords =
false
;
}
private
Vector3 ScreenToCanvas(Vector3 screenPosition)
{
Vector3 localPosition;
Vector2 min;
Vector2 max;
var canvasSize = m_canvasRectTransform.sizeDelta;
if
(m_canvas.renderMode == RenderMode.ScreenSpaceOverlay || (m_canvas.renderMode == RenderMode.ScreenSpaceCamera && m_canvas.worldCamera == null))
{
localPosition = screenPosition;
min = Vector2.zero;
max = canvasSize;
}
else
{
var ray = m_canvas.worldCamera.ScreenPointToRay(screenPosition);
var plane =
new
Plane(m_canvasRectTransform.forward, m_canvasRectTransform.position);
float
distance;
if
(plane.Raycast(ray, out distance) ==
false
)
{
throw
new
Exception(
"Is it practically possible?"
);
};
var worldPosition = ray.origin + ray.direction * distance;
localPosition = m_canvasRectTransform.InverseTransformPoint(worldPosition);
min = -Vector2.Scale(canvasSize, m_canvasRectTransform.pivot);
max = Vector2.Scale(canvasSize, Vector2.one - m_canvasRectTransform.pivot);
}
// keep window inside canvas
localPosition.x = Mathf.Clamp(localPosition.x, min.x + KeepWindowInCanvas, max.x - KeepWindowInCanvas);
localPosition.y = Mathf.Clamp(localPosition.y, min.y + KeepWindowInCanvas, max.y - KeepWindowInCanvas);
return
localPosition;
}
}
}
|
10、FlowLayoutGroup 一种布局组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
|
using
System.Collections.Generic;
namespace
UnityEngine.UI.Extensions
{
/// <summary>
/// Layout Group controller that arranges children in rows, fitting as many on a line until total width exceeds parent bounds
/// </summary>
[AddComponentMenu(
"Layout/Extensions/Flow Layout Group"
)]
public
class
FlowLayoutGroup : LayoutGroup
{
public
float
Spacing = 0f;
public
bool
ChildForceExpandWidth =
false
;
public
bool
ChildForceExpandHeight =
false
;
private
float
_layoutHeight;
public
override
void
CalculateLayoutInputHorizontal()
{
base.CalculateLayoutInputHorizontal();
var minWidth = GetGreatestMinimumChildWidth() + padding.left + padding.right;
SetLayoutInputForAxis(minWidth, -1, -1, 0);
}
public
override
void
SetLayoutHorizontal()
{
SetLayout(rectTransform.rect.width, 0,
false
);
}
public
override
void
SetLayoutVertical()
{
SetLayout(rectTransform.rect.width, 1,
false
);
}
public
override
void
CalculateLayoutInputVertical()
{
_layoutHeight = SetLayout(rectTransform.rect.width, 1,
true
);
}
protected
bool
IsCenterAlign
{
get
{
return
childAlignment == TextAnchor.LowerCenter || childAlignment == TextAnchor.MiddleCenter ||
childAlignment == TextAnchor.UpperCenter;
}
}
protected
bool
IsRightAlign
{
get
{
return
childAlignment == TextAnchor.LowerRight || childAlignment == TextAnchor.MiddleRight ||
childAlignment == TextAnchor.UpperRight;
}
}
protected
bool
IsMiddleAlign
{
get
{
return
childAlignment == TextAnchor.MiddleLeft || childAlignment == TextAnchor.MiddleRight ||
childAlignment == TextAnchor.MiddleCenter;
}
}
protected
bool
IsLowerAlign
{
get
{
return
childAlignment == TextAnchor.LowerLeft || childAlignment == TextAnchor.LowerRight ||
childAlignment == TextAnchor.LowerCenter;
}
}
/// <summary>
/// Holds the rects that will make up the current row being processed
/// </summary>
private
readonly IList<RectTransform> _rowList =
new
List<RectTransform>();
/// <summary>
/// Main layout method
/// </summary>
/// <param name="width">Width to calculate the layout with</param>
/// <param name="axis">0 for horizontal axis, 1 for vertical</param>
/// <param name="layoutInput">If true, sets the layout input for the axis. If false, sets child position for axis</param>
public
float
SetLayout(
float
width,
int
axis,
bool
layoutInput)
{
var groupHeight = rectTransform.rect.height;
// Width that is available after padding is subtracted
var workingWidth = rectTransform.rect.width - padding.left - padding.right;
// Accumulates the total height of the rows, including spacing and padding.
var yOffset = IsLowerAlign ? (
float
)padding.bottom : (
float
)padding.top;
var currentRowWidth = 0f;
var currentRowHeight = 0f;
for
(var i = 0; i < rectChildren.Count; i++) {
// LowerAlign works from back to front
var index = IsLowerAlign ? rectChildren.Count - 1 - i : i;
var child = rectChildren[index];
var childWidth = LayoutUtility.GetPreferredSize(child, 0);
var childHeight = LayoutUtility.GetPreferredSize(child, 1);
// Max child width is layout group with - padding
childWidth = Mathf.Min(childWidth, workingWidth);
// If adding this element would exceed the bounds of the row,
// go to a new line after processing the current row
if
(currentRowWidth + childWidth > workingWidth) {
currentRowWidth -= Spacing;
// Process current row elements positioning
if
(!layoutInput) {
var h = CalculateRowVerticalOffset(groupHeight, yOffset, currentRowHeight);
LayoutRow(_rowList, currentRowWidth, currentRowHeight, workingWidth, padding.left, h, axis);
}
// Clear existing row
_rowList.Clear();
// Add the current row height to total height accumulator, and reset to 0 for the next row
yOffset += currentRowHeight;
yOffset += Spacing;
currentRowHeight = 0;
currentRowWidth = 0;
}
currentRowWidth += childWidth;
_rowList.Add(child);
// We need the largest element height to determine the starting position of the next line
if
(childHeight > currentRowHeight) {
currentRowHeight = childHeight;
}
currentRowWidth += Spacing;
}
if
(!layoutInput) {
var h = CalculateRowVerticalOffset(groupHeight, yOffset, currentRowHeight);
// Layout the final row
LayoutRow(_rowList, currentRowWidth, currentRowHeight, workingWidth, padding.left, h, axis);
}
_rowList.Clear();
// Add the last rows height to the height accumulator
yOffset += currentRowHeight;
yOffset += IsLowerAlign ? padding.top : padding.bottom;
if
(layoutInput) {
if
(axis == 1)
SetLayoutInputForAxis(yOffset, yOffset, -1, axis);
}
return
yOffset;
}
private
float
CalculateRowVerticalOffset(
float
groupHeight,
float
yOffset,
float
currentRowHeight)
{
float
h;
if
(IsLowerAlign) {
h = groupHeight - yOffset - currentRowHeight;
}
else
if
(IsMiddleAlign) {
h = groupHeight*0.5f - _layoutHeight * 0.5f + yOffset;
}
else
{
h = yOffset;
}
return
h;
}
protected
void
LayoutRow(IList<RectTransform> contents,
float
rowWidth,
float
rowHeight,
float
maxWidth,
float
xOffset,
float
yOffset,
int
axis)
{
var xPos = xOffset;
if
(!ChildForceExpandWidth && IsCenterAlign)
xPos += (maxWidth - rowWidth) * 0.5f;
else
if
(!ChildForceExpandWidth && IsRightAlign)
xPos += (maxWidth - rowWidth);
var extraWidth = 0f;
if
(ChildForceExpandWidth) {
extraWidth = (maxWidth - rowWidth)/_rowList.Count;
}
for
(var j = 0; j < _rowList.Count; j++) {
var index = IsLowerAlign ? _rowList.Count - 1 - j : j;
var rowChild = _rowList[index];
var rowChildWidth = LayoutUtility.GetPreferredSize(rowChild, 0) + extraWidth;
var rowChildHeight = LayoutUtility.GetPreferredSize(rowChild, 1);
if
(ChildForceExpandHeight)
rowChildHeight = rowHeight;
rowChildWidth = Mathf.Min(rowChildWidth, maxWidth);
var yPos = yOffset;
if
(IsMiddleAlign)
yPos += (rowHeight - rowChildHeight) * 0.5f;
else
if
(IsLowerAlign)
yPos += (rowHeight - rowChildHeight);
if
(axis == 0)
SetChildAlongAxis(rowChild, 0, xPos, rowChildWidth);
else
SetChildAlongAxis(rowChild, 1, yPos, rowChildHeight);
xPos += rowChildWidth + Spacing;
}
}
public
float
GetGreatestMinimumChildWidth()
{
var max = 0f;
for
(var i = 0; i < rectChildren.Count; i++) {
var w = LayoutUtility.GetMinWidth(rectChildren[i]);
max = Mathf.Max(w, max);
}
return
max;
}
}
}
|
11、CurvedText
让文本按照曲线进行显示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
using
System.Collections.Generic;
namespace
UnityEngine.UI.Extensions
{
/// <summary>
/// Curved text.让文本按照曲线进行显示 【注意对Image的变形 也是可以的】
/// 说明: 对Text的操作就和 shadow 和 outline 组件类似。
/// </summary>
// [RequireComponent(typeof(Text), typeof(RectTransform))]
[AddComponentMenu(
"UI/Effects/Extensions/Curved Text"
)]
public
class
CurvedText : BaseMeshEffect
{
// 曲线类型
public
AnimationCurve curveForText = AnimationCurve.Linear(0, 0, 1, 10);
// 曲线程度
public
float
curveMultiplier = 1;
private
RectTransform rectTrans;
#if UNITY_EDITOR
protected
override
void
OnValidate()
{
base.OnValidate();
if
(curveForText[0].
time
!= 0)
{
var tmpRect = curveForText[0];
tmpRect.
time
= 0;
curveForText.MoveKey(0, tmpRect);
}
if
(rectTrans == null)
rectTrans = GetComponent<RectTransform>();
if
(curveForText[curveForText.length - 1].
time
!= rectTrans.rect.width)
OnRectTransformDimensionsChange();
}
#endif
protected
override
void
Awake()
{
base.Awake();
rectTrans = GetComponent<RectTransform>();
OnRectTransformDimensionsChange();
}
protected
override
void
OnEnable()
{
base.OnEnable();
rectTrans = GetComponent<RectTransform>();
OnRectTransformDimensionsChange();
}
/// <summary>
/// Modifies the mesh. 最重要的重载函数
/// </summary>
/// <param name="mesh">Mesh.</param>
public
override
void
ModifyMesh(
/*List<UIVertex> verts*/
Mesh mesh)
{
if
(!IsActive())
return
;
// 从mesh 得到 顶点集
List<UIVertex> verts =
new
List<UIVertex> ();
using
(VertexHelper vertexHelper =
new
VertexHelper (mesh))
{
vertexHelper.GetUIVertexStream (verts);
}
// 顶点的 y值按曲线变换
for
(
int
index = 0; index < verts.Count; index++)
{
var uiVertex = verts[index];
//Debug.Log ();
uiVertex.position.y += curveForText.Evaluate(rectTrans.rect.width * rectTrans.pivot.x + uiVertex.position.x) * curveMultiplier;
verts[index] = uiVertex;
}
// 在合成mesh
using
(VertexHelper vertexHelper2 =
new
VertexHelper ())
{
vertexHelper2.AddUIVertexTriangleStream (verts);
vertexHelper2.FillMesh (mesh);
}
}
protected
override
void
OnRectTransformDimensionsChange()
{
var tmpRect = curveForText[curveForText.length - 1];
tmpRect.
time
= rectTrans.rect.width;
curveForText.MoveKey(curveForText.length - 1, tmpRect);
}
}
}
|
本篇UGUI教程关于UGUI的拓展介绍到此结束,下篇我们来学习 CanvasGroupActivator、BestFitOutline 、AimerInputModule等