Skip to content

Instantly share code, notes, and snippets.

@Giacom
Last active October 23, 2015 10:27
Show Gist options
  • Select an option

  • Save Giacom/c9da8abb05ac8daa8148 to your computer and use it in GitHub Desktop.

Select an option

Save Giacom/c9da8abb05ac8daa8148 to your computer and use it in GitHub Desktop.
This class was designed to be used in a dialog system where text is printed on screen one character at a time. A problem I had ran into is that if you used Unity's RichText, such as for colouring text, then you would need to find a way to have the text become coloured as they print, as Unity will show the tags until the ending tag is printed.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
/**
* Class used to parse string as RichText.
* WARNING: This does not use recursion and will not detect tags within tags.
* ANOTHER WARNING: It will think anything with a < is a tag so don't use them.
* FINAL WARNING: I made this in a day as I needed something quick and which suited the game's needs. Expect large chunks of code.
*/
public class RichText {
/**
* The original content with the RichText tags.
*/
public string content;
/**
* The length of the content with the RichTags tags omitted.
*/
public int richTextLength;
private List<RichNode> children = new List<RichNode>();
private enum SearchMode {
OPENING_TAG,
TAKING_TAG_ID,
FOUND_OPENING_TAG_ID,
CLOSING_TAG
}
public RichText(string content) {
this.content = content;
Parse();
this.richTextLength = GetRichTextLength();
}
/**
* Will produce a substring which does not cut out the RichText tags.
* For example, If the content is "Blah <color='red'>Blah</color>" then Substring(4) will return
* Blah <color='red'>B</color>
*/
public string Substring(int length) {
string result = "";
string formatlessResult = "";
foreach (RichNode child in children) {
if (formatlessResult.Length + child.content.Length < length) {
formatlessResult += child.content;
result += child.result;
} else {
string finalString = child.content.Substring(0, length - formatlessResult.Length);
if (!string.IsNullOrEmpty(child.tag)) {
result += child.tag + finalString + "</" + child.tagId + ">";
} else {
result += finalString;
}
break;
}
}
return result;
}
private int GetRichTextLength() {
int amount = 0;
foreach (RichNode child in children) {
amount += child.content.Length;
}
return amount;
}
/**
* Big ugly method to non-recursively detect RichText and to store them into RichNodes.
* How it works:
* Collect text up until we detect a tag.
* Store the collected text.
* Get the tag information.
* Get the end tag information.
* Store the text between the texts and the tag information.
* Repeat until end of the content.
* Add the rest of the collected text.
*/
private void Parse() {
int openingTagIndex = 0;
string foundOpeningTag = "";
string foundTagId = "";
string searchingContent = content;
SearchMode searchMode = SearchMode.OPENING_TAG;
for (int i = 0; i < searchingContent.Length; i++) {
char character = searchingContent[i];
if (searchMode == SearchMode.OPENING_TAG) {
if (character == '<') {
string nodeContent = searchingContent.Substring(0, i);
children.Add(new RichNode(nodeContent));
searchMode = SearchMode.TAKING_TAG_ID;
searchingContent = searchingContent.Substring(i, searchingContent.Length - i);
i = 0;
}
continue;
} else if (searchMode == SearchMode.TAKING_TAG_ID) {
if (character == '>') {
searchMode = SearchMode.CLOSING_TAG;
openingTagIndex = i + 1;
foundOpeningTag = searchingContent.Substring(0, openingTagIndex);
foundTagId = foundOpeningTag.Substring(1, foundOpeningTag.Length - 2);
} else if (character == '=') {
foundTagId = searchingContent.Substring(1, i - 1);
searchMode = SearchMode.FOUND_OPENING_TAG_ID;
} else {
foundTagId += character;
}
continue;
} else if (searchMode == SearchMode.FOUND_OPENING_TAG_ID) {
if (character == '>') {
searchMode = SearchMode.CLOSING_TAG;
openingTagIndex = i + 1;
foundOpeningTag = searchingContent.Substring(0, openingTagIndex);
}
continue;
} else if (searchMode == SearchMode.CLOSING_TAG) {
string closingTag = "</" + foundTagId + ">";
int closingTagIndex = searchingContent.IndexOf(closingTag);
if (closingTagIndex == -1) {
closingTagIndex = searchingContent.Length;
}
string insideContent = searchingContent.Substring(openingTagIndex, closingTagIndex - openingTagIndex);
RichNode richNode = new RichNode(insideContent, foundTagId, foundOpeningTag);
children.Add(richNode);
searchMode = SearchMode.OPENING_TAG;
foundOpeningTag = "";
foundTagId = "";
int startCut = closingTagIndex + closingTag.Length;
int endCut = searchingContent.Length - startCut;
searchingContent = searchingContent.Substring(startCut, endCut);
i = 0;
continue;
}
}
string finalNodeContent = searchingContent;
children.Add(new RichNode(finalNodeContent));
}
}
/**
* RichNodes holds parsed data.
* It will hold both content with and without tags.
*/
public class RichNode {
public string result;
public string content;
public string tagId;
public string tag;
public RichNode(string content) {
Init(content, "", "");
}
public RichNode(string content, string tagId, string tag) {
Init(content, tagId, tag);
}
private void Init(string content, string tagId, string tag) {
this.tagId = tagId;
this.tag = tag;
this.content = content;
if (!string.IsNullOrEmpty(tagId) && !string.IsNullOrEmpty(tag)) {
this.result = tag + content + "</" + tagId + ">";
} else {
this.result = content;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment