あらまし
電光掲示板を見ていて uGUI の Text も文字スクロールができるのではと思い立って作成してみる
テキストを左に動かしてみる
今回 Text の頂点を動かすことで、文字スクロールを実装するため IMeshModifier
を継承したクラスを利用する
IMeshModifier
を利用することで、Graphic
継承クラスの頂点を操作することができる
例えば「New Text」という文字の場合メッシュは上図のようになっており
とりあえずこの頂点座標を左にずらす処理を書いてみる
using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; [RequireComponent(typeof(Text))] public class TextScroll : BaseMeshEffect { [SerializeField] private float _offsetX; private List<UIVertex> uiVertexList = new List<UIVertex>(); #if UNITY_EDITOR public new void OnValidate() { gameObject.GetComponent<Graphic>().SetVerticesDirty(); } #endif public override void ModifyMesh(VertexHelper vertex) { uiVertexList.Clear(); vertex.GetUIVertexStream(uiVertexList); var count = uiVertexList.Count; for (var i = 0; i < count; ++i) { var vert = uiVertexList[i]; vert.position.x -= _offsetX; uiVertexList[i] = vert; } vertex.Clear(); vertex.AddUIVertexTriangleStream(uiVertexList); } }
左に移動した文字を右に移動させる
スクロールさせるためには、左に移動した文字を右に移動させる必要がある
左に隠れたかの判定には position.x
を見て判断する
RectTransform
の Pivot によって左端の値は変わってくるので判定に利用する値も変える必要がある
1文字メッシュ構成
1文字のメッシュ構成は下図のようになっており、(1, 2, 3)番目の頂点のxを見れば良い
左端にいったら右側に移動する処理
※わかりやすくするために Mask 処理をはずしている
var offset = _offset % (textWidth); // 左端の座標 var leftValue = Mathf.Lerp(0, _rectWidth * -1, _pivotX); // 文字ごとにループする for (var i = 0; i < count; i += 6) { // 右下 var checkVert = _uiVertexes[i + 3]; checkVert.position.x -= offset; // 文字が隠れているか var isAddValue = checkVert.position.x < leftValue; // 隠れていた場合は右端に移動 if (isAddValue) checkVert.position.x += textWidth; _uiVertexes[i + 3] = checkVert; foreach (var index in new[]{0, 1, 2, 4, 5}) { var vert = _uiVertexes[i + index]; vert.position.x -= offset; // 隠れていた場合は右端に移動 if (isAddValue) vert.position.x += textWidth; _uiVertexes[i + index] = vert; } }
完成コード
スクロール時のスペースやスクロールのスピードを調整できるように対応したコードが以下になります
またOutline
や Shadow
にも対応していますが結構な数の頂点を動かすことになるので注意が必要です
using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; [RequireComponent(typeof(Text))] public class TextScroll : BaseMeshEffect { [SerializeField] private float _space; [SerializeField] private float _speed; private Graphic _cacheGraphic; private List<UIVertex> _uiVertexes = new List<UIVertex>(); private float _offset; private float _rectWidth; private float _pivotX; protected override void Awake() { Init(); } #if UNITY_EDITOR protected override void OnValidate() { Init(); _cacheGraphic.SetVerticesDirty(); } #endif private void Init() { var text = GetComponent<Text>(); text.horizontalOverflow = HorizontalWrapMode.Overflow; _cacheGraphic = GetComponent<Graphic>(); _pivotX = (transform as RectTransform).pivot.x; _rectWidth = (transform as RectTransform).rect.width; } private void Update() { _offset += Time.deltaTime * _speed; _cacheGraphic.SetVerticesDirty(); } public override void ModifyMesh(VertexHelper vertex) { _uiVertexes.Clear(); vertex.GetUIVertexStream(_uiVertexes); var count = _uiVertexes.Count; if (count > 5) { var textWidth = _uiVertexes[count - 3].position.x - _uiVertexes[0].position.x; // テキスト数 var textCount = _uiVertexes.Count / 6; // 一文字あたりのサイズ var charaWidth = textWidth / textCount; if (textWidth - charaWidth + _space > _rectWidth) { var offset = _offset % (textWidth + _space); var leftValue = Mathf.Lerp(0, _rectWidth * -1, _pivotX); for (var i = 0; i < count; i += 6) { var checkVert = _uiVertexes[i + 3]; checkVert.position.x -= offset; var isAddValue = checkVert.position.x < leftValue; if (isAddValue) checkVert.position.x += textWidth + _space; _uiVertexes[i + 3] = checkVert; foreach (var index in new[]{0, 1, 2, 4, 5}) { var vert = _uiVertexes[i + index]; vert.position.x -= offset; if (isAddValue) vert.position.x += textWidth + _space; _uiVertexes[i + index] = vert; } } } } vertex.Clear(); vertex.AddUIVertexTriangleStream(_uiVertexes); } }