This script adds each dialogue line to a single Text box, creating a log of all speech.
It can optionally tint the text according to the character who says each line, as well as include clickable Conversation options.
To use it:
- Create a new C# file named ChatLog and paste in the code below
- Add a TextMeshPro UGUI component to the scene, attach the Chat Log component to it, and assign the Text component in its Inspector.
- (Optional) The UI can be converted to an AC Menu, but don't link the Text box itself to a Label element - only the UI canvas prefab.
- (Optional) If the Text box is assigned as the Content of a Scroll Rect, assign it as the Auto Scroll Rect to have it scroll to the bottom automatically. Note that you'll also need to have a Content Size Fitter component on the Text box with its Vertical Fit set to Preferred Size. To allow for manual scrolling at the same time, attach the Chat Log component instead to the Scroll Rect.
ChatLog.cs:
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using TMPro;
using AC;
public class ChatLog : MonoBehaviour, IPointerClickHandler
{
#region Variables
[SerializeField] private TMPro.TextMeshProUGUI uiText = null;
[SerializeField] private Canvas canvas = null;
[SerializeField] private ScrollRect autoScrollRect = null;
[SerializeField] private ColorOption colorOption = ColorOption.Speaker;
[SerializeField] private bool includeConversationOptions;
[SerializeField] private string linkStyle;
private enum ColorOption { None, Speaker, Subtitle, Full };
private readonly HashSet<LineBase> allLines = new HashSet<LineBase> ();
#endregion
#region UnityStandards
private void OnEnable ()
{
EventManager.OnStartConversation += OnStartConversation;
EventManager.OnClickConversation += OnClickConversation;
EventManager.OnEndConversation += OnEndConversation;
EventManager.OnStartSpeech_Alt += OnStartSpeech;
EventManager.OnChangeLanguage += OnChangeLanguage;
RewriteLog ();
}
private void OnDisable ()
{
EventManager.OnStartConversation -= OnStartConversation;
EventManager.OnClickConversation -= OnClickConversation;
EventManager.OnEndConversation += OnEndConversation;
EventManager.OnChangeLanguage -= OnChangeLanguage;
EventManager.OnStartSpeech_Alt -= OnStartSpeech;
}
#endregion
#region CustomEvents
private void OnStartSpeech (Speech speech)
{
allLines.Add (new SpeechLine (speech));
RewriteLog ();
}
private void OnChangeLanguage (int language)
{
RewriteLog ();
}
private void OnClickConversation (Conversation conversation, int optionID)
{
if (includeConversationOptions)
{
allLines.Add (new DialogOption (conversation, optionID));
RewriteLog ();
}
}
private void OnStartConversation (Conversation conversation)
{
RewriteLog (conversation);
}
private void OnEndConversation (Conversation conversation)
{
RewriteLog ();
}
#endregion
#region PublicFunctions
public void OnPointerClick (PointerEventData eventData)
{
if (KickStarter.playerInput.activeConversation == null) return;
Vector3 mousePosition = new Vector3 (eventData.position.x, eventData.position.y, 0f);
Camera camera = (canvas.renderMode == RenderMode.ScreenSpaceOverlay) ? null : Camera.main;
int linkIndex = TMP_TextUtilities.FindIntersectingLink (uiText, mousePosition, camera);
if (linkIndex != -1)
{
var linkInfo = uiText.textInfo.linkInfo[linkIndex];
if (int.TryParse (linkInfo.GetLinkID (), out int clickedOption))
{
KickStarter.playerInput.activeConversation.RunOptionWithID (clickedOption);
}
}
}
#endregion
#region PrivateFunctions
private void RewriteLog (Conversation conversation = null)
{
if (uiText == null)
{
return;
}
StringBuilder sb = new StringBuilder ();
foreach (var line in allLines)
{
string lineText = line.GetText (colorOption);
if (!string.IsNullOrEmpty (lineText))
{
sb.Append (lineText).Append ("\n");
}
}
if (conversation && includeConversationOptions)
{
foreach (var option in conversation.options)
{
if (!option.isOn) continue;
string optionText = KickStarter.runtimeLanguages.GetTranslation (option.label, option.lineID, Options.GetLanguage (), AC_TextType.DialogueOption);
if (!string.IsNullOrEmpty (linkStyle))
{
optionText = "<style=\"" + linkStyle + "\">" + optionText + "</style>";
}
string linkText = "<link=\"" + option.ID + "\">" + optionText + "</link>";
sb.Append (linkText).Append ("\n");
}
}
uiText.text = sb.ToString ();
if (autoScrollRect)
{
Canvas.ForceUpdateCanvases ();
autoScrollRect.verticalNormalizedPosition = 0f;
}
}
#endregion
#region PrivateClasses
private abstract class LineBase
{
public abstract string GetText (ColorOption colorOption);
protected string WrapColorTags (string text, Color color)
{
string colorHTML = ColorUtility.ToHtmlStringRGBA (color);
return "<color=#" + colorHTML + ">" + text + "</color>";
}
}
private class SpeechLine : LineBase
{
private readonly Speech Speech;
public SpeechLine (Speech speech)
{
Speech = speech;
}
public override string GetText (ColorOption colorOption)
{
string lineText = KickStarter.runtimeLanguages.GetTranslation (Speech.FullText, Speech.LineID, Options.GetLanguage (), AC_TextType.Speech);
if (string.IsNullOrEmpty (lineText))
{
return lineText;
}
if (colorOption == ColorOption.Subtitle)
{
lineText = WrapColorTags (lineText, Speech.GetColour ());
}
string speakerLabel = Speech.GetSpeaker (Options.GetLanguage ());
if (!string.IsNullOrEmpty (speakerLabel))
{
if (colorOption == ColorOption.Speaker)
{
speakerLabel = WrapColorTags (speakerLabel, Speech.GetColour ());
}
lineText = speakerLabel + ": " + lineText;
}
if (colorOption == ColorOption.Full)
{
lineText = WrapColorTags (lineText, Speech.GetColour ());
}
return lineText;
}
}
private class DialogOption : LineBase
{
private readonly string OriginalLabel;
private readonly int LineID;
public DialogOption (Conversation conversation, int optionID)
{
OriginalLabel = conversation.GetOptionNameWithID (optionID);
LineID = conversation.GetOptionWithID (optionID).lineID;
}
public override string GetText (ColorOption colorOption)
{
string lineText = KickStarter.runtimeLanguages.GetTranslation (OriginalLabel, LineID, Options.GetLanguage (), AC_TextType.DialogueOption);
if (string.IsNullOrEmpty (lineText))
{
return lineText;
}
if (KickStarter.player)
{
string speakerLabel = KickStarter.player.GetName (Options.GetLanguage ());
if (!string.IsNullOrEmpty (speakerLabel))
{
if (colorOption == ColorOption.Speaker)
{
speakerLabel = WrapColorTags (speakerLabel, KickStarter.player.speechColor);
}
lineText = speakerLabel + ": " + lineText;
}
}
return lineText;
}
}
#endregion
}