unity 输入框 自己定义光标显示逻辑
实现一个输入框 光标一直闪烁的功能,
public GameObject Cursor; //模拟光标
private RectTransform cursorRectTrans;
private float m_CaretBlinkRate = 0.85f;
protected bool m_CaretVisible;
private Coroutine m_BlinkCoroutine = null;
private float m_BlinkStartTime = 0.0f;
public void Awake()
{
inputField = GetComponent<InputField>();
m_CaretBlinkRate = inputField.caretBlinkRate;
}
void OnEnable()
{
bool currentVisibility = inputField.isFocused && inputField.isActiveAndEnabled;
if (!currentVisibility)
{
isRealCursorVisible = currentVisibility;
SetCaretVisible();
}
}
void SetCaretVisible()
{
m_CaretVisible = true;
m_BlinkStartTime = Time.unscaledTime;
SetCaretActive();
}
/// <summary>
/// 自定义光标
/// </summary>
void SetCaretActive()
{
if (m_CaretBlinkRate > 0.0f)
{
if(m_BlinkCoroutine != null)
{
StopCoroutine(m_BlinkCoroutine);
}
m_BlinkCoroutine = StartCoroutine(CaretBlink());
}
else
{
m_CaretVisible = true;
}
}
void StopBlinkCoroutine()
{
if (m_BlinkCoroutine != null)
{
m_CaretVisible = false;
Cursor.gameObject.SetActive(false);
StopCoroutine(m_BlinkCoroutine);
m_BlinkCoroutine = null;
}
}
IEnumerator CaretBlink()
{
// Always ensure caret is initially visible since it can otherwise be confusing for a moment.
m_CaretVisible = true;
yield return null;
while (!inputField.isFocused && m_CaretBlinkRate > 0)
{
// the blink rate is expressed as a frequency
float blinkPeriod = 1f / m_CaretBlinkRate;
// the caret should be ON if we are in the first half of the blink period
bool blinkState = (Time.unscaledTime - m_BlinkStartTime) % blinkPeriod < blinkPeriod / 2;
if (m_CaretVisible != blinkState)
{
m_CaretVisible = blinkState;
}
// Then wait again.
yield return null;
}
m_BlinkCoroutine = null;
}
void Update()
{
//Debug.Log("当前光标位置:" + inputField.caretPosition);
// 检测系统光标显隐变化 [1,5](@ref)
bool currentVisibility = inputField.isFocused && inputField.isActiveAndEnabled;
if (currentVisibility != isRealCursorVisible)
{
isRealCursorVisible = currentVisibility;
if (!isRealCursorVisible)
SetCaretVisible();
else
StopBlinkCoroutine();
}
if (!isRealCursorVisible && !inputField.isFocused)
{
Cursor.gameObject.SetActive(m_CaretVisible);
UpdateCustomCursorPosition();
}
else
{
Cursor.gameObject.SetActive(false);
}
}
void UpdateCustomCursorPosition()
{
// 获取文本组件及光标索引 [6,7](@ref)
TextGenerator gen = inputField.textComponent.cachedTextGenerator;
int caretPos = inputField.caretPosition;
// 计算光标在文本中的局部坐标
Vector2 cursorLocalPos;
if (caretPos > 0 && caretPos <= gen.characterCountVisible)
{
UICharInfo charInfo = gen.characters[caretPos - 1];
cursorLocalPos = new Vector2(charInfo.cursorPos.x, 0);
}
else
{
if(gen.characterCountVisible > 0)
{
UICharInfo charInfo = gen.characters[gen.characterCountVisible - 1];
cursorLocalPos = new Vector2(charInfo.cursorPos.x + charInfo.charWidth,charInfo.cursorPos.y - (inputField.textComponent.fontSize * 0.5f + 5));
// 转换为屏幕坐标 [4](@ref)
Vector2 screenPos = inputField.textComponent.transform.TransformPoint(cursorLocalPos);
cursorRectTrans.transform.position = screenPos;
}
else
{
cursorLocalPos = Vector2.zero; // 处理边界情况
cursorRectTrans.anchoredPosition = cursorLocalPos;
}
}
}