Skip to content

Instantly share code, notes, and snippets.

@anzfactory
Last active May 30, 2016 15:05
Show Gist options
  • Save anzfactory/5f70494eed4045d2c7ef7866b2d55133 to your computer and use it in GitHub Desktop.
Save anzfactory/5f70494eed4045d2c7ef7866b2d55133 to your computer and use it in GitHub Desktop.
メッセージログ風な表示をするやつ http://anz-note.tumblr.com/post/145086980021
/*********************************
* メッセージキューシステム
* 2016-05-29
*********************************/
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Assertions;
[RequireComponent(typeof(Image))]
public class MessageQueueSystem : MonoBehaviour
{
/// <summary>
/// 一度に表示できる行数
/// </summary>
[SerializeField] private int displayLines = 3;
/// <summary>
/// 表示行数がいっぱいになったときに先頭行を消して行追加するまでの時間
/// </summary>
[SerializeField] private float replacementSeconds = 2;
/// <summary>
/// テキスト文字色
/// </summary>
[SerializeField] private Color textColor = Color.white;
/// <summary>
/// テキストフォント
/// </summary>
[SerializeField] private Font textFont;
private int keepLines = 0;
private RectTransform rectTransform;
private GridLayoutGroup gridLayoutGroup;
private Message template;
private List<Message> lines;
private List<string> queue;
private void Awake()
{
this.rectTransform = this.gameObject.GetComponent<RectTransform>();
this.lines = new List<Message>();
this.queue = new List<string>();
var mask = this.gameObject.GetComponent<Mask>();
if (mask == null) {
this.gameObject.AddComponent<Mask>();
}
}
private void Start()
{
// 実際には指定された行数より1行多く持つ
// きっちりだとメッセージをpopしたときに、
// 一番下の行が一瞬空白行になってから埋められるのがなんか嫌
this.keepLines = this.displayLines + 1;
// Awake() のタイミングで呼び出すと
// RectTransform.rect が描画前なので意図している値が取れないので注意
this.gridLayoutGroup = SetupGridLayout(this.rectTransform);
this.template = MakeTemplateText((int)(this.gridLayoutGroup.cellSize.y * 0.8));
}
private void Update()
{
if (this.queue.Count > 0 && this.lines.Count < this.keepLines) {
Show();
}
}
/// <summary>
/// メッセージキュー追加
/// </summary>
/// <param name="message">Message.</param>
public void AddMessage(string message)
{
List<string> temp = new List<string>();
if (this.textFont != null) {
// 1行にはいらない文字列がわたってくる可能性もあるので、
// チェックしつつおさまるように分割して追加していく
TextGenerator textGen = new TextGenerator();
var calcMessage = message;
var resultMessage = "";
int len = 0;
float stringWidth = 0;
var changed = false;
do {
calcMessage = calcMessage.Substring(len);
resultMessage = calcMessage;
stringWidth = textGen.GetPreferredWidth(calcMessage, this.template.GetGenerationSettings(Vector2.zero));
len = calcMessage.Length;
changed = false;
while (stringWidth > this.gridLayoutGroup.cellSize.x) {
resultMessage = calcMessage.Substring(0, --len);
stringWidth = textGen.GetPreferredWidth(
resultMessage,
this.template.GetGenerationSettings(Vector2.zero)
);
changed = true;
}
temp.Add(resultMessage);
} while(changed);
}
this.queue.AddRange(temp);
}
/// <summary>
/// メッセージ表示表のGridLayoutのセットアップ
/// </summary>
/// <returns>GlidLayoutGroup</returns>
private GridLayoutGroup SetupGridLayout(RectTransform rectTransform)
{
float spaceing = 0;
var gridLayoutGroup = this.gameObject.AddComponent<GridLayoutGroup>();
gridLayoutGroup.startAxis = GridLayoutGroup.Axis.Vertical;
gridLayoutGroup.startCorner = GridLayoutGroup.Corner.UpperLeft;
gridLayoutGroup.childAlignment = TextAnchor.UpperLeft;
gridLayoutGroup.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
gridLayoutGroup.constraintCount = 1;
gridLayoutGroup.spacing = new Vector2(spaceing, 0);
gridLayoutGroup.cellSize = new Vector2(rectTransform.rect.width - spaceing * 2, rectTransform.rect.height / this.displayLines);
return gridLayoutGroup;
}
/// <summary>
/// テンプレート用のText作成
/// </summary>
/// <returns>Text</returns>
private Message MakeTemplateText(int fontSize)
{
var gameObject = new GameObject();
gameObject.name = "Template Message";
var text = gameObject.AddComponent<Message>();
text.color = this.textColor;
text.font = this.textFont;
text.fontSize = fontSize;
text.alignment = TextAnchor.MiddleLeft;
gameObject.SetActive(false);
gameObject.transform.SetParent(this.gameObject.transform);
return text;
}
/// <summary>
/// キューからとりだして表示する
/// </summary>
private void Show()
{
if (this.lines.Count >= this.keepLines) {
return;
}
GameObject newTextObject = Instantiate(this.template.gameObject) as GameObject;
newTextObject.name = "Message";
newTextObject.SetActive(true);
Message newText = newTextObject.GetComponent<Message>();
newText.text = this.PopMessage();
newText.Lifetime = this.replacementSeconds;
newText.CallbackDestroied = this.DestroiedMessage;
newTextObject.transform.SetParent(this.gridLayoutGroup.transform);
this.lines.Add(newText);
// リミットに到達している場合は最初の奴に対してタイマーを開始する
if (this.lines.Count >= this.keepLines) {
this.lines[0].StartTimer();
}
}
/// <summary>
/// キューからメッセージ取り出し
/// </summary>
/// <returns>The message.</returns>
private string PopMessage()
{
Assert.IsTrue(this.queue.Count > 0, "メッセージキューがありませんYO!!");
var msg = this.queue[0];
this.queue.RemoveAt(0);
return msg;
}
private void ShiftMessage(string message)
{
this.queue.Insert(0, message);
}
/// <summary>
/// メッセージが破棄されたときによばれるやつ
/// リストから該当メッセージを消去
/// </summary>
/// <param name="sender">Sender.</param>
private void DestroiedMessage(Message sender)
{
this.lines.Remove(sender);
}
}
class Message : Text
{
private float lifetime;
// 秒
public float Lifetime {
get { return this.lifetime; }
set { this.lifetime = value; }
}
private Action<Message> callbackDestroied = null;
public Action<Message> CallbackDestroied {
set { this.callbackDestroied = value; }
}
private bool isRun = false;
public void StartTimer()
{
this.isRun = true;
}
public void Update()
{
if (this.isRun) {
this.lifetime -= Time.deltaTime;
if (this.lifetime < 0) {
this.isRun = false;
if (this.callbackDestroied != null) {
this.callbackDestroied(this);
}
Destroy(this.gameObject);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment