5 режимов скгроллинга текста [UI]

В некоторых приложениях, можно заметить, что есть некое текстовое окно, фиксированного размера. И если текст внутри окна не помещается полностью, то мы наблюдаем анимацию этого текст, он плавно движется слева направо и наоборот. Довольно полезный эффект. Но мы не будем на этом останавливаться и добавим еще несколько текстовых эффектов. Сделаем зацикленное движение справа налево и наоборот + сверху вниз и наоборот. Итого получается пять плюшек. Однако, и этого тоже мало, для упрощения создания UI шаблонов, сделаем небольшой генератор, где нужно лишь заполнить требуемые параметры, выбрать режим, и создать шаблон прямо в инспекторе Юнити.


Для начала напишем небольшой Enum:

public enum ScrollingEnum 
{
	Standard,
	LeftToRight,
	RightToLeft,
	BottomToTop,
	TopToBottom
}

Это будет наш фильтр.

Теперь надо автоматизировать процесс, напишем несколько формул, по которым генератор будет собирать нам шаблоны объектов. Собственно мы выбираем текст, размер шрифта, цвет, размер окна. Нажимаем соответствующие кнопочки и готово:

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

[RequireComponent(typeof(Scrolling))]

public class ScrollingSetup : MonoBehaviour {

	[SerializeField] private RectTransform targetCanvas;
	[SerializeField] private string content = "Example Text Example Text Example Text";
	[SerializeField] private Color textColor;
	[SerializeField] private int fontSize = 18;
	[SerializeField] private float fixedWidth = 200; // ширина окна для режимов: Standard, LeftToRight, RightToLeft
	[SerializeField] private ScrollingEnum direction;
	private int objectID = 1;
	private ScrollingComponent comp;

	public void CreateNew()
	{
		if(targetCanvas == null)
		{
			Error();
			return;
		}

		RectTransform mask = new GameObject("Scrolling-" + objectID).AddComponent<RectTransform>();
		RectTransform text = new GameObject("Text").AddComponent<RectTransform>();
		mask.gameObject.AddComponent<Image>();
		comp = mask.gameObject.AddComponent<ScrollingComponent>();
		mask.gameObject.AddComponent<Mask>().showMaskGraphic = false;
		mask.SetParent(targetCanvas);
		mask.localScale = Vector3.one;
		text.SetParent(mask);
		text.localScale = Vector3.one;
		Text t = text.gameObject.AddComponent<Text>();
		t.text = content;
		t.alignment = TextAnchor.MiddleCenter;
		t.horizontalOverflow = HorizontalWrapMode.Overflow;
		t.color = textColor;
		t.fontSize = fontSize;
		if(direction == ScrollingEnum.Standard || direction == ScrollingEnum.LeftToRight || direction == ScrollingEnum.RightToLeft)
		{
			if(fixedWidth >= t.preferredWidth)
			{
				DestroyImmediate(mask.gameObject);
				Debug.Log("Параметр 'Fixed Width' больше, чем требуется для конечного текста. Отмена.");
				return;
			}
		}
		text.sizeDelta = new Vector2(t.preferredWidth, t.preferredHeight);
		if(direction == ScrollingEnum.BottomToTop || direction == ScrollingEnum.TopToBottom)
		{
			mask.sizeDelta = new Vector2(t.preferredWidth, t.preferredHeight);
			text.anchoredPosition = Vector2.zero;
		}
		else 
		{
			mask.sizeDelta = new Vector2(fixedWidth, t.preferredHeight);
			text.anchoredPosition = new Vector2((text.sizeDelta.x - fixedWidth)/2, 0);
		}
		mask.anchoredPosition = Vector2.zero;
		comp.SetComponent(text, mask, direction, t.text.Length, Position(text));
		objectID++;
		Debug.Log("Добавлен новый объект: " + mask.gameObject.name);
	}

	Vector2 Position(RectTransform rect)
	{
		Vector2 pos = Vector2.zero;
		switch(direction)
		{
		case ScrollingEnum.Standard:
			pos = rect.anchoredPosition;
			break;
		case ScrollingEnum.LeftToRight:
			pos = rect.anchoredPosition + new Vector2(fixedWidth, 0);
			break;
		case ScrollingEnum.RightToLeft:
			pos = rect.anchoredPosition - new Vector2(rect.sizeDelta.x, 0);
			break;
		case ScrollingEnum.BottomToTop:
			pos = rect.anchoredPosition + new Vector2(0, rect.sizeDelta.y);
			break;
		case ScrollingEnum.TopToBottom:
			pos = rect.anchoredPosition - new Vector2(0, rect.sizeDelta.y);
			break;
		}

		return pos;
	}

	public void DestroyLast()
	{
		if(targetCanvas == null)
		{
			Error();
			return;
		}

		if(comp != null)
		{
			Debug.Log("Уничтожен объект: " + comp.gameObject.name);
			DestroyImmediate(comp.gameObject);
			objectID--;
		}
	}

	public void Complete()
	{
		if(targetCanvas == null)
		{
			Error();
			return;
		}

		Scrolling scrolling = GetComponent<Scrolling>();
		ScrollingComponent[] comp = targetCanvas.GetComponentsInChildren<ScrollingComponent>();
		if(comp.Length == 0)
		{
			Debug.Log("В указанном 'Canvas' ничего не найдено.");
			return;
		}
		scrolling.SetComponent(comp);
		Debug.Log("Обновление массива скрипта --> 'Scrolling' ");
	}

	public void ClearAll()
	{
		if(targetCanvas == null)
		{
			Error();
			return;
		}

		Scrolling scrolling = GetComponent<Scrolling>();
		ScrollingComponent[] comp = targetCanvas.GetComponentsInChildren<ScrollingComponent>();
		scrolling.SetComponent(new ScrollingComponent[]{});
		foreach(ScrollingComponent e in comp)
		{
			DestroyImmediate(e.gameObject);
		}
		objectID = 1;
		Debug.Log("Удаление всех объектов / очистка массивов.");
	}

	void Error()
	{
		Debug.LogError("Внимание! Не указан ключевой объект 'Canvas' в переменной --> 'Target Canvas'");
	}
}

5 режимов скгроллинга текста [UI]

Важно иметь ввиду, что после генерации шаблона, его параметры уже нельзя редактировать вручную, разве что кроме цвета текста. Потому, все сгенерированные данные используется для корректного движения текста. Когда будут созданы необходимые объекты, нажимаем кнопочку Complete после чего, массив заполнится и всё будет работать.

Итак, теперь моторчик, который и двигает текст:

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(ScrollingSetup))]

public class Scrolling : MonoBehaviour {

	[SerializeField] private float speed = 1;
	[SerializeField] private ScrollingComponent[] list;

	public void SetComponent(ScrollingComponent[] arr)
	{
		list = arr;
	}

	void LateUpdate()
	{
		if(list == null || list.Length == 0) return;

		for(int j = 0; j < list.Length; j++)
		{
			switch(list[j].direction)
			{
			case ScrollingEnum.Standard:
				if(list[j].rectTransform.anchoredPosition.x == list[j].position.x)
				{
					list[j].NewDir(-list[j].position);
				}
				list[j].rectTransform.anchoredPosition = Vector2.MoveTowards(list[j].rectTransform.anchoredPosition, list[j].position, speed * list[j].textLength * Time.deltaTime);
				break;

			case ScrollingEnum.LeftToRight:
				if(list[j].rectTransform.anchoredPosition.x == list[j].position.x)
				{
					list[j].rectTransform.anchoredPosition -= new Vector2(list[j].rectTransform.sizeDelta.x + list[j].rectParent.sizeDelta.x, 0);
				}
				list[j].rectTransform.anchoredPosition = Vector2.MoveTowards(list[j].rectTransform.anchoredPosition, list[j].position, speed * list[j].textLength * Time.deltaTime);
				break;

			case ScrollingEnum.RightToLeft:
				if(list[j].rectTransform.anchoredPosition.x == list[j].position.x)
				{
					list[j].rectTransform.anchoredPosition += new Vector2(list[j].rectTransform.sizeDelta.x + list[j].rectParent.sizeDelta.x, 0);
				}
				list[j].rectTransform.anchoredPosition = Vector2.MoveTowards(list[j].rectTransform.anchoredPosition, list[j].position, speed * list[j].textLength * Time.deltaTime);
				break;

			case ScrollingEnum.BottomToTop:
				if(list[j].rectTransform.anchoredPosition.y == list[j].position.y)
				{
					list[j].rectTransform.anchoredPosition -= new Vector2(0, list[j].rectTransform.sizeDelta.y + list[j].rectParent.sizeDelta.y);
				}
				list[j].rectTransform.anchoredPosition = Vector2.MoveTowards(list[j].rectTransform.anchoredPosition, list[j].position, speed * list[j].rectParent.sizeDelta.y * Time.deltaTime);
				break;

			case ScrollingEnum.TopToBottom:
				if(list[j].rectTransform.anchoredPosition.y == list[j].position.y)
				{
					list[j].rectTransform.anchoredPosition += new Vector2(0, list[j].rectTransform.sizeDelta.y + list[j].rectParent.sizeDelta.y);
				}
				list[j].rectTransform.anchoredPosition = Vector2.MoveTowards(list[j].rectTransform.anchoredPosition, list[j].position, speed * list[j].rectParent.sizeDelta.y * Time.deltaTime);
				break;
			}
		}
	}
}

Тут разве что скорость можно регулировать.

Необходимый класс для шаблона:

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class ScrollingComponent : MonoBehaviour {

	[SerializeField] private int _TextLength;
	[SerializeField] private RectTransform _RectTransform;
	[SerializeField] private RectTransform _RectParent;
	[SerializeField] private ScrollingEnum _direction;
	[SerializeField] private Vector2 _position;

	public void NewDir(Vector2 p)
	{
		_position = p;
	}

	public Vector2 position
	{
		get{ return _position; }
	}

	public int textLength
	{
		get{ return _TextLength; }
	}

	public RectTransform rectTransform
	{
		get{ return _RectTransform; }
	}

	public RectTransform rectParent
	{
		get{ return _RectParent; }
	}

	public ScrollingEnum direction
	{
		get{ return _direction; }
	}

	public void SetComponent(RectTransform rect, RectTransform rectParent, ScrollingEnum dir, int length, Vector2 pos)
	{
		_RectTransform = rect;
		_direction = dir;
		_TextLength = length;
		_position = pos;
		_RectParent = rectParent;
	}
}

Данный класс используется генератором, все переменные заполняются автоматом. И менять их нельзя.

Теперь, добавим кнопочки в инспекторе:

#if UNITY_EDITOR
using UnityEngine;
using System.Collections;
using UnityEditor;

[CustomEditor(typeof(ScrollingSetup))]

public class ScrollingEditor : Editor {

	public override void OnInspectorGUI()
	{
		DrawDefaultInspector();
		ScrollingSetup t = (ScrollingSetup)target;
		GUILayout.Label("Создание / редактирование шаблонов:", EditorStyles.boldLabel);
		GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
		if(GUILayout.Button("Create New"))
		{
			t.CreateNew();
		}
		if(GUILayout.Button("Destroy Last"))
		{
			t.DestroyLast();
		}
		if(GUILayout.Button("Complete"))
		{
			t.Complete();
		}
		GUILayout.EndHorizontal();
		if(GUILayout.Button("Clear All"))
		{
			t.ClearAll();
		}
	}
}
#endif

Вот собственно и всё. Тестируем и изучаем.

Скачать проект:

У вас нет доступа!
Тестировалось на: Unity 5.4.3

Комментариев 4

Офлайн
Masyanya1 9 декабря 2016
Проект скатился, а жаль, информация выходит крайне редко, дошло вплоть до того, что за месяц было всего одна публикация, ибо тема "Компиляция проекта под ОС Android" с исправлением той самой ошибки, я не считаю полноценной...

P.S. У меня лично, null-code.ru даже в белом листе блокировщика рекламы.
--------------------
Завсегдатый
Офлайн
Light 9 декабря 2016
Masyanya1, по вашему я должен пахать сутки на пролет на этот ресурс? У меня нет права на отдых или на другие дела?) Кроме того, подкидывайте больше тем в ВК или тут ЛС, о чем можно сделать публикацию и т.п. А то, все только и просят, но отдача нулевая... ну не будем о грустном, вот на моем coub-канале, по теме лучше посмотрите http://coub.com/view/a5zjt

Цитата: Masyanya1
P.S. У меня лично, null-code.ru даже в белом листе блокировщика рекламы.

Это хорошо, все бы так. Ибо доход от рекламы помножен на ноль.
Офлайн
Enemyqish 9 декабря 2016
Light, ну например в русскоязычном сообществе очень слабо высветлена тема на счёт генерации ландшафта, ну вообще я если подумаю, то наверное смогу пару-тройку идей на счет публикаций подкинуть, куда будет лучше в сюда в ЛС, или всё таки в ВК?
Офлайн
Light 10 декабря 2016
Цитата: Enemyqish
куда будет лучше в сюда в ЛС, или всё таки в ВК?

Ну, особой разницы нет.
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Яндекс.Метрика