Sorry, don’t know of any off hand as I’d just write my own when I need it. I seldom like code assets because I usually want something specifically unique to my own, and might as well just write it myself.
I can show you the one I’m using for my current game:
using UnityEngine;
using System.Collections.Generic;
using com.spacepuppy;
using com.spacepuppy.Tween;
using com.spacepuppy.Tween.Curves;
using com.spacepuppy.UI;
using com.spacepuppy.UserInput;
using com.mansion.UserInput;
namespace com.mansion.Entities.UI
{
public class MessageBox : SPComponent
{
public const float CLOSE_DUR = 0.05f;
public enum StateType
{
Closing = -1,
Closed = 0,
Opening = 1,
Open = 2
}
public enum FormatOption
{
Custom = -1,
Standard = 0,
Script = 1,
}
#region Fields
[SerializeField()]
[DefaultFromSelf()]
private UnityEngine.UI.Text _textBox;
[SerializeField()]
private float _openDur = 1f;
[SerializeField()]
[MinRange(1f)]
private float _charactersPerSecond = 10f;
[SerializeField()]
private float _closeDur = 1f;
[SerializeField()]
private StringTweenStyle _textTweenStyle;
[SerializeField]
private TextSettings _scriptFormat;
[System.NonSerialized()]
private TextSettings _standardFormat;
[System.NonSerialized()]
private StateType _state;
[System.NonSerialized()]
private RadicalCoroutine _currentMessage;
#endregion
#region CONSTRUCTOR
protected override void Awake()
{
base.Awake();
_standardFormat = TextSettings.FromText(_textBox);
}
#endregion
#region Properties
public StateType State
{
get { return _state; }
set { _state = value; }
}
public UnityEngine.UI.Text TextBox
{
get { return _textBox; }
set { _textBox = value; }
}
public bool IsShowing
{
get { return _currentMessage != null; }
}
#endregion
#region Methods
public TextSettings GetFormat(FormatOption option)
{
switch(option)
{
case FormatOption.Script:
return _scriptFormat;
default:
return _standardFormat;
}
}
public IRadicalWaitHandle Close(float dur, bool hideOnFinish)
{
switch(_state)
{
case StateType.Closing:
case StateType.Open:
case StateType.Opening:
if (dur > 0f)
{
return SPTween.Tween((RectTransform)this.transform)
.To("sizeDelta", Vector2.zero, dur)
.OnFinish((s, e) =>
{
_state = StateType.Closed;
_currentMessage = null;
if (hideOnFinish) this.gameObject.SetActive(false);
IEntity.BroadcastStallStateChangedGlobally();
})
.Play(true);
}
else
{
var trans = (RectTransform)this.transform;
trans.sizeDelta = Vector2.zero;
_state = StateType.Closed;
_currentMessage = null;
if (hideOnFinish) this.gameObject.SetActive(false);
IEntity.BroadcastStallStateChangedGlobally();
return RadicalWaitHandle.Null;
}
default:
_state = StateType.Closed;
_currentMessage = null;
if (hideOnFinish) this.gameObject.SetActive(false);
IEntity.BroadcastStallStateChangedGlobally();
return RadicalWaitHandle.Null;
}
}
public void ShowMessage(string msg, TextSettings format, System.Action callback = null)
{
if (!this.gameObject.activeSelf) this.gameObject.SetActive(true);
if (_currentMessage != null)
{
_currentMessage.Stop();
_currentMessage = null;
}
_currentMessage = this.StartRadicalCoroutine(this.ShowRoutine(msg, format, callback, true));
IEntity.BroadcastStallStateChangedGlobally();
}
public void ShowMessageSequence(string[] msgs, TextSettings format, System.Action callback = null)
{
if (msgs == null) throw new System.ArgumentNullException("msgs");
if (!this.gameObject.activeSelf) this.gameObject.SetActive(true);
if (_currentMessage != null)
{
_currentMessage.Stop();
_currentMessage = null;
}
_currentMessage = this.StartRadicalCoroutine(this.ShowSequenceRoutine(msgs, format, callback));
IEntity.BroadcastStallStateChangedGlobally();
}
private System.Collections.IEnumerator ShowRoutine(string msg, TextSettings format, System.Action callback, bool deactivateOnComplete)
{
var trans = (RectTransform)this.transform;
if(_state != StateType.Closed)
{
yield return this.Close(CLOSE_DUR, false);
}
_textBox.text = string.Empty;
trans.sizeDelta = Vector2.zero;
//open
_state = StateType.Opening;
var w = 768f;
var h = 180f;//_textBox.cachedTextGenerator.GetPreferredHeight(msg, _textBox.GetGenerationSettings(new Vector2(w, float.PositiveInfinity)));
var sz = new Vector2(w, h);
yield return SPTween.Tween(trans)
.To("sizeDelta", sz, _openDur)
.Play(true);
_state = StateType.Open;
while(true)
{
//reset for new pass
_textBox.text = string.Empty;
format.Apply(_textBox);
bool lastLoop = false;
var str = msg;
var t = _textBox.cachedTextGenerator;
t.Populate(str, _textBox.GetGenerationSettings(sz));
if(str.Length > t.characterCountVisible)
{
msg = "..." + str.Substring(t.characterCountVisible);
str = str.Substring(0, t.characterCountVisible);
}
else
{
lastLoop = true;
}
//show text
var textTween = SPTween.Tween(_textBox)
.UseCurve(new StringCurve("text", str.Length / _charactersPerSecond, string.Empty, str, _textTweenStyle))
.Play(true);
var input = Game.InputManager.GetDevice<MansionInputDevice>(Game.MAIN_INPUT);
while (!textTween.IsComplete)
{
if (input.GetCurrentButtonState(MansionInputs.Run) == ButtonState.Down ||
input.GetCurrentButtonState(MansionInputs.Action) == ButtonState.Down ||
input.GetCurrentButtonState(MansionInputs.UICancel) == ButtonState.Down)
{
textTween.Stop();
_textBox.text = str;
yield return null;
break;
}
yield return null;
}
while (!(input.GetCurrentButtonState(MansionInputs.Run) == ButtonState.Down ||
input.GetCurrentButtonState(MansionInputs.Action) == ButtonState.Down ||
input.GetCurrentButtonState(MansionInputs.UICancel) == ButtonState.Down))
{
yield return null;
}
if (lastLoop) break;
}
//close
_state = StateType.Closing;
_textBox.text = string.Empty;
yield return SPTween.Tween(trans)
.To("sizeDelta", Vector2.zero, _closeDur)
.Play(true);
_state = StateType.Closed;
if (deactivateOnComplete)
{
_currentMessage = null;
this.gameObject.SetActive(false);
}
if (callback != null) callback();
IEntity.BroadcastStallStateChangedGlobally();
}
private System.Collections.IEnumerator ShowSequenceRoutine(string[] msgs, TextSettings format, System.Action callback)
{
for(int i = 0; i < msgs.Length; i++)
{
yield return ShowRoutine(msgs[i], format, null, false);
}
_currentMessage = null;
this.gameObject.SetActive(false);
if (callback != null) callback();
}
#endregion
}
}
It works with a simple TextBox, you can shape and size the TextBox and put a background and whatever you want on it. Mine looks like this:
Very simple.
You could easily add an Image, and a Sprite parameter to include a portrait with it.
Of course, my implementation relies on my spacepuppy framework for SPTween and what not.
But it’s fairly generic tween engine, any tween engine would do.
You could use it as a starting point for your own…