Skip to content

Commit 411ea13

Browse files
committed
Editor上での表示不具合修正。開始時の異常値に対応。
1 parent db255e5 commit 411ea13

File tree

1 file changed

+186
-78
lines changed

1 file changed

+186
-78
lines changed

Scripts/Runtime/TextMeshProGeometryAnimator.cs

Lines changed: 186 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// https://github.com/coposuke/TextMeshProAnimator
55
//
66
// ===================================
7-
7+
//#define TMPA_OUTPUT_LOG
88

99
using UnityEngine;
1010
using TMPro;
@@ -38,15 +38,13 @@ public class TextMeshProGeometryAnimator : MonoBehaviour
3838
/// ループするかどうか
3939
/// </summary>
4040
[SerializeField]
41-
private bool isLoop = false;
42-
41+
private bool isLoop = false;
42+
4343
/// <summary>
4444
/// アニメーション中かどうか
4545
/// </summary>
46-
public bool isAnimating { get { return isPlaying && time < maxTime; } }
47-
/// <summary>再生フラグ</summary>
48-
private bool isPlaying = false;
49-
46+
public bool isAnimating { get; private set; } = false;
47+
5048
/// <summary>
5149
/// 文字送りアニメーションデータ
5250
/// </summary>
@@ -61,8 +59,8 @@ public class TextMeshProGeometryAnimator : MonoBehaviour
6159
/// <summary>アニメーション時間</summary>
6260
private float time = 0f;
6361
/// <summary>アニメーション最大時間</summary>
64-
private float maxTime = 0f;
65-
62+
private float maxTime = 0f;
63+
6664
/// <summary>頂点座標のキャッシュ</summary>
6765
private Vector3[][] baseVertices = default;
6866
/// <summary>頂点カラーのキャッシュ</summary>
@@ -75,29 +73,52 @@ public class TextMeshProGeometryAnimator : MonoBehaviour
7573
private int characterCount = 0;
7674

7775

76+
#region Unity Events
7877
#if UNITY_EDITOR
7978
/// <summary>
8079
/// Unity Event OnValidate
8180
/// </summary>
8281
private void OnValidate()
8382
{
83+
if (Application.isPlaying)
84+
return;
85+
8486
if (this.textComponent == null)
85-
{
86-
this.textComponent = GetComponent<TMP_Text>();
87-
}
87+
this.textComponent = GetComponent<TMP_Text>();
88+
89+
if (this.textComponent == null)
90+
return;
8891

89-
if (!Application.isPlaying)
90-
this.time = this.maxTime * this.progress;
92+
if (this.gameObject.activeSelf)
93+
OnValidateChild();
94+
}
9195

92-
UpdateMaxVisibleCharacters();
93-
this.textComponent.ForceMeshUpdate(true);
96+
/// <summary>
97+
/// OnValidateの再帰メソッド
98+
/// </summary>
99+
private void OnValidateChild()
100+
{
101+
if (Application.isPlaying)
102+
return;
94103

95-
UpdateText();
96-
UpdateCachedVertex(true);
97-
UpdateAnimation();
98-
}
104+
// シーンロード後およびコンパイル直後はTextInfoが準備できていないので強制更新する
105+
this.textComponent.ForceMeshUpdate(true);
106+
107+
if (!Refresh(true))
108+
{
109+
// ゲームプレイ終了時はTextInfoが異常値な為,遅らせて処理する
110+
UnityEditor.EditorApplication.delayCall += () =>
111+
{
112+
// ゲームプレイ終了時にDestroyされているケースを回避
113+
if (this.textComponent == null)
114+
return;
115+
116+
OnValidateChild();
117+
};
118+
}
119+
}
99120
#endif
100-
121+
101122
/// <summary>
102123
/// Unity Event Awake
103124
/// </summary>
@@ -114,10 +135,13 @@ private void OnEnable()
114135
{
115136
TMPro_EventManager.TEXT_CHANGED_EVENT.Add(OnChangedText);
116137

138+
// シーンロード時はTextInfoが異常値な為,強制更新する
139+
this.textComponent?.ForceMeshUpdate(true);
140+
117141
if (this.playOnEnable)
118142
Play();
119143
else
120-
Finish();
144+
Refresh(true);
121145
}
122146

123147
/// <summary>
@@ -126,60 +150,51 @@ private void OnEnable()
126150
private void OnDisable()
127151
{
128152
TMPro_EventManager.TEXT_CHANGED_EVENT.Remove(OnChangedText);
129-
130-
Finish();
131-
}
132-
153+
}
154+
133155
/// <summary>
134156
/// Unity Event Update
135157
/// </summary>
136158
private void Update()
137159
{
160+
if (this.textComponent == null) { return; }
161+
if (!this.textComponent.isActiveAndEnabled) { return; }
138162
if (null == animationData) { return; }
139-
if (null == textInfo) { return; }
140-
141-
if (playByProgress)
142-
{
143-
time = maxTime * progress;
144-
}
145-
else if (0f < maxTime)
146-
{
147-
if (!isPlaying) { return; }
148-
149-
time += Time.deltaTime * animationData.speed;
150-
151-
if (isLoop)
152-
{
153-
time += (time < 0f ? maxTime : 0f);
154-
time %= maxTime;
155-
}
156-
else
157-
{
158-
time = Mathf.Clamp(time, 0f, maxTime);
159-
}
160-
161-
#if UNITY_EDITOR
162-
progress = time / maxTime;
163-
#endif
164-
}
165-
166-
UpdateAnimation();
163+
if (null == textInfo) { return; }
164+
165+
if (this.isAnimating || this.playByProgress)
166+
{
167+
UpdateTime(this.playByProgress, Time.deltaTime);
168+
UpdateAnimation();
169+
170+
if (this.isAnimating)
171+
{
172+
if ((this.animationData.speed > 0 && this.time >= this.maxTime) ||
173+
(this.animationData.speed < 0 && this.time <= 0f))
174+
{
175+
this.isAnimating = false;
176+
Log($"TMPA Stop: {this.textComponent.name}");
177+
}
178+
}
179+
}
167180
}
168181

169182
/// <summary>
170183
/// TextMesh Proのtext変更時に呼び出されるメソッドです
171184
/// OnEnableとOnDisableにてTMPro_EventManagerに登録しています
172185
/// </summary>
173-
/// <param name="obj"></param>
174186
private void OnChangedText(Object obj)
175187
{
176188
if (obj == this.textComponent)
177189
{
178-
UpdateText();
179-
UpdateAnimation();
190+
Log($"TMPA OnChangedText: {this.textComponent.name}");
191+
Refresh(this.playByProgress);
180192
}
181193
}
194+
#endregion
182195

196+
197+
#region For User
183198
/// <summary>
184199
/// アニメーションデータの上書き設定
185200
/// </summary>
@@ -193,32 +208,111 @@ public void SetAnimation(TextMeshProGeometryAnimation animation)
193208
/// </summary>
194209
public void Play()
195210
{
196-
this.isPlaying = true;
197-
this.time = 0.0f;
198-
UpdateText();
199-
UpdateAnimation();
211+
Log($"TMPA Play: {this.textComponent.name}");
212+
this.isAnimating = true;
213+
Refresh(true);
200214
}
201215

202216
/// <summary>
203217
/// 強制終了
204218
/// </summary>
205-
public void Finish()
219+
public void Finish(float normalizedTime = 0f)
206220
{
207-
this.isPlaying = false;
208-
this.time = this.maxTime;
209-
UpdateText();
210-
UpdateAnimation();
221+
Log($"TMPA Finish: {this.textComponent.name}");
222+
this.isAnimating = false;
223+
this.time = this.maxTime * normalizedTime;
224+
Refresh(false);
225+
}
226+
#endregion
227+
228+
229+
#region Private Methods
230+
private bool Refresh(bool useProgress)
231+
{
232+
UpdateText();
233+
234+
if (VerifyTextInfo())
235+
{
236+
UpdateTime(useProgress, 0f);
237+
238+
// MaxVisibleCharactersに変動があった場合はForceMeshUpdateでメッシュを更新する必要がある
239+
if (UpdateMaxVisibleCharacters())
240+
{
241+
this.textComponent?.ForceMeshUpdate(true);
242+
UpdateCachedVertex(true);
243+
}
244+
245+
UpdateAnimation();
246+
247+
return true;
248+
}
249+
250+
return false;
251+
}
252+
253+
/// <summary>
254+
/// 時間の更新
255+
/// </summary>
256+
private void UpdateTime(bool useProgress, float elapsedTime)
257+
{
258+
if (useProgress)
259+
{
260+
this.time = this.maxTime * this.progress;
261+
}
262+
else if (0f < this.maxTime)
263+
{
264+
if (!this.isAnimating) { return; }
265+
266+
this.time += elapsedTime * this.animationData.speed;
267+
268+
if (this.isLoop)
269+
{
270+
this.time += (this.time < 0f ? this.maxTime : 0f);
271+
this.time %= this.maxTime;
272+
}
273+
else
274+
{
275+
this.time = Mathf.Clamp(this.time, 0f, this.maxTime);
276+
}
277+
}
278+
}
279+
280+
/// <summary>
281+
/// TMPro Textの情報が有効かどうか(主に開始時に異常値が発生する)
282+
/// </summary>
283+
private bool VerifyTextInfo()
284+
{
285+
if (this.textComponent == null)
286+
return false;
287+
288+
if (!this.textComponent.isActiveAndEnabled)
289+
return false;
290+
291+
if (this.textInfo == null)
292+
return false;
293+
294+
if (this.textInfo.characterCount <= 0)
295+
return false;
296+
297+
for (int i = 0; i < this.textInfo.materialCount; i++)
298+
{
299+
if (this.textInfo.meshInfo[i].vertices == null ||
300+
this.textInfo.meshInfo[i].colors32 == null)
301+
return false;
302+
}
303+
304+
return true;
211305
}
212306

213307
/// <summary>
214308
/// TMPro Textの情報更新
215309
/// </summary>
216310
private void UpdateText()
217311
{
218-
this.textInfo = textComponent.textInfo;
312+
this.textInfo = this.textComponent.textInfo;
219313

220314
// 各アニメーション要素で、一番時間がかかるものを最大時間として計算
221-
maxTime = Mathf.Max(
315+
this.maxTime = Mathf.Max(
222316
CalcAnimationTotalTime(this.textInfo.characterCount, this.animationData.position),
223317
CalcAnimationTotalTime(this.textInfo.characterCount, this.animationData.rotation),
224318
CalcAnimationTotalTime(this.textInfo.characterCount, this.animationData.scale),
@@ -235,7 +329,7 @@ private void UpdateText()
235329
/// <summary>
236330
/// 頂点データのキャッシュ
237331
/// </summary>
238-
/// <returns>成功判定</returns>
332+
/// <returns>成功判定(失敗時は異常なTextInfoである, 主に開始時に発生)</returns>
239333
private bool UpdateCachedVertex(bool forceCopy)
240334
{
241335
if (this.textInfo == null)
@@ -286,10 +380,14 @@ private bool UpdateCachedVertex(bool forceCopy)
286380
return true;
287381
}
288382

289-
private bool UpdateMaxVisibleCharacters()
290-
{
291-
// マーカーや下線等の追加描画物はどうしても描画されてしまうので、
292-
// 表示最大数も合わせてアニメーションすることで対応しています。
383+
/// <summary>
384+
/// MaxVisibleCharactersの更新
385+
/// </summary>
386+
/// <returns></returns>
387+
private bool UpdateMaxVisibleCharacters()
388+
{
389+
// マーカーや下線等の追加描画物はどうしても描画されてしまうので、
390+
// 表示最大数も合わせてアニメーションすることで対応しています。
293391
if (this.animationData.useMaxVisibleCharacter)
294392
{
295393
var visibleCharacters = CalcAnimationCharacterCount(time, this.animationData.alpha);
@@ -298,19 +396,20 @@ private bool UpdateMaxVisibleCharacters()
298396
this.textComponent.maxVisibleCharacters = visibleCharacters;
299397
return true;
300398
}
301-
}
302-
303-
return false;
399+
}
400+
401+
return false;
304402
}
305403

306404
/// <summary>
307405
/// TMPro Textの頂点情報の編集
308406
/// </summary>
309407
private void UpdateAnimation()
310408
{
409+
// Wave位置(テキストの動き出し)に合わせて,MaxVisibleCharactersの最大値を更新する
311410
bool forceCacheCopy = UpdateMaxVisibleCharacters();
312-
313-
// アニメーション用の頂点キャッシュ更新
411+
412+
// アニメーション用の頂点キャッシュ更新
314413
if (!UpdateCachedVertex(forceCacheCopy))
315414
return;
316415

@@ -526,4 +625,13 @@ static private int CalcAnimationCharacterCount(float time, TextMeshProGeometryAn
526625
if (item.wave <= 0.0f) { return int.MaxValue; }
527626
return (int)((time - item.delay) / item.wave) + 1;
528627
}
628+
629+
[System.Diagnostics.Conditional("TMPA_OUTPUT_LOG")]
630+
static private void Log(string str)
631+
{
632+
#if TMPA_OUTPUT_LOG
633+
Debug.Log(str);
634+
#endif
635+
}
636+
#endregion
529637
}

0 commit comments

Comments
 (0)