Created
April 4, 2017 09:55
-
-
Save iwashihead/d11e8e88255cf929cf5e2ecb334e87e9 to your computer and use it in GitHub Desktop.
RichTextのようにタグ設定してルビを振るコンポーネント
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System; | |
using System.Collections; | |
using UnityEngine.UI; | |
using UnityEngine.EventSystems; | |
using System.Collections.Generic; | |
/// <summary> | |
/// ルビ付きテキスト. | |
/// "ふわふわ[時間:タイム]" のようにタグを設定することでルビが振られます | |
/// </summary> | |
[RequireComponent(typeof(Text))] | |
public class RubyText : UIBehaviour, IMeshModifier | |
{ | |
private const int RUBY_OBJECT_MAX_COUNT = 16;//ルビの最大数 ( ルビタグを設定できる最大数 ) | |
private const int TEXT_LENGTH_MAX = 200;//本文の最大文字数 | |
private const int VERTEX_COUNT_PER_CHARACTER = 6; | |
private const char TAG_RUBY_START = '['; | |
private const char TAG_RUBY_END = ']'; | |
private const char TAG_RUBY_SEPARETOR = ':'; | |
/// <summary> | |
/// 各ルビ文字のデータ. | |
/// </summary> | |
public struct RubyData | |
{ | |
/// <summary>ルビ文字の内容.</summary> | |
public string Text; | |
/// <summary>本文の何文字目から何文字目にルビを降るか?</summary> | |
public int StartIndex; | |
/// <summary>本文の何文字目から何文字目にルビを降るか?</summary> | |
public int LastIndex; | |
public RubyData(string text, int startIndex, int lastIndex) | |
{ | |
this.Text = text; | |
this.StartIndex = startIndex; | |
this.LastIndex = lastIndex; | |
} | |
}; | |
private Text selfText; | |
private Text SelfText { | |
get { | |
if (!selfText) selfText = GetComponent<Text> (); | |
return selfText; | |
} | |
} | |
[Header("ルビ文字列の設定")] | |
public Font RubyFont; | |
public int RubyFontSize = 18; | |
public FontStyle RubyFontStyle = FontStyle.Normal; | |
public bool SupportRichText = true; | |
public float RubyHeight = 0.8f; | |
//ルビ文字表示用のテキストオブジェクト | |
Text[] rubyTextObjs = new Text[RUBY_OBJECT_MAX_COUNT]; | |
//ルビ情報 | |
RubyData[] rubyDatas = new RubyData[RUBY_OBJECT_MAX_COUNT]; | |
//各文字の座標データ | |
Vector2[] chrPosition = new Vector2[TEXT_LENGTH_MAX]; | |
//ルビ箇所の総数 | |
int rubyCount; | |
/// <summary> | |
/// 初期化 | |
/// </summary> | |
public void Initialize() | |
{ | |
rubyCount = 0; | |
// 本文からルビ文字部分をカットして、ルビ文字データを作る。 | |
SelfText.text = ParseRuby (SelfText.text); | |
// ルビ文字の生成 | |
for (int i = 0; i < rubyCount; ++i) { | |
if (rubyTextObjs [i] != null) { | |
// text 設定 | |
rubyTextObjs [i].font = RubyFont ?? SelfText.font; | |
rubyTextObjs [i].fontSize = RubyFontSize; | |
rubyTextObjs [i].fontStyle = RubyFontStyle; | |
rubyTextObjs [i].supportRichText = SupportRichText; | |
rubyTextObjs [i].text = rubyDatas [i].Text; | |
continue; | |
} | |
var go = new GameObject (i.ToString ()); | |
rubyTextObjs [i] = go.AddComponent<Text> (); | |
// transform 設定 | |
rubyTextObjs [i].transform.SetParent (base.transform); | |
rubyTextObjs [i].transform.localRotation = Quaternion.identity; | |
rubyTextObjs [i].transform.localScale = Vector3.one; | |
// text 設定 | |
rubyTextObjs [i].font = RubyFont ? RubyFont : SelfText.font; | |
rubyTextObjs [i].fontSize = RubyFontSize; | |
rubyTextObjs [i].fontStyle = RubyFontStyle; | |
rubyTextObjs [i].supportRichText = SupportRichText; | |
rubyTextObjs [i].text = rubyDatas [i].Text; | |
rubyTextObjs [i].alignment = TextAnchor.MiddleCenter; | |
// text adjuster 設定 | |
var textAdjust = go.AddComponent<TextSizeAdjuster> (); | |
textAdjust.Mode = TextSizeAdjuster.AdjustMode.HeightAndWidth; | |
textAdjust.AlwaysUpdate = false; | |
textAdjust.Adjust (); | |
} | |
} | |
/// <summary> | |
/// ルビ記号を解析して、ルビデータと本文を分離する。 | |
/// </summary> | |
/// <returns>ルビ無しの文字列.</returns> | |
/// <param name="rubyText">ルビのタグが設定されている文字列.</param> | |
public string ParseRuby (string rubyText) | |
{ | |
int n1 = rubyText.IndexOf (TAG_RUBY_START); //記号が何文字目にあるか? | |
int n2 = rubyText.IndexOf (TAG_RUBY_SEPARETOR); //記号が何文字目にあるか? | |
int n3 = rubyText.IndexOf (TAG_RUBY_END); //記号が何文字目にあるか? | |
//記号が発見できなかったら何もしない。 | |
if (n1 == -1 || n2 == -1 || n3 == -1) { | |
return rubyText;//そのまま本文を返す。 | |
} | |
string sub1 = rubyText.Substring (0, n1); // 先頭から'['までの部分 | |
string sub2 = rubyText.Substring (n1+1,n2-n1-1);// '['〜':'の部分 | |
string sub3 = rubyText.Substring (n2+1,n3-n2-1);// ':'〜']'の部分 (ルビ文字の部分) | |
string sub4 = rubyText.Substring (n3+1); // ']'以降の部分 | |
rubyDatas [rubyCount].StartIndex = n1;//何文字目にルビを降るか? | |
rubyDatas [rubyCount].LastIndex = n2-2;//何文字目にルビを降るか? | |
rubyDatas [rubyCount].Text = sub3; | |
rubyCount++; | |
return ParseRuby (sub1+sub2+sub4);//再帰的に処理する。 | |
} | |
#region UnityEvents | |
private new void OnEnable() | |
{ | |
base.OnEnable(); | |
Initialize (); | |
} | |
private new void OnDisable() | |
{ | |
base.OnDisable(); | |
for (int i = 0; i < rubyCount; i++) { | |
if (rubyTextObjs [i]) | |
Destroy (rubyTextObjs [i].gameObject); | |
} | |
rubyCount = 0; | |
} | |
private new void OnDestroy() | |
{ | |
base.OnDestroy(); | |
for (int i = 0; i < rubyCount; i++) { | |
if (rubyTextObjs [i]) | |
Destroy (rubyTextObjs [i].gameObject); | |
} | |
rubyCount = 0; | |
} | |
#if UNITY_EDITOR | |
public new void OnValidate() | |
{ | |
base.OnValidate(); | |
var graphics = base.GetComponent<Graphic>(); | |
if (graphics != null) | |
{ | |
graphics.SetVerticesDirty(); | |
} | |
} | |
#endif | |
#endregion | |
#region IMeshModifier | |
public void ModifyMesh (Mesh mesh) {} | |
public void ModifyMesh (VertexHelper verts) | |
{ | |
if (!this.IsActive()) | |
{ | |
return; | |
} | |
List<UIVertex> vertexList = new List<UIVertex>(); | |
verts.GetUIVertexStream(vertexList); | |
ModifyVertices(vertexList); | |
verts.Clear(); | |
verts.AddUIVertexTriangleStream(vertexList); | |
} | |
private void ModifyVertices(List<UIVertex> vertexList) | |
{ | |
// 1文字が6頂点で構成されてる | |
// chrPosition[] に各文字の中心座標を格納している | |
int cnt = 0;//本文の文字数 | |
for (int i = 0; i < vertexList.Count; i += VERTEX_COUNT_PER_CHARACTER) | |
{ | |
chrPosition [cnt] = new Vector2 ( | |
(vertexList [i].position.x + vertexList [i + 2].position.x) * 0.5f, | |
(vertexList [i].position.y + vertexList [i + 2].position.y) * 0.5f | |
); | |
cnt++; | |
} | |
//ルビ文字の準備 その2 | |
//表示位置を決める(ルビ文字の表示位置は rubyDatas [i].StartIndex 番目の文字と rubyDatas [i].LastIndex 番目の文字の中間地点としている) | |
//どれだけ上にルビを表示するか。(フォントサイズの大きさxRubyHeight)分上にずらして表示 | |
float dy = SelfText.fontSize * RubyHeight; | |
for (int i = 0; i < rubyCount; ++i) { | |
rubyTextObjs [i].transform.localPosition = new Vector3 ( | |
(chrPosition [rubyDatas [i].StartIndex].x + chrPosition [rubyDatas [i].LastIndex].x) * 0.5f, | |
(chrPosition [rubyDatas [i].StartIndex].y + chrPosition [rubyDatas [i].LastIndex].y) * 0.5f + dy, | |
0 | |
); | |
} | |
} | |
#endregion | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment