Unity3D UGUI 源码学习 其它

在UGUI中遇到的其它类,统一放在一起了。主要包括CoroutineTweenIndexedSetListPoolReflectionMethodsCacheSetPropertyUtility等。

CoroutineTween

用于实现一些逐帧变化的数值或动画。之前一直用DOTween插件做动画,推测其背后是相似的原理。CoroutineTween.cs包含了1个接口,2个实现了该接口的struct和1个类:

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
namespace UnityEngine.UI.CoroutineTween
{
// Base interface for tweeners,
// using an interface instead of
// an abstract class as we want the
// tweens to be structs.
internal interface ITweenValue
{
// ...
}

// Color tween class, receives the
// TweenValue callback and then sets
// the value on the target.
internal struct ColorTween : ITweenValue
{
// ...
}

// Float tween class, receives the
// TweenValue callback and then sets
// the value on the target.
internal struct FloatTween : ITweenValue
{
// ...
}

// Tween runner, executes the given tween.
// The coroutine will live within the given
// behaviour container.
internal class TweenRunner<T> where T : struct, ITweenValue
{
// ...
}
}

接下来逐个学习:

ITweenValue

可以用来“Tween”的值,可以是一个浮点数、一个颜色等。它包含了一个方法:

1
void TweenValue(float floatPercentage);

是实现渐变的最核心的函数。

TweenRunner

TweenRunner用于驱动一个Tween,借助于协程来实现每帧的更新,以下是其包含的Start方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static IEnumerator Start(T tweenInfo)
{
if (!tweenInfo.ValidTarget())
yield break;

var elapsedTime = 0.0f;
while (elapsedTime < tweenInfo.duration)
{
elapsedTime += tweenInfo.ignoreTimeScale ? Time.unscaledDeltaTime : Time.deltaTime;
var percentage = Mathf.Clamp01(elapsedTime / tweenInfo.duration);
tweenInfo.TweenValue(percentage);
yield return null;
}
tweenInfo.TweenValue(1.0f);
}

ITweenValueTweenRunner组成了最近本的框架。

ColorTween

ColorTween是颜色渐变动画类,其实现了TweenValue方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void TweenValue(float floatPercentage)
{
if (!ValidTarget())
return;

var newColor = Color.Lerp(m_StartColor, m_TargetColor, floatPercentage);

if (m_TweenMode == ColorTweenMode.Alpha)
{
newColor.r = m_StartColor.r;
newColor.g = m_StartColor.g;
newColor.b = m_StartColor.b;
}
else if (m_TweenMode == ColorTweenMode.RGB)
{
newColor.a = m_StartColor.a;
}
m_Target.Invoke(newColor);
}

以下是在Graphic中使用ColorTween完成颜色渐变的相关代码:

1
2
3
4
5
6
7
protected Graphic()
{
if (m_ColorTweenRunner == null)
m_ColorTweenRunner = new TweenRunner<ColorTween>();
m_ColorTweenRunner.Init(this);
useLegacyMeshGeneration = true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public virtual void CrossFadeColor(Color targetColor, float duration, bool ignoreTimeScale, bool useAlpha, bool useRGB)
{
if (canvasRenderer == null || (!useRGB && !useAlpha))
return;

Color currentColor = canvasRenderer.GetColor();
if (currentColor.Equals(targetColor))
{
m_ColorTweenRunner.StopTween();
return;
}

ColorTween.ColorTweenMode mode = (useRGB && useAlpha ?
ColorTween.ColorTweenMode.All :
(useRGB ? ColorTween.ColorTweenMode.RGB : ColorTween.ColorTweenMode.Alpha));

var colorTween = new ColorTween { duration = duration, startColor = canvasRenderer.GetColor(), targetColor = targetColor };
colorTween.AddOnChangedCallback(canvasRenderer.SetColor);
colorTween.ignoreTimeScale = ignoreTimeScale;
colorTween.tweenMode = mode;
m_ColorTweenRunner.StartTween(colorTween);
}

FloatTween

ColorTween原理相似,FloatTween用来在每帧改变的是浮点数的值,不再赘述。

IndexedSet

一个数据容器,内部维护的是一个数据的List和一个以数据为键以索引顺序为值的字典。主要特点是数据唯一,且按顺序存储。支持的接口有快速随机移除,快速在尾部添加不重复的数据项,顺序访问等。

ListPool

ListPool是各种List的对象池,其基于ObjectPool<T>实现:

1
2
internal class ObjectPool<T> where T : new()
{}
1
2
internal static class ListPool<T>
{}

其中ObjectPool<T>内部使用一个Stack来保存对象并计数,以实现对象复用:

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
public T Get()
{
T element;
if (m_Stack.Count == 0)
{
element = new T();
countAll++;
}
else
{
element = m_Stack.Pop();
}
if (m_ActionOnGet != null)
m_ActionOnGet(element);
return element;
}

public void Release(T element)
{
if (m_Stack.Count > 0 && ReferenceEquals(m_Stack.Peek(), element))
Debug.LogError("Internal error. Trying to destroy object that is already released to pool.");
if (m_ActionOnRelease != null)
m_ActionOnRelease(element);
m_Stack.Push(element);
}

ReflectionMethodsCache

使用反射的方法获取函数,降低模块间的依赖程度。如当调用Physics.Raycast(...)方法时,其中包含这样的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
internal class ReflectionMethodsCache
{
public delegate bool Raycast3DCallback (Ray r, out RaycastHit hit, float f, int i);

public Raycast3DCallback raycast3D = null;

// ...

public ReflectionMethodsCache()
{
var raycast3DMethodInfo = typeof(Physics).GetMethod("Raycast", new[] {typeof(Ray), typeof(RaycastHit).MakeByRefType(), typeof(float), typeof(int)});
if (raycast3DMethodInfo != null)
raycast3D = (Raycast3DCallback)UnityEngineInternal.ScriptingUtils.CreateDelegate(typeof(Raycast3DCallback), raycast3DMethodInfo);

// ...

}

// ...

}

GraphicRaycaster中可以看到其使用方法如下:

1
2
3
4
5
6
if (ReflectionMethodsCache.Singleton.raycast3D != null)
{
RaycastHit hit;
if (ReflectionMethodsCache.Singleton.raycast3D(ray, out hit, dist, m_BlockingMask))
hitDistance = hit.distance;
}

SetPropertyUtility

一个工具类,当调用set方法(set属性)时,判断二者是否相等,如果不相等则替换为新的值,最后返回bool值表示是否替换了旧的值,如在设置颜色时:

1
2
3
4
5
6
7
8
public static bool SetColor(ref Color currentValue, Color newValue)
{
if (currentValue.r == newValue.r && currentValue.g == newValue.g && currentValue.b == newValue.b && currentValue.a == newValue.a)
return false;

currentValue = newValue;
return true;
}

Graphic有调用如下:

1
2
3
4
5
6
7
8
9
10
11
12
public virtual Color color 
{
get
{
return m_Color;
}
set
{
if (SetPropertyUtility.SetColor(ref m_Color, value))
SetVerticesDirty();
}
}

本系列其它文章详见Unity3D UGUI 源码学习

REFERENCE