UGUI教程之-自定义UGUI 扩展集(三)

 

继续上篇文章《UGUI教程之-自定义UGUI 扩展集(二)》,我们继续来学习自定义扩展UGUI控件

8、Gradient 

和之前发表的blog差不多吧,这个是可以对Text文本内容的而且有global/local模式,选项更多了【可以研究源码】


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元素


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      一种布局组件


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 

让文本按照曲线进行显示


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的拓展介绍到此结束,下篇我们来学习 CanvasGroupActivatorBestFitOutline   、AimerInputModule等